{"id":13719345,"url":"https://github.com/meza/trance-stack","last_synced_at":"2025-05-16T15:09:06.779Z","repository":{"id":37524152,"uuid":"497725909","full_name":"meza/trance-stack","owner":"meza","description":"A production-ready Remix stack built for AWS Lambda. Authentication. Security, Internationalization, Feature Flags, Analytics, Tests, Storybook, Ephemeral and Production CI/CD and more.","archived":false,"fork":false,"pushed_at":"2025-05-13T08:26:39.000Z","size":3871,"stargazers_count":268,"open_issues_count":5,"forks_count":15,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-13T09:33:59.293Z","etag":null,"topics":["remix","remix-run","remix-stack"],"latest_commit_sha":null,"homepage":"https://trance-stack.vsbmeza.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/meza.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":"FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"meza"}},"created_at":"2022-05-29T22:45:23.000Z","updated_at":"2025-02-11T13:03:40.000Z","dependencies_parsed_at":"2024-01-11T21:41:59.218Z","dependency_job_id":"97d76c0c-1139-4a30-83ff-df8b4065cdf9","html_url":"https://github.com/meza/trance-stack","commit_stats":null,"previous_names":[],"tags_count":253,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meza%2Ftrance-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meza%2Ftrance-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meza%2Ftrance-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meza%2Ftrance-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/meza","download_url":"https://codeload.github.com/meza/trance-stack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553958,"owners_count":22090417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["remix","remix-run","remix-stack"],"created_at":"2024-08-03T01:00:46.907Z","updated_at":"2025-05-16T15:09:01.765Z","avatar_url":"https://github.com/meza.png","language":"TypeScript","readme":"# TRANCE STACK [![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@main/badge/badge-storybook.svg)](https://meza.github.io/trance-stack/)\n\n\u003c!-- initremove:begin --\u003e\n\u003e **Warning**\n\u003e **This stack is typescript and NPM only for now.**\n\u003e\n\u003e The NPM requirement comes from the GitHub actions scripts. I will make it possible to use both pnpm and yarn soon, but\n\u003e it\n\u003e requires a bit more time and I would love to get feedback on the stack until then.\n\n## What's included\n\nThis is a [Remix](https://remix.run) stack that offers _a_ way to ship production ready remix applications.\nIt is constructed in an opinionated way and is meant to be used as a starting point for your own remix projects.\nYou can modify it to your liking and use it as a base for your own remix projects.\n\n\u003cdetails\u003e\n\u003csummary\u003e📦 Click to see a list of included technologies\u003c/summary\u003e\n\n- Good security practices with CSP and sensible auth processes\n- i18n with [i18next](https://www.i18next.com/) and its remix\n  integration [remix-i18next](https://github.com/sergiodxa/remix-i18next)\n- [Auth0](https://auth0.com/) for authentication\n- [PostHog](https://posthog.com) for feature flags\n- [Sentry](https://sentry.io) for Client Side error tracking (server side soon)\n- Custom-built cookie consent banner to maximise security [read more](./docs/adr/0013-custom-cookie-consent.md)\n- Analytics Integrations\n  - [PostHog](https://posthog.com)\n  - [Hotjar](https://hotjar.com)\n  - [Google Analytics v4](https://analytics.google.com)\n- Static Types with [TypeScript](https://typescriptlang.org)\n- Linting with [ESLint](https://eslint.org)\n- Unit testing with [Vitest](https://vitest.dev) and [Testing Library](https://testing-library.com)\n- End-to-End testing with [Playwright](https://playwright.dev)\n- [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages to enable automated\n  versioning\n- [semantic-release](https://github.com/semantic-release/semantic-release) for automatic release management\n- [Storybook v7](https://storybook.js.org) for component development\n- [NPM](https://docs.npmjs.com/cli) for package management (for now. Will have support for [yarn](https://yarnpkg.com/)\n  and [pnpm](https://pnpm.io/) soon)\n- [GitHub Actions](https://github.com/features/actions) for a full CI setup\n- [AWS](https://aws.com) deployment with [CDK](https://docs.aws.amazon.com/cdk/index.html)\n  via [GitHub Actions](https://github.com/features/actions)\n  - Using AWS Lambda + Api Gateway + Cloud Front for production builds\n  - Using AWS Lambda + Api Gateway for ephemeral builds (for feature branches, pull requests, etc)\n- Automatic dependency updates with [Renovate](https://www.mend.io/free-developer-tools/renovate)\n\n\u003c/details\u003e\n\n## Using the stack\n\nCreate your project with the stack\n\n```bash\nnpx create-remix@latest --template meza/trance-stack my-app\n```\n\nThe setup process will ask you for a GitHub repository name.\nIf you don't have one, don't worry, you can create it after the setup process.\n\n\u003e **Warning**\n\u003e\n\u003e Read this documentation in your own project's directory **from this point onwards**.\n\u003e It will contain links relevant to you as the init script will replace the links in this\n\u003e README with the ones customized to your project.\n\nNow start the dev server\n\n```bash\nnpm run dev\n```\n\nThis has set you up with a default remix application. It won't function well until you complete the setup process.\nYou can find the instructions for that [here](#getting-started)\n\n---\n\u003c!-- initremove:end --\u003e\n\n## Quickstart\n\n1. Install the dependencies\n\n```bash\nnpm install\n```\n\n2. Start the dev server\n\n```bash\nnpm run dev\n```\n\n3. Go through the [Getting Started](#getting-started) section to set up the local and deployment environments\n\n### Notable npm scripts\n\n- `npm run ci` - run the same verification scripts that are run on CI\n- `npm run clean` - removes all generated files\n- `npm run clean:all` - removes all generated files and all the node_modules directories\n- `npm run dev` - Starts the dev server\n- `npm run deploy:dev` - Deploys the application to an ephemeral environment\n- `npm run deploy:prod` - Deploys the application to production _(you probably should never use this one locally)_\n- `npm run int` - Runs the Playwright integration tests\n- `npm run report` - Runs all the things that generate reports for you (coverage, cpd, loc, etc)\n- `npm run storybook` - Starts the storybook server\n- `npm run validate` - Runs both the CI tests and the integration tests\n\n## Table Of Contents\n\n\u003c!-- TOC --\u003e\n\u003c!-- initremove:begin --\u003e\n\n* [What's included](#whats-included)\n* [Using the stack](#using-the-stack)\u003c!-- initremove:end --\u003e\n* [Quickstart](#quickstart)\n  * [Notable npm scripts](#notable-npm-scripts)\n* [Table Of Contents](#table-of-contents)\n* [Getting Started](#getting-started)\n  * [Environment](#environment)\n  * [GitHub Settings](#github-settings)\n    * [Workflow permissions](#workflow-permissions)\n    * [Branch Protection](#branch-protection)\n    * [Pages](#pages)\n    * [Environments](#environments)\n    * [Variables vs. Secrets](#variables-vs-secrets)\n    * [GitHub Token - Do This First!](#github-token---do-this-first)\n  * [Setup for continuous deployment](#setup-for-continuous-deployment)\n  * [Authentication with Auth0](#authentication-with-auth0)\n    * [Adding the Auth0 variables to GitHub](#adding-the-auth0-variables-to-github)\n    * [Enabling the Auth0 integration for feature branch/PR deployments](#enabling-the-auth0-integration-for-feature-branchpr-deployments)\n    * [Removing the Auth0 integration from the application](#removing-the-auth0-integration-from-the-application)\n  * [Google Analytics 4 integration](#google-analytics-4-integration)\n    * [Removing the Google Analytics 4 integration from the application](#removing-the-google-analytics-4-integration-from-the-application)\n  * [Hotjar integration](#hotjar-integration)\n    * [Removing the Hotjar integration from the application](#removing-the-hotjar-integration-from-the-application)\n  * [PostHog integration](#posthog-integration)\n    * [Removing the PostHog integration from the application](#removing-the-posthog-integration-from-the-application)\n  * [Renovate bot setup](#renovate-bot-setup)\n  * [Sentry integration](#sentry-integration)\n    * [How to find the DSN](#how-to-find-the-dsn)\n    * [Removing the Sentry integration from the application](#removing-the-sentry-integration-from-the-application)\n  * [Split integration](#split-integration)\n    * [Removing the Split integration from the application](#removing-the-split-integration-from-the-application)\n* [How to use ...?](#how-to-use-)\n  * [Authentication](#authentication)\n  * [Automated Semantic Versioning](#automated-semantic-versioning)\n  * [Branching Strategy with Semantic Versioning](#branching-strategy-with-semantic-versioning)\n    * [Linting](#linting)\n    * [Which version am I running?](#which-version-am-i-running)\n  * [Cookie Consent](#cookie-consent)\n    * [Using the consent provider](#using-the-consent-provider)\n  * [Dependency Version Updates](#dependency-version-updates)\n    * [Runtime dependencies](#runtime-dependencies)\n    * [Development dependencies](#development-dependencies)\n  * [Deployment](#deployment)\n    * [Ephemeral Environments](#ephemeral-environments)\n      * [Manual Ephemeral Deployment](#manual-ephemeral-deployment)\n      * [Pull Request Ephemeral Deployment](#pull-request-ephemeral-deployment)\n    * [Production-like Environments](#production-like-environments)\n    * [GitHub Actions](#github-actions)\n    * [CDK](#cdk)\n      * [Environment Variables](#environment-variables)\n        * [Local Environments](#local-environments)\n      * [The deployment directory](#the-deployment-directory)\n      * [The context variables](#the-context-variables)\n      * [Deploying from your local machine](#deploying-from-your-local-machine)\n      * [The githubActionSupport.ts file](#the-githubactionsupportts-file)\n        * [Testing the GitHub support locally](#testing-the-github-support-locally)\n  * [Environment variables](#environment-variables-1)\n    * [Adding a new environment variable checklist:](#adding-a-new-environment-variable-checklist)\n    * [Bundling environment variables](#bundling-environment-variables)\n  * [Feature Flags](#feature-flags)\n    * [Production](#production)\n    * [The `features.ts` file](#the-featurests-file)\n    * [Local development](#local-development)\n  * [I18N - Internationalization](#i18n---internationalization)\n    * [Using translations](#using-translations)\n    * [Adding a new locale](#adding-a-new-locale)\n    * [Removing i18n from your project](#removing-i18n-from-your-project)\n  * [Lefthook](#lefthook)\n  * [NPMIgnore - automated](#npmignore---automated)\n  * [Playwright - End-to-end testing](#playwright---end-to-end-testing)\n    * [Installing Playwright dependencies](#installing-playwright-dependencies)\n    * [Configuring Playwright](#configuring-playwright)\n    * [Running the tests](#running-the-tests)\n      * [Playwright on GitHub Actions](#playwright-on-github-actions)\n      * [Playwright locally](#playwright-locally)\n  * [Storybook](#storybook)\n    * [Running Storybook](#running-storybook)\n    * [Publishing Storybook](#publishing-storybook)\n      * [Accessing the published Storybook](#accessing-the-published-storybook)\n  * [Styling / CSS](#styling--css)\n    * [Shared Component Styles](#shared-component-styles)\n    * [Surfacing Styling](#surfacing-styling)\n    * [PostCSS](#postcss)\n  * [Typescript Paths](#typescript-paths)\n    * [Issues with Typescript Paths](#issues-with-typescript-paths)\n      * [Vitest](#vitest)\n      * [Storybook](#storybook-1)\n  * [Unit Testing](#unit-testing)\n    * [Globals: true](#globals-true)\n    * [Test reporters](#test-reporters)\n    * [Setup files](#setup-files)\n    * [Threads](#threads)\n    * [Coverage](#coverage)\u003c!-- initremove:begin --\u003e\n  * [Development of the stack itself](#development-of-the-stack-itself)\n\n\u003c!-- initremove:end --\u003e\n\u003c!-- TOC --\u003e\n\n## Getting Started\n\nIn order to get this project to work, you will need to have a few things set up first.\n\u003c!-- initremove:begin --\u003e\nThe stack is designed in a way that makes it relatively simple to remove the parts you don't need. You will be able to\nfind removal instructions at every step so don't worry if you're not a fan of a particular service.\n\u003c!-- initremove:end --\u003e\n\u003e **But... why?**\n\u003e\n\u003e **Note**\n\u003e We've been\n\u003e using [Architecture Decision Records](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions)\n\u003e throughout the development of the project so if you ever find yourself wondering why we've chosen a particular service\n\u003e or\n\u003e implementation, you can check the [ADR](./docs/adr/decisions.md) page for more information.\n\u003e\n\u003e We highly encourage you to keep on adding your own decisions. It's a great way to document the historical context of\n\u003e your\n\u003e project, and it's a great way to share your knowledge with the rest of the team.\n\u003e\n\u003e We use [adr-tools](https://github.com/meza/adr-tools) to manage our ADRs. It is installed as part of the dependencies,\n\u003e so you\n\u003e should be able to use it right away.\n\n### Environment\n\nCheck the project root directory for a `.env` file. If it's not there, copy the `.env.example` file to `.env`\n\n```bash\ncp .env.example .env\n```\n\nThis file contains all the variables you will need to set for the project to function as is.\n\nThe `APP_DOMAIN` should generally stay the same. It's the domain that your application will be served from. This\nvariable\nwill also be set by the deployment scripts, so you don't need to worry about it. During local development\nit will be set to `http://localhost:3000`.\n\nThe `NODE_ENV` variable is used to determine which environment you're running the application in. It seems like ARC has\na\nhard time figuring it out on its own, so we've set it up to be set manually. If all goes well, it won't be needed for\nlong.\n\nThe `SESSION_SECRET` variable is used to encrypt the session cookies. It should be a long, random string.\n\n### GitHub Settings\n\n\u003e **Note**\n\u003e The project uses GitHub Actions. If you're not familiar with GitHub Actions, you can read more about it\n\u003e [here](https://docs.github.com/en/actions).\n\nYou need to do a few things to make sure GitHub Actions can work with your project.\n\n#### Workflow permissions\n\nFirst, head over to https://github.com/meza/trance-stack/settings/actions and under the `Workflow permissions`\nsection, make sure it's on the `Read and write permissions` option.\n\nWithout this, the deployment scripts won't be able to create the necessary GitHub releases.\n\n#### Branch Protection\n\nNext, head over to https://github.com/meza/trance-stack/settings/branches and add a few branch protection rules.\n\n- main\n- alpha\n- beta\n\nThese are the branches that will be used for the different stages of the application. You can set the settings of these\nbranches however you like, but there's one setting that you need to make sure is unchecked: `Allow deletions`.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/images/gihtub-branch-protection.png\" alt=\"Branch Protection\" /\u003e\n\u003c/p\u003e\n\nWe use this later in the [Deployment](#deployment) section to prevent named environments from being deleted.\n\n#### Pages\n\nNext, head over to https://github.com/meza/trance-stack/settings/pages and make sure the `Source` is set\nto `GitHub Actions`.\nThis will allow us to deploy the project's storybook to GitHub Pages.\n\n#### Environments\n\n\u003e **Note**\n\u003e We use GitHub environments to manage the different stages of our application. You can read more about them\n\u003e [here](https://docs.github.com/en/actions/deployment/targeting-different-environments).\n\nGitHub environments are great to control the environment variables that are used in your workflows.\n\nFor now, go to https://github.com/meza/trance-stack/settings/environments and create the following environments:\n\n- `Production`\n- `Staging`\n- `Ephemeral`\n\nThese are referred to in [the deployment workflow](./.github/workflows/deploy.yml) for example with the `environment`\nkey.\nThe `Ephemeral` environment is used for feature branches and pull requests and is referenced\nin [the ephemeral workflow](./.github/workflows/ephemeralDeploy.yml).\n\n#### Variables vs. Secrets\n\nSome configuration values are sensitive while others are not. For example, the `COOKIEYES_TOKEN` is not sensitive, but\nthe\n`AUTH0_CLIENT_SECRET` is.\nThis mainly comes from the fact that some of these values will be embedded into the html of your application and be\nvisible\nto everyone.\n\n\u003e **Warning**\n\u003e Please double-check the documentation of the services to ensure you're setting them up correctly.\n\u003e\n\u003e The application won't work properly if you add a secret as a variable or a variable as a secret.\n\n### GitHub Token - Do This First!\n\nFor the releases to work properly, you will need\nto [create a Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).\nIt needs the following settings:\n\n- Expiration: never\n- Scopes\n  - [repo](https://github.com/settings/tokens/new?scopes=repo) for private repositories\n  - [public_repo](https://github.com/settings/tokens/new?scopes=public_repo) for public repositories\n\nOnce you've created the token, go to the [secrets settings][gh-secrets] and add it as `GH_TOKEN`\n\n### Setup for continuous deployment\n\nThe deployment processes are described in the [Deployment](#deployment) section but to get you started, please\ncreate the environment variables and secrets defined in the [environment variables](#environment-variables) section.\n\n### Authentication with Auth0\n\nWe use [Auth0 for authentication](./docs/adr/0010-authentication-is-done-by-auth0.md).\nYou will need to create an account with them\nand [set up an application](https://auth0.com/docs/get-started/auth0-overview/create-applications).\n\nWhen creating your new application, make sure to set the following settings:\n\n1. The application type should be `Regular Web Applications`\n2. Ignore the Quick Start section\n3. Go to Settings and copy the `Domain` and `Client ID` and `Client Secret` and paste them in the `.env` file\n4. Set the Token Endpoint Authentication Method to `Post`\n5. Go to the `Allowed Callback URLs` section and add `http://localhost:3000/auth/callback`\n6. Go to the `Allowed Logout URLs` section and add `http://localhost:3000`\n7. Go to the `Allowed Web Origins` section and add `http://localhost:3000`\n8. Go to the `Allowed Origins (CORS)` section and add `http://localhost:3000`\n9. Go to the `Refresh Token Rotation` section and enable it and with that, you also have to enable\n   the `Absolute Expiration`\n   option.\n\n#### Adding the Auth0 variables to GitHub\n\nNow that you have your Auth0 variables, you will need to add them to the GitHub environments you created above.\n\nGo to the [secrets settings][gh-secrets] and add the Auth0 secrets with the same name as the\nvariables in the `.env` file.\n\nYou can set custom values for every environment if you want to. For example, you can set the `AUTH0_DOMAIN` to\n`dev-123456.eu.auth0.com` for the `Staging` environment and `prod-123456.eu.auth0.com` for the `Production` environment.\n\nBut for the sake of simplicity, you can just set the same values only once in the main Actions secrets page, and it will\nbe used for all environments.\n\n#### Enabling the Auth0 integration for feature branch/PR deployments\n\nIf you want to enable the Auth0 integration for feature branch/PR deployments, you will need to do a few extra steps.\nSince the feature branch/PR deployments are ephemeral, they will have a different domain name every time they are\ndeployed. This means that you will need to add the domain name to the `Allowed Callback URLs` and `Allowed Logout URLs`\n\nTo make this painless, we can use the `*` wildcard in the domain name. This will allow any domain name to be used.\n\nDuring the initial setup above, you have added `http://localhost:3000` in a few places.\nYou will need to add `,https://*.execute-api.us-east-1.amazonaws.com` to the same places.\n_(Note the comma at the beginning. Domains need to be separated by commas)_\n\n\u003e **Note**\n\u003e You will need to replace the `us-east-1` part with the region you're using.\n\nFor example, the Allowed Callback URLs section should look like this:\n\n```text\nhttp://localhost:3000/auth/callback,https://*.execute-api.us-east-1.amazonaws.com/auth/callback\n```\n\n\u003e **Warning**\n\u003e\n\u003e The `*` wildcard will allow you to use as wide of a domain name as you would like to. This however comes at the cost\n\u003e of security. We would highly recommend creating an alternative tenant on Auth0 for your feature branch/PR deployments.\n\n#### Removing the Auth0 integration from the application\n\n1. Delete the `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID` and `AUTH0_CLIENT_SECRET` variables from the `.env` file and GitHub\n   secrets.\n2. Delete the `src/auth.server.ts` and the `src/auth.server.test.ts` files.\n3. Delete the `auth0-remix-server` dependency from the `package.json` file.\n4. Follow the compilation and test errors to remove all the code that uses the `auth0-remix-server` dependency.\n\n### Google Analytics 4 integration\n\nWe use [Google Analytics v4](https://analytics.google.com) for analytics. You will need to create an account with them\nand [set up a property](https://support.google.com/analytics/answer/1008015?hl=en).\n\nWhen you are done setting up your property, you will need to copy the `Measurement ID` of your Data Stream and paste\nset the `GOOGLE_ANALYTICS_ID` variable in the `.env` file.\n\nYou will also have to go to the [variables settings][gh-variables] and add the same variable\nname as the one in the `.env` file.\n\n\u003e **Warning**\n\u003e The `GOOGLE_ANALYTICS_ID` is **set as a variable** for the actions.\n\n#### Removing the Google Analytics 4 integration from the application\n\n1. Delete the `GOOGLE_ANALYTICS_ID` variable from the `.env` file and GitHub variables.\n2. Delete the `src/components/GoogleAnalytics` directory.\n3. Delete the relevant types off the `appConfig` type in the `src/types/global.d.ts` file.\n4. Delete the `\u003cGoogleAnalytics ... /\u003e` component and its import from the `src/root.tsx` file.\n5. Run `vitest --run --update` to update the snapshots.\n\n### Hotjar integration\n\nWe use [Hotjar](https://www.hotjar.com) for heatmaps and user recordings. You will need to create an account with them\nand set up a new site.\n\nWhen you have your site set up, head to https://insights.hotjar.com/site/list and copy the ID of your site and paste\nset the `HOTJAR_ID` variable in the `.env` file.\n\nYou will also have to go to the [variables settings][gh-variables] and add the same variable\nname as the one in the `.env` file.\n\n\u003e **Warning**\n\u003e The `HOTJAR_ID` is **set as a variable** for the actions.\n\n#### Removing the Hotjar integration from the application\n\n1. Delete the `HOTJAR_ID` variable from the `.env` file and GitHub variables.\n2. Delete the `src/components/Hotjar` directory.\n3. Delete the relevant types off the `appConfig` type in the `src/types/global.d.ts` file.\n4. Delete the `\u003cHotjar ... /\u003e` component and its import from the `src/root.tsx` file.\n5. Run `vitest --run --update` to update the snapshots.\n\n### PostHog integration\n\nWe use [PostHog](https://posthog.com) for analytics. You will need to create an account with them\nand set up a new project.\n\nWhen you have your project set up, head to https://posthog.com/project/settings and copy the API key of your project and paste\nset the `POSTHOG_TOKEN` variable in the `.env` file.\nYou also need to set the `POSTHOG_API` variable to either `https://eu.posthog.com` or `https://posthog.com` depending on your\ndata residency preferences.\n\nYou will also have to go to the [variables settings][gh-variables] and add the same variable names as the one in the `.env` file.\n\n#### Differentiating between environments\n\nIn PostHog, your main unit is called an Organization. An organization can have multiple \"projects\" which are\nessentially environments. For example, you can have a `production` project and a `staging` project.\n\nThis allows you to have different feature flags, users and data for each environment. Feel free to create a new\nproject for each environment and then set the appropriate environment variables.\n\n#### Removing the PostHog integration from the application\n\n1. Delete the `POSTHOG_TOKEN` and `POSTHOG_API` variables from the `.env` file and GitHub variables.\n2. Delete the `src/components/Posthog` directory.\n3. Delete the relevant types off the `appConfig` type in the `src/types/global.d.ts` file.\n4. Delete the `\u003cPosthog ... /\u003e` component and its import from the `src/root.tsx` file.\n5. Run `vitest --run --update` to update the snapshots.\n6. Delete the `posthog` dependency from the `package.json` file.\n7. Follow the compilation and test errors to remove all the code that uses the `posthog` dependency.\n\n### Renovate bot setup\n\nWe use [Renovate](https://www.mend.io/free-developer-tools/renovate) to manage dependency updates.\nTo take advantage of it, you will need to install\nthe [Renovate GitHub App](https://docs.renovatebot.com/getting-started/installing-onboarding/#hosted-githubcom-app).\n\nFirst, navigate to https://github.com/apps/renovate and click on the Install button.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/images/renovate-github-app-install.png\" alt=\"Renovate GitHub App install button\"/\u003e\n\u003c/p\u003e\n\nOn the following screen, we recommend selecting \"All repositories\" to make life easier, but you can configure it to only\nwork on the repository you're currently in.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/images/renovate-github-app-choose-repos.png\" alt=\"Select which repositories to use Renovate on\"/\u003e\n\u003c/p\u003e\n\n### Sentry integration\n\n\u003e **Note**\n\u003e Due to compatibility issues with Architect, the server-side instrumentation of Sentry is not\n\u003e working for now.\n\u003e Keep an eye on [this issue](https://github.com/getsentry/sentry-javascript/issues/6062) for\n\u003e updates.\n\u003e The relevant code is commented out in the `entry.server.tsx` file.\n\nWe use [Sentry](https://sentry.io) for error reporting. You will need to create an account with them\nand set up a new project.\n\nWhen you have your project set up, head to the project settings and copy the `DSN` and paste it\nset the `SENTRY_DSN` variable in the `.env` file.\n\nYou will also have to go to the [variables settings][gh-variables] and add the same variable\nname as the one in the `.env` file.\n\nNext, head over to https://sentry.io/settings/account/api/auth-tokens/ and create a new token.\nYou will need `project:releases` and `project:read` permissions.\n\nOnce you have the token, go to the [secrets settings][gh-secrets] and add\n\n- `SENTRY_AUTH_TOKEN` - the token you just created\n- `SENTRY_ORG` - the organization slug\n- `SENTRY_PROJECT` - the project slug\n\nWe will be using these to send the source maps to Sentry so that the errors are properly mapped to the source code.\n\nThe deployment script will automatically upload the source maps to Sentry and then remove them locally, so they don't\nget\nuploaded to the environments.\n\n#### How to find the DSN\n\nFirst, Go to the project settings\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/images/sentry-settings.png\" alt=\"Sentry Settings Icon\" /\u003e\n\u003c/p\u003e\n\nThen on the sidebar, click on the `Client Keys (DSN)`\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/images/sentry-sidebar.png\" alt=\"Sentry Client Keys Icon\" /\u003e\n\u003c/p\u003e\n\nFinally, copy the `DSN` value\n\n#### Removing the Sentry integration from the application\n\n1. Delete the `SENTRY_DSN` variable from the `.env` file and GitHub variables.\n2. Run `npm remove @sentry/*` to remove all the sentry packages.\n3. Remove the `sentryDsn` from the `appConfig` and the `SENTRY_DSN` from the `ProcessEnv` type in\n   the `src/types/global.d.ts` file.\n4. On the very bottom of the `src/root.tsx` file, replace the `withSentry(App)` with `App`.\n5. Remove the `Sentry.init` call from the `src/entry.client.tsx` and the `src/entry.server.tsx` files.\n6. Follow the compilation and test errors to remove all the code that uses Sentry.\n7. Open the `.github/workflows/deploy.yml` and the `.github/workflows/ephemeralDeply.yml` files and remove\n   the `Sentry Sourcemaps` step.\n\n---\n\n## How to use ...?\n\nThis section dives deeper into the concepts present in the stack.\n\n### Authentication\n\nThe authentication is done via the [auth0-remix-server](https://github.com/meza/auth0-remix-server) package.\nThe README file in that package has all the information you need to understand how it works.\n\n### Automated Semantic Versioning\n\nWe use [Conventional Commits](https://www.conventionalcommits.org/en) to automatically determine the next\nversion of the package. It uses the [semantic-release](https://semantic-release.gitbook.io/semantic-release) package to\nautomate the versioning and release process.\n\nThe functionality is controlled by the `.releaserc.json` file.\nSince the projects that are created from this stack are most likely aren't going to be npm libraries, the npm publishing\nplugin is not included in the configuration.\n\nTo effectively use conventional commits, you need to understand the following basic principle:\n\n**Your commit messages determine if a new deployment happens to production.**\n\nMessages that trigger builds are:\n\n- `fix: ...` - fixes a bug\n- `feat: ...` - adds a new feature\n\nMessages that don't trigger new versions (therefore builds) are:\n\n- `docs: ...` - changes to the documentation\n- `chore: ...` - changes to the build process or auxiliary tools and libraries such as documentation generation\n- `refactor: ...` - code changes that neither fixes a bug nor adds a feature\n- `style: ...` - changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)\n- `test: ...` - adding missing tests or correcting existing tests\n- `ci: ...` - changes to the CI configuration files and scripts\n- `perf: ...` - a code change that improves performance\n\n### Branching Strategy with Semantic Versioning\n\nWe will talk about how the deployment works in the [Deployment](#deployment) section. For now, let's look at how the\nbranching strategy works with the versioning.\n\nThere are 3 main branches:\n\n- `main` - this is the main branch. It is the branch that is deployed to production.\n- `beta` - this is the branch that is deployed to the beta (Staging) environment.\n- `alpha` - this is the branch that is deployed to the alpha (Staging) environment.\n\nWhen you push to the `main` branch, a new version is released to production. The version is determined by the commit\nmessages\nand every commit that is pushed to the `main` branch will trigger a new version.\n\nWhen you push to the `alpha` or `beta` branch, a\nnew [Pre-release](https://semantic-release.gitbook.io/semantic-release/usage/workflow-configuration#pre-release-branches)\nversion is created. This allows you to iterate on features for an upcoming release and not worry about bumping the\nversion\nnumber every time you push a commit that introduces a new feature or a fix.\n\nFor example, if you have a `1.0.0` version in production, and you push a commit to the `alpha` branch, the version will\nbe\n`1.1.0-alpha.0`. If you push another commit to the `alpha` branch, the version will be `1.1.0-alpha.1` and so on.\n\nWhen you merge a pull request from the `alpha` or `beta` branch to the `main` branch, all the changes in those branches\nwill be collected and bundled into a single release. To follow the example above, if you have a `1.0.0` version in\nproduction,\nand merge the `alpha` branch with its `1.1.0-alpha.1` version, your newly created version on production will be `1.1.0`.\n\n```mermaid\n---\ntitle: Branching \u0026 Versioning\n---\n%%{title: '', init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}} }%%\ngitGraph\n    commit id: \"v1.0.0\"\n    branch feature order: 2\n    branch alpha order: 1\n    checkout feature\n    commit id: \"fix: x\"\n    commit id: \"fix: y\"\n    checkout alpha\n    merge feature id: \"v1.0.1-alpha.1\"\n    checkout feature\n    commit id: \"fix: z\"\n    checkout alpha\n    merge feature id: \"v1.0.1-alpha.2\"\n    checkout feature\n    commit id: \"feat: added something cool\"\n    commit id: \"fix: fixed a mistake\"\n    commit id: \"refactor: refactored the tests\"\n    checkout alpha\n    merge feature id: \"v1.1.0-alpha.1\"\n    checkout main\n    merge alpha id: \"v1.1.0\"\n```\n\n#### Linting\n\nWe use [commitlint](https://commitlint.js.org/#/) to lint the commit messages. The configuration is in the\n`package.json` file.\nThe linting happens whenever you make a commit. If the commit message doesn't follow the conventional commits format,\nthe commit will fail.\n\nThe linting itself is triggered by [lefthook](#lefthook)\n\n#### Which version am I running?\n\nThe version of the app is sent into the `\u003chtml data-version=\"...\"\u003e` attribute. You can use this to determine which\nversion\nof the app is running on any given environment.\n\n### Cookie Consent\n\nWe have built a custom cookie consent solution that is compatible with secure XSS protection practices as well as with\nthe EU cookie law.\n\n\u003e **Note**\n\u003e you can read more about this in the [Cookie Consent ADR](./docs/adr/0001-cookie-consent.md)\n\nThe solution is in the `src/components/CookieConsent` folder, and **it is meant to be modified to fit your needs.**\n\nWhen you open up the `_index.tsx` file there, you can see the following interfaces:\n\n```ts\ninterface ConsentData {\n  analytics?: boolean | undefined;\n  //add your own if you need more\n  // marketing?: boolean | undefined;\n  // tracking?: boolean | undefined;\n}\n\ninterface CookieConsentContextProps {\n  analytics?: boolean | undefined;\n  setAnalytics: (enabled: boolean) =\u003e void;\n  //add your own if you need more\n  // marketing?: boolean | undefined;\n  // setMarketing: (enabled: boolean) =\u003e void;\n  // tracking?: boolean | undefined;\n  // setTracking: (enabled: boolean) =\u003e void;\n}\n```\n\nYou will need to modify these in order to add your specific cookie types. For example, if you want to add a `marketing`\ncookie, you will need to add the following:\n\n```ts\ninterface ConsentData {\n  analytics?: boolean | undefined;\n  marketing?: boolean | undefined;\n}\n\ninterface CookieConsentContextProps {\n  analytics?: boolean | undefined;\n  setAnalytics: (enabled: boolean) =\u003e void;\n  marketing?: boolean | undefined;\n  setMarketing: (enabled: boolean) =\u003e void;\n}\n```\n\n#### Using the consent provider\n\nIn order to adhere to the cookie consent, you will need to identify the elements of your project that add a specific\ntype of cookie.\n\n\u003e A good example in this stack is the GoogleAnalytics component. It is located in the `src/components/GoogleAnalytics`\n\nThe cookie consent provider is use in the `root.tsx` file, so it's available for all your components.\nTo use it, all you need to do is:\n\n```tsx\nconst { analytics } = useContext(CookieConsentContext);\n\nif (analytics) {\n  //add your analytics code here\n}\n```\n\n### Dependency Version Updates\n\nWe use [Renovate](https://www.mend.io/free-developer-tools/renovate) to automatically update the dependencies.\nThe configuration is in the `.github/renovate.json` file.\n\nBy default, it is configured to update the dependencies according to some basic rules:\n\n#### Runtime dependencies\n\n\u003e Runtime dependencies are the `dependencies` section in the `package.json` file\n\nRuntime dependencies are the libraries we use to run the application. This also means that security and bug fixes are\nimportant for these dependencies.\n\nWe want to update these dependencies as soon as possible, so we have the following configuration:\n\n- `minor and patch versions` - create a pull request with a `fix: ` prefix in the commit message and merge automatically\n  if possible\n- `major versions` - create a pull request with a `fix: ` prefix in the commit message and do NOT merge automatically\n\n#### Development dependencies\n\n\u003e Development dependencies are the `devDependencies` section in the `package.json` file\n\nDevelopment dependencies are the libraries we use to develop the application. This means that we don't need to release\na new version of the app when we update these dependencies.\n\nWe still want to update these dependencies as soon as possible, so we have the following configuration:\n\n- `minor and patch versions` - create a pull request with a `chore: ` prefix in the commit message and merge\n  automatically if possible\n- `major versions` - create a pull request with a `chore: ` prefix in the commit message and do NOT merge automatically\n\n### Deployment\n\n\u003c!-- initremove:begin --\u003e\nOne of the main focuses of this stack was to create a deployment strategy that is a good starting point for anyone\nbuilding from this stack.\n\u003c!-- initremove:end --\u003e\nWe use a combination of [GitHub Actions](#github-actions) and [AWS CDK](#cdk) to deploy the application to both the\nproduction-like and ephemeral environments.\n\n#### Ephemeral Environments\n\nEphemeral environments are environments that are created on-demand and destroyed when they are no longer needed.\nWe use these for feature branches and pull requests.\n\nThey are automatically created for pull requests, but you will have to manually trigger one if you just want to deploy\na feature branch.\n\n##### Manual Ephemeral Deployment\n\nNavigate to https://github.com/meza/trance-stack/actions/workflows/ephemeralDeploy.yml and click the \"Run workflow\"\nbutton.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/images/github-run-workflow.png\" alt=\"Run workflow button\" /\u003e\n\u003c/p\u003e\n\nOnce you have chosen a branch, it will start building the application and deploying it to the ephemeral environment.\n\nWhen the process is finished, it will publish a summary to the run's Summary Dashboard with the link to the deployed\napplication. It will look something like this:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/images/github-deployment-details.png\" alt=\"Run workflow summary\" /\u003e\n\u003c/p\u003e\n\n##### Pull Request Ephemeral Deployment\n\nWhen you create a pull request, GitHub Actions will automatically create an ephemeral environment for you and the\ndeployment link will be added to the pull request as a comment.\n\n#### Production-like Environments\n\nProduction-like environments are environments that are created once and then updated when the application is updated.\n\nThe branch `main` is considered to be the production branch while `alpha` and `beta` are considered to be staging.\n\nThis is decided in the `deploy.yml` file:\n\n```yaml\n  build:\n    environment: ${{ github.ref_name == 'main' \u0026\u0026 'Production' || 'Staging' }}\n```\n\nThe `Production` and `Staging` words here directly reference the [GitHub Environments](#environments) that we have\nconfigured.\n\n\u003e **Warning**\n\u003e This means that both the `alpha` and `beta` branches will be deployed to the `Staging` environment.\n\u003c!-- initremove:begin --\u003e\nThis was done for convenience with the stack, but you are highly encouraged to change this to suit your needs.\nMaybe add a separate `alpha` environment?\n\u003c!-- initremove:end --\u003e\n\n\u003e **Note**\n\u003e Remember that the GitHub Environments hold the environment variables used for that given workflow. This means that you\n\u003e can set a different `APP_URL` for each environment among other things like a separate Auth0 tenant.\n\n#### GitHub Actions\n\nGitHub Actions respond to various events in the repository's lifecycle.\nThe diagram below shows the flow of the deployment process.\n\n```mermaid\nflowchart TD\n    F1 -.-\u003e|Manual Trigger| F\n\n    subgraph Push\n        A[Push] --\u003e D{Is Protected Branch?}\n\n        D --\u003e|Yes| H{Is it the 'main' branch?}\n        D --\u003e|No| F1[Offer Manual Ephemeral Deployment]\n        H --\u003e|Yes| I1{{Deploy to Production}}\n        H --\u003e|No| I2{{Deploy to Staging}}\n\n        I1 --\u003e J1[Create GitHub Release]\n        H --\u003e|Yes| J2[Deploy Storybook]\n        I2 --\u003e J1\n    end\n    subgraph Pull Request\n        B[Pull Request] --\u003e F{{Ephemeral Deployment}}\n    end\n\n    subgraph Cleanup\n        C[Delete Branch] --\u003e X{{Destroy Deployment Stack}}\n    end\n```\n\nThe hexagonal nodes are processes which are executed by [CDK](#cdk) while the others are handled with GitHub Actions.\n\n#### CDK\n\n[AWS Cloud Development Kit (CDK)](https://docs.aws.amazon.com/cdk/index.html) is an open-source software development\nframework to define cloud infrastructure in code and provision it through AWS CloudFormation.\n\n\u003e **Note**\n\u003e If you are interested in why we chose CDK, check\n\u003e out [the relevant ADR](./docs/adr/0008-use-aws-cdk-for-deployments.md)\n\nThe majority of the infrastructure is defined in the `deployment` directory. The `deployment/lib` directory contains the\ncustom [Constructs](https://docs.aws.amazon.com/cdk/v2/guide/constructs.html) that are used to build the infrastructure.\n\n##### Environment Variables\n\nIn order to deploy the application, you will need to set the following environment variables:\n\n| [Variable][gh-variables] | [Secret][gh-secrets]  | Description                                               |\n|--------------------------|-----------------------|-----------------------------------------------------------|\n|                          | AWS_ACCESS_KEY_ID     | The AWS access key ID used to deploy the application.     |\n|                          | AWS_CERT_ARN          | The ARN of the certificate used for the domain.           |\n|                          | AWS_SECRET_ACCESS_KEY | The AWS secret access key used to deploy the application. |\n| AWS_DOMAIN_NAME          |                       | The final domain name of the application.                 |\n| AWS_HOSTED_ZONE_NAME     |                       | The name of the hosted zone in Route53.                   |\n\nIf you came here from the top of the document, [go back to where you were](#setup-for-continuous-deployment) and\ncontinue from there.\n\n###### Local Environments\n\nIf you want to deploy the application locally, you will need to set the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`\nenvironment variables only.\n\n##### The deployment directory\n\nThe `deployment/stacks` directory contains the actual stacks that are deployed to AWS. Their naming should be\nself-explanatory.\nWe have one for the `Ephemeral` environment and one for the `Production` environment.\n\nIf you examine either of the deployment files, you will notice that the deployment is basically\na single command:\n\n```bash\n npx cdk deploy remix-trance-stack-ephemeral -O /tmp/deployment.result.json \\\n --require-approval never \\\n --context environmentName=${{ env.REF_NAME }} \\\n --context domainName=${{ vars.AWS_DOMAIN_NAME }} \\\n --context certificateArn=${{ secrets.AWS_CERT_ARN }} \\\n --context hostedZoneName=${{ vars.AWS_HOSTED_ZONE_NAME }}\n```\n\nThe difference between the ephemeral and the production deployments is the name of the stack.\nIt can be either `remix-trance-stack-ephemeral` or `remix-trance-stack-production`.\n\n##### The context variables\n\nThe context variables are used to pass information to the CDK stack.\n\n| Variable          | Description                                                                                                  | Example                                                                          |\n|-------------------|--------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|\n| `environmentName` | The name of the environment. This is used to create derive the name of every single resource created on AWS. | feature1                                                                         |\n| `domainName`      | The domain name of the application.                                                                          | trance-stack.vsbmeza.com                                                         |\n| `certificateArn`  | The ARN of the certificate used for the application.                                                         | arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012 |\n| `hostedZoneName`  | The name of the hosted zone used for the application.                                                        | vsbmeza.com                                                                      |\n\n\u003e The `domainName`, `certificateArn` and `hostedZoneName` are only used for Production deployments.\n\n\u003e **Note**\n\u003e Even though some context variables are only used for production deployments, they are still passed to the\n\u003e ephemeral deployment. This is because the CDK stack is the same for both environments and the evaluation of the\n\u003e context variables is done at runtime.\n\u003e For ephemeral deployments you can have an empty string for the `domainName`, `certificateArn` and `hostedZoneName`.\n\n##### Deploying from your local machine\n\nWe advise you to use the GitHub Actions to deploy the application. However, if you want to deploy from your local\nmachine,\nyou can do so by running the same command as the deployment scripts would.\n\n\u003e **Warning**\n\u003e Don't forget to run `npm run build` before deploying.\n\nYou can define the context variables either on the command line or you can use the `cdk.context.json` file.\n\n```json\n{\n  \"environmentName\": \"localdev\",\n  \"domainName\": \"trance-stack.example.com\",\n  \"hostedZoneName\": \"example.com\",\n  \"certificateArn\": \"arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012\"\n}\n```\n\n##### The githubActionSupport.ts file\n\nLet's talk about the `githubActionSupport.ts` file.\n\nThis file uses the GitHub Actions toolkit to allow us to report back with the deployment URL to the GitHub Actions/Pull\nRequest.\n\nThe reason it's a bit more complex than it needs to be is because we don't want to publish a PR comment every time we\ndeploy the same branch. Since the URL won't change for a branch that's already deployed, there is no need to spam the\nPR.\n\nThis posed a challenge of finding an existing deployment comment and updating it instead of creating a new one.\n\n###### Testing the GitHub support locally\n\nIf for whatever reason you would like to get a local output of the GitHub Actions support, you can do so by running the\nfollowing command:\n\n```bash\nnpx ts-node --prefer-ts-exts deployment/githubActionSupport.ts /tmp/deployment.result.json\n```\n\nThis requires you to have a `deployment.result.json` file in the `/tmp` directory. You can get this file by running the\n[deployment command locally](#deploying-from-your-local-machine).\n\nThe results will be added to the `deploymentSummary.md` file.\n\n### Environment variables\n\nEnvironment variables are probably the biggest pain-point in the maintenance of this project.\nYou have to add them to GitHub, add them to the deploy scripts and add them to the `.env` file.\n\nWe're working on a solution to this, but for now, you have to do it manually.\n\n#### Adding a new environment variable checklist:\n\nAdd the variable to...\n\n- [ ] the `.env` file\n- [ ] the `.env.example` script. **This is very important**\n- [ ] the `.github/workflows/deploy.yml` script to the `npm run build` command\n- [ ] the `.github/workflows/ephemeralDeploy.yml` script to the `npm run build` command\n- [ ] the `.github/workflows/ephemeralDestroy.yml` script to the `npm run build` command\n- [ ] the `.github/workflows/playwright.yml` script to the `Create Envfile` section\n\n#### Bundling environment variables\n\nWe bundle most of the environment variables into the server bundle. To understand why,\nread [the relevant adr](./docs/adr/0005-bundling-environment-variables.md),\nand [it's addendum](./docs/adr/0009-no-more-need-to-bundle-environment-variables.md).\n\nThe important thing to know is that what gets bundled is decided by reading the `.env.example` file and taking its\nkeys.\n\nYou can prevent certain keys to get bundled by adding them to the deny list in the `remix.config.js` file.\n\n```js\n  const doNotBundleEnv = [\n  'APP_DOMAIN' // deny list for the environmentPlugin\n]\n```\n\n### Feature Flags\n\nFeature flags are a fantastic way to test new features in production without having to worry about breaking anything.\nIt enables you to decouple the release of new code from the release of new\nfeatures. [Read more](https://posthog.com/docs/feature-flags/manual)\n\nLet's look at an example which is in the `src/routes/_index.tsx` file\n\n```tsx\nexport const loader: LoaderFunction = async ({ request, context }) =\u003e {\n  const isAuth = await hasFeature(request, Features.AUTH);\n  return json({\n    isHelloEnabled: await hasFeature(request, Features.HELLO),\n    isAuthEnabled: isAuth\n  });\n};\n\nexport default () =\u003e {\n  const { isHelloEnabled, isAuthEnabled } = useLoaderData\u003ctypeof loader\u003e();\n  if (isHelloEnabled) {\n    return (\u003cdiv\u003e\n      \u003cHello/\u003e\n      {isAuthEnabled ? \u003cLogin/\u003e : null}\n    \u003c/div\u003e);\n  }\n  return \u003cdiv\u003eGoodbye World!\u003c/div\u003e;\n};\n```\n\nHere all elements of the page are wrapped in a feature flag. The `Hello` component will only be rendered if the `HELLO`\nfeature is enabled. The `Login` component will only be rendered if the `AUTH` feature is enabled.\n\n#### Differentiating between environments\n\nIn PostHog, your main unit is called an Organization. An organization can have multiple \"projects\" which are\nessentially environments. For example, you can have a `production` project and a `staging` project.\n\nThis allows you to have different feature flags, users and data for each environment. Feel free to create a new\nproject for each environment and then set the appropriate environment variables.\n\n### I18N - Internationalization\n\nWe're using i18next for internationalization. You can read more about it in\nthe [i18next documentation](https://www.i18next.com/).\nTo integrate it with Remix, we're using the [remix-i18next](https://github.com/sergiodxa/remix-i18next) package and our\nsetup\nis based on the remix-i18next Readme file.\n\nYou can find the i18n configuration in the `src/i18n` directory. The `i18n.config.ts` file contains the configuration\nfor\nthe defaults of i18next. The `i18n.server.ts` file contains the configuration for the server side while\nthe `i18n.client.ts`\nfile contains the configuration for the client side.\n\nThe only deviation we have from the remix-i18next sample setup is that we're actually bundling the translations into the\nserver package. This is done in the `src/i18n/i18n.server.ts` file.\n\n```ts\nawait i18nextInstance.init({\n  debug: process.env.I18N_DEBUG === 'true',\n  ...baseConfig,\n  lng: locale,\n  ns: remixI18next.getRouteNamespaces(remixContext),\n  // The sample setup in remix-i18next\n  //backend: {\n  //  loadPath: resolve(\"./public/locales/{{lng}}/{{ns}}.json\"),\n  //},\n  resources: {\n    en: {\n      translation: en\n    }\n  }\n});\n```\n\nWe're doing this because in the [AWS Lambda](https://aws.amazon.com/lambda/) environment, we have one single file as the\nhandler, and it needs to be self-contained. While traditional lambda functions could have access to attached file\nsystems,\nit would make deployments more complicated and the function would become incompatible\nwith [Lambda@Edge](https://aws.amazon.com/lambda/edge/) solutions.\n\nTherefore, instead of using the `fs-backend`, we're directly importing the resources from the `public/locales`\ndirectory.\n\nThis does mean that when you add a new locale, you will have to add it to the resources in the `i18n.server.ts` file.\n\n#### Using translations\n\nTo use translations in your application, you can use the `useTranslation` hook from the `react-i18next` package.\n\n```tsx\nimport { useTranslation } from 'react-i18next';\n\nexport const Hello = () =\u003e {\n  const { t } = useTranslation();\n  return (\n    \u003ch1 data-testid={'greeting'} className={'hello'}\u003e{t('microcopy.helloWorld')}\u003c/h1\u003e\n  );\n};\n```\n\nYou can also [pass in variables](https://www.i18next.com/translation-function/interpolation#working-with-data-models) to\nthe translations. This helps the translators to create more context-sensitive translations.\n\nTake this example from the initial logged in Dashboard of the application:\n\n```tsx\nexport default () =\u003e {\n  const { t } = useTranslation();\n  const { user } = useLoaderData\u003ctypeof loader\u003e();\n  return (\u003c\u003e\n    \u003cdiv\u003e{t('dashboard.for', { name: user.nickname || user.givenName || user.name })}\u003cbr/\u003e\u003cLogout/\u003e\u003c/div\u003e\n  \u003c/\u003e);\n};\n```\n\nHere we pass in the `name` variable to the translation. This means that the location of where the name appears in the\nfinal\ntext can be different in different languages. For example, in one context we could say \"Dashboard for John!\" and in\nanother context\nwe could say \"John's dashboard!\".\n\nThe translation file in our dashboard's case looks like this:\n\n```json\n{\n  \"dashboard\": {\n    \"for\": \"Dashboard for {{ name }}\"\n  }\n}\n```\n\n#### Adding a new locale\n\nTo add a new locale, you will have to do the following:\n\n1. Add the new locale to the `public/locales` folder. Follow the example of the existing locale(s)\n2. Add the new locale to the `resources` object in the `i18n.server.ts` file.\n3. Add the new locale to the `supportedLngs` array in the `i18n.config.ts` file.\n\n#### Removing i18n from your project\n\nIf you don't want to use i18n, you can remove it from your project. You will have to do the following:\n\n1. Remove the `i18n` folder from the `src` directory\n2. Remove the `locales` folder from the `public` directory\n3. Run `npm remove i18next i18next* *i18next`\n4. Remove the `\u003c\u003cI18nextProvider ...\u003e` from both the `src/entry.server.tsx` and `src/entry.client.tsx` files\n5. Follow the compilation errors and remove any remaining references to i18n\n\n\u003e **Note**\n\u003e\n\u003e There are some great tips about organising your translations in the\n\u003e [i18n Readme file](./src/i18n/README.md).\n\n### Lefthook\n\nThe commit validation and the automatic dependency installation is done\nby [Lefthook](https://github.com/evilmartians/lefthook)\n\nThe configuration file is at `.lefthook.yml`.\nYou can see all the commands that happen and the git hooks they are attached to.\n\nIf running all the tests at every commit is too much, you can always set it to happen on pre-push instead.\n\n### NPMIgnore - automated\n\nIn case you would ever want to publish your project to NPM (which you shouldn't), you can use\nthe [npmignore](https://www.npmjs.com/package/npmignore) package to\nautomatically generate an `.npmignore` file. This file will be generated based on the `.gitignore` file.\n\nThere is a basic ignore configuration in the `package.json` file's `publishConfig` section.\n\n### Playwright - End-to-end testing\n\nWe use Playwright for our end-to-end tests. Playwright is a successor to Cypress and Puppeteer. It's maintained by\nMicrosoft and is a cross-browser testing tool. It's also a lot faster than Cypress.\n\nLearn more about Playwright [here](https://playwright.dev/docs/intro).\n\n#### Installing Playwright dependencies\n\nPlaywright requires a few dependencies to be installed in order to run locally. You can install them by running the\nfollowing command:\n\n```bash\nnpx playwright install --with-deps\n```\n\n#### Configuring Playwright\n\nThe tests are located in the `playwright/e2e` directory. Feel free to change the directory structure to your liking.\nIf you do so, don't forget to update the test location in the `playwright.config.ts` file.\n\n```ts\nexport default defineConfig({\n  testDir: './playwright/e2e', // \u003c-- Update this\n```\n\n**You do not need to start the dev server before running the tests.**\n\nPlaywright will start the dev server for you. It is configured in the `playwright.config.ts` file right\nat the bottom:\n\n```ts\n  /* Run your local dev server before starting the tests */\nwebServer: {\n  command: 'npm run dev',\n    url\n:\n  'http://localhost:3000',\n    timeout\n:\n  1 * 60 * 1000,\n    reuseExistingServer\n:\n  !process.env.CI\n}\n```\n\n#### Running the tests\n\n##### Playwright on GitHub Actions\n\nEvery time you open a pull request to the `main` branch, the tests will be run on GitHub Actions.\n\n##### Playwright locally\n\nYou can run the tests locally by running the following command:\n\n```bash\nnpm run int\n```\n\nThe reports will go to the `reports/e2e` directory.\n\n### Storybook\n\nWe use [Storybook V7](https://storybook.js.org/releases/7.0) with Webpack 5.\nRemix is still a bit behind in terms of Storybook support, so we had to do a few things to get it to work.\n\n\u003e **Warning**\n\u003e Storybook 7 brings some fundamental changes to how Storybook works.\n\u003e It is **HIGHLY** encouraged that you read\n\u003e the [migration guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-65x-to-700)\n\u003e to see what changed. Things that you are used to might not work the same way anymore.\n\nThere is an [ongoing discussion](https://github.com/remix-run/remix/discussions/2481) within the Remix community\nabout how to best solve this issue.\n\nThis code doesn't include the remixStub yet, but it might change soon.\n\n\u003e If you know how to configure it properly, please open a PR.\n\n#### Running Storybook\n\nYou can run Storybook by running the following command:\n\n```bash\nnpm run storybook\n```\n\nIf you're looking for inspiration on how to organise your stories, you can check out the\n[Telekom Scale project](https://telekom.github.io/scale/?path=/docs/scale-design-system--page)\n\n#### Publishing Storybook\n\nRemember when we set up [Pages](#pages) at the beginning?\n\nStorybook automatically gets published to GitHub Pages when you push to the `main` branch.\n\nThis is done via the `.github/workflows/storybook.yml` workflow.\n\n##### Accessing the published Storybook\n\nRight at the top of this README, you can see a badge linking to the published Storybook.\n\n### Styling / CSS\n\nWe use regular stylesheets in this project which means a combination\nof [Shared Component Styles](#shared-component-styles)\nand [Surfacing Styling](#surfacing-styling).\n\n#### Shared Component Styles\n\nThe shared component styles live in the `src/styles` directory. They are imported in the routes that use them.\n\n```ts\n// src/root.tsx\nimport styles from './styles/app.css';\n\nexport const links: LinksFunction = () =\u003e {\n  return [\n    { rel: 'stylesheet', href: styles }\n  ];\n};\n```\n\nThe styles that are uniform across the entire application are loaded from the `src/root.tsx` file while the styles that\nare\nspecific to a single route are loaded from the route itself.\n\nThese are all additive, so you can have a single stylesheet that is loaded on every route via the `root.tsx`, and then\nadditional stylesheets that are loaded on specific routes.\n\nIf you need a component-specific stylesheet, you can use the [Surfacing Styling](#surfacing-styling) method.\n\n#### Surfacing Styling\n\nTo have local styles per component, we use\nis [Surfacing Styling](https://remix.run/docs/en/main/guides/styling#surfacing-styles).\n\n\u003e _Because these are not routes, and therefore not associated with a URL segment, Remix doesn't know when to prefetch,\n\u003e load, or unload the styles. We need to \"surface\" the links up to the routes that use the components_\n\nThis solution is a bit more complex, but it allows us to have styles that are only loaded when the component is loaded.\n\nTake the `Hello` component as an example:\n\n```tsx\nimport { useTranslation } from 'react-i18next';\nimport styles from './hello.css';\n\nexport const links = () =\u003e [\n  { rel: 'stylesheet', href: styles }\n];\n\nexport const Hello = () =\u003e {\n  const { t } = useTranslation();\n  return (\n    \u003ch1 data-testid={'greeting'} className={'hello'}\u003e{t('microcopy.helloWorld')}\u003c/h1\u003e\n  );\n};\n\nexport default Hello;\n```\n\nNotice that it imports the `hello.css` file. This file is located in the same directory as the component.\nIt also has a `links` export that returns the stylesheet link.\n\nIn Remix terms however, a component is not a route, so we need to \"surface\" the links up to the routes that use the\ncomponents.\nYou can see an example of this in the `src/routes/_index.tsx` file:\n\n```tsx\nimport { Hello, links as helloLinks } from '~/components/Hello';\n\nexport const links: LinksFunction = () =\u003e ([\n  ...helloLinks()\n]);\n```\n\nWe import the `links` export from the `Hello` component and add it to the `links` export of the `_index.tsx` route.\n\nYes, this is more complicated than it should be but with the rapid development of Remix, we hope that this will be\nsimplified in the future.\n\n#### PostCSS\n\nWe use [PostCSS](https://postcss.org) to process CSS. Remix has a built-in PostCSS plugin that allows you to\nimport CSS files directly into your components. Read more about\nhow [CSS in Remix](https://remix.run/docs/en/main/guides/styling#built-in-postcss-support) works.\n\nOur PostCSS configuration is located in the `postcss.config.js` file, and it gets applied every single time Remix builds\nthe application.\nThis means that you don't have to think about prefixes or other browser-specific CSS features. Just write your CSS and\nPostCSS will take care of the rest automagically.\n\n### Typescript Paths\n\nWe use [Typescript Paths](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping).\nThis means that instead of messy relative paths in the imports, we can use handy aliases.\n\nWe have the following paths defined by default:\n\n- `~` - the `src` folder\n- `@styles` - the `src/styles` folder\n- `@test` - the `test` folder\n\nThis means that no matter where you are in the file tree, you can always reference the `src` folder with the `~` alias.\n\n```ts\nimport Hello from '~/components/Hello';\nimport appStyles from '@styles/app.css';\nimport { renderWithi18n } from '@test';\n```\n\nFeel free to add your own paths in the `tsconfig.json` file.\n\nCommon ones that you might want to add are:\n\n- `@components` - the `src/components` folder\n- `@routes` - the `src/routes` folder\n- `@hooks` - the `src/hooks` folder\n\nWe have chosen not to add those because `~/hooks` and `@hooks` are not that different to warrant extra settings.\n\n#### Issues with Typescript Paths\n\nUnfortunately, typescript paths are somewhat esoteric and support across tools can be spotty.\n\n##### Vitest\n\nVitest for example needs special configuration to handle it. You can find the configuration in the `vitest.config.ts`\nfile.\nIt both requires the [vite-tsconfig-paths](https://www.npmjs.com/package/vite-tsconfig-paths) plugin and in some cases\nyou need to manually add the path to the `resolve.alias` array.\n\n```ts\n// vite.config.ts\nresolve: {\n  alias: {\n    '~'\n  :\n    path.resolve(__dirname, './src')\n  }\n}\n```\n\n##### Storybook\n\nStorybook also needs to be told to respect the typescript paths. We use\nthe [tsconfig-paths-webpack-plugin](https://www.npmjs.com/package/tsconfig-paths-webpack-plugin)\nto tell the storybook webpack config to respect the paths.\n\nWe add it to the `webpackFinal` function in the `.storybook/main.ts` file.\n\n```ts\nwebpackFinal: async config =\u003e {\n  config.plugins?.push(new DefinePlugin({\n    __DEV__: process.env.NODE_ENV !== 'production'\n  }));\n  if (config.resolve) {\n    config.resolve.plugins = config.resolve.plugins || [];\n    config.resolve.plugins.push(new TsconfigPathsPlugin()); // \u003c--- this line\n  }\n  return config;\n}\n```\n\n### Unit Testing\n\nWe use [Vitest](https://vitest.dev/) as the unit testing framework.\nIf you're unfamiliar with Vitest, fear not, its interface is very similar to Jest and you will have no issues getting\nstarted.\n\nThe main configuration file of Vitest is located at `vitest.config.ts`.\n\nThere has been quite a few deliberate decisions made here, so let's go through them.\n\n#### Globals: true\n\nThe globals are off by default but to get `js-dom` to work with vitest, they need to be on.\n\n#### Test reporters\n\nWe use different reporters depending on the environment. In the CI environment, we output `junit` and `cobertura`\nreports\nwhich then get published to the GitHub Actions Summary or as a Pull Request comment.\nOn your local machine, we use the `html` reporter for coverage and a default text reporter for the test results.\n\nIn both cases we also print out a textual representation of the coverage report.\n\nAll the test reporting goes into the `reports` directory.\n\n#### Setup files\n\nIf you look closely, you can see that we have a `setupFiles` section which calls the\n`vitest.setup.ts` file. This file is responsible for setting up the environment for the tests.\nIt installs the `@testing-library/jest-dom` package and sets up a universal `afterEach` hook to clean up after the\ntests.\n\nThis might not be to everyone's liking so feel free to change it. Just remember that if you remove the global\n`afterEach` hook, you will need to clean up after the tests yourself so make sure to run `npm run ci` and see what\nbroke.\n\nSince Remix relies on browser APIs such as fetch that are not natively available in Node.js you may find that your unit\ntests fail without these globals when running with some tools.\n\nIf you need to add more globals, you can do so in the `vitest.setup.ts` file.\n\nSimply add:\n\n```ts\nimport { installGlobals } from '@remix-run/node';\n\n// This installs globals such as \"fetch\", \"Response\", \"Request\" and \"Headers\".\ninstallGlobals();\n```\n\nRead about this more [here](https://remix.run/docs/en/1.14.0/other-api/node#polyfills);\n\n#### Threads\n\nWhile the promise of threads might sound appealing, switching them on drastically reduces the speed of\nvitest. This is a known issue, and we're waiting for it to be fixed.\n\n#### Coverage\n\nThe stack comes with 100%+ coverage to cover edge cases. We know that this isn't everyone's cup of tea,\nso you can remove the `statements`, `branches`, `lines` and `functions` sections from the `coverage`\nconfiguration object if you want to.\n\nAlternatively, you can modify the `report` script in the `package.json` file to remove the `--coverage` flag.\n\n\n[gh-variables]: https://github.com/meza/trance-stack/settings/variables/actions\n\n[gh-secrets]: https://github.com/meza/trance-stack/settings/secrets/actions\n\u003c!-- initremove:begin --\u003e\n\n---\n\n### Development of the stack itself\n\n---\n\n\u003e **Note**\n\u003e\n\u003eA note on lockfiles.\n\u003e\n\u003e Since this is a \"create\" package, lockfiles are not included. This is to ensure that the latest versions of\n\u003e dependencies are used when creating a new project.\n\u003c!-- initremove:end --\u003e\n","funding_links":["https://github.com/sponsors/meza"],"categories":["Starter"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeza%2Ftrance-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmeza%2Ftrance-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeza%2Ftrance-stack/lists"}