## Project structure

- The project is a [Next.JS](https://nextjs.org/) app.
- We also have a nested app:
  - `edgeWorker` - A [Cloudflare Worker](https://workers.cloudflare.com/) app that acts as a programatic caching layer infront of the Vercel origin deployment. This is built and deployed automatically.
    This rarely changes so isn't deployed automatically.

## Prerequisites

- Node.js configured repo\
  https://www.notion.so/Node-js-project-setup-guide-86feadffeb304962b06e6b1bb8f09213

## Installing

- Install dependencies: `yarn`
- Copy the `.env.shadow` to `.env` and fill in the values: `cp .env.shadow .env` (ask existing dev for secrets)
- Download translations: `yarn i18n:download`

## Running

When running you can choose which songwhip-api instance to use. Most of the time using the staging/qa data is fine.

### Using production songwhip-api

1. Run `yarn dev:production` (uses `.env` & `.env.production`)
2. Visit `http://localhost:3000/`

### Using staging songwhip-api

1. Run `yarn dev:staging` (uses `.env` & `.env.staging`)
2. Visit `http://localhost:3000/`

### Using a local songwhip-api

If you want to debug songwhip-api while songwhip-web is making requests, you can point songwhip-web at a local songwhip-api instance.

1. Start a local songwhip-api instance running (see songwhip-api README for details).
2. Ensure `NEXT_PUBLIC_SONGWHIP_API_ENDPOINT` in `.env` is pointing to the correct localhost address.
3. Run `yarn dev` (uses `.env`)
4. Visit `http://localhost:3000/`

## Testing

- Run the linter and the tests: `yarn test`
- The tests will be run automatically by a hook when pushing changes

## Deployment/CI

Deployment is managed by Github Actions. When a pull-request is opened/updated we:

1. Deploy a version of the app using [`vercel`](https://vercel.com).
2. Point `staging.songwhip.com` to the new deployment
3. Deploy a new Cloudflare Worker with clean caches
4. Put the Worker in-front of `staging.songwhip.com`
5. Run integration tests against the deployment

- When a new commit is pushed to master we run the above deployment except using the production `songwhip.com` domain.

## Environment variables & secrets

- Variables that differ between production/staging/development are defined as environment variables during the Next.js build.
- For local development these are defined in an (uncommitted) `.env`/`.env.staging`/`.env.production` files at the root of the project (ask an existing dev for a copy).
- When deploying to production or staging these are defined in the relevant `.github/actions/workflows/` file and exposed to the build using the Next.js `--build-env` flag.
- Secrets are defined in the Github Repo and used in Github Actions Workflows via the `${{ secrets.FOO }}` syntax.

## End-to-end (E2E) tests

- [`test/cypress/local`](test/cypress/local/README.md)
- [`test/cypress/e2e`](test/cypress/e2e/README.md)
- ['test/playwright'](test/playwright/README.md)

### Mocking

During testing we're sending large `x-mock-sub-requests` headers containing mock json responses so we need to tell Next.js node server to allow larger headers using the `NODE_OPTIONS="--max-http-header-size=1000000"` option to prevent it rejecting with 431.
That's why both `dev` and `start`scripts define this option.

## Node.js v20 workarounds

### Next.js and crypto

Node.js v20 has some new apis that are not fully compatible with the current Next.js version (v11). One of the issues we are facing, is that Next.js is using the older crypto api to create file hashes during build. As a workaround we are configuring Node.js to use the legacy openssl api by providing the `NODE_OPTIONS="--openssl-legacy-provider"` option in the `build`, `dev` and `start` scripts.

### fetch and nock

Another issue is the Node.js built-in `fetch` api not being compatible with the current `nock` request mocking library. We should try to use the built-in apis whenever we can, but for some tests you can override the `fetch` implementation to use the `nock` compatible `node-fetch` library. **Note that you should only do this server side, since we do not want the `node-fetch` dependency in the client side bundles.**

If your server side code is using `fetchJson` or `songwhipApi`, you can override the implementation by providing a `fetchFn` property. e.g

```ts
import fetchFn from 'node-fetch';
...
await fetchJson('/some-path', { fetchFn });
await songwhipApi('/some-path', { fetchFn });
```

## Feature flags

Features that are not ready for release can but put behind a 'flag'. In code you can use the `useIsEnabled('my-feature')` hook to 'gate' logic related to a feature.

```tsx
const SomePage = () => {
  const myFeatureEnabled = useIsEnabled('my-feature');

  if (myFeature) {
    return <NewImplementation />;
  }

  return <OldImplementation />;
};
```

### Flags can be enabled using:

- A. Using query params `/some-page?enable=my-feature,my-other-feature` (per app load)
- B. Using `enable` cookie set via Devtools (or however you like to set your cookies)

### Limitations

- Currently flags will only work on the clientside, flags are invisible to server renders. This could be improved, it's just more work.
- Flags can't be used to enable features for random users. But you can share `…?enable=…` links with team members to show them features.

## Documentation

- [Icons »](src/components/Icon/README.md)
- [Localization »](docs/localization.md)
