Monorepo Structure
Yarn 4
This project uses yarn 4 workspaces to manage the monorepo. It hoists dependencies shared across workspaces to the root of the project, and allows for a single yarn.lock
file to be used for all packages.
As long as you have yarn 1 installed, you will use the yarn 4 automatically using the version specified in .yarnrc.yml
.
nodeLinker
Yarn 4 philosophically has chosen to go in a direction where dependencies should be vendored and checked into the repo. This is a departure from the way yarn 1 and npm work, where dependencies are installed in the node_modules
directory. They call this Plug'n'Play, and it is enabled by default in yarn 4.
However, this can behavior can be modified through the nodeLinker
value in yarnrc.yml
to use node-modules
, which is the normal behavior for yarn 1 and npm.
Since yarn 1 is no longer being actively worked on, so we are using yarn 4 with the nodeLinker
set to node-modules
.
Patches
Yarn 4 has a patching feature similar to patch-package
through yarn patch <package>
. You can read more about it here: https://yarnpkg.com/cli/patch
Nx
This project uses nx
as a task runner for the monorepo. Nx has many features, including build integration, dependency graph visualization, and more. You can read more about it here: https://nx.dev/
Tasks
npx nx run-many
is a common command used to run a command across all workspaces. For example, npx nx run-many --target=lint
will run the lint
target in all workspaces. Targets are like npm scripts, but they can be run across multiple workspaces.
These are aliased as yarn scripts in the root package.json
file. For example, yarn lint
will run the lint
target in all workspaces.
Affected
Nx also has a affected
command that will run a command on all workspaces that have been affected by a change. For example, npx nx affected:lint
will run the lint
target in all workspaces that have been affected by a change.
project.json
Each workspace needs a project.json
file to register it with nx. This file is used to define the targets for the workspace, and to define the dependencies between workspaces.
Any script in the scripts
section of the package.json
file will be registered as a target in nx. For example, the lint
script in the package.json
file will be registered as a target in nx.
Additionally, you can set target defaults in the root nx.json
file.
Task Caching
Nx has a feature called task caching that will cache the output of a task. Nx can fingerprint whether a task like build
has already run and not changed. The cache is stored in the node_modules/.cache/.nx
directory. If you want to clear the cache, you can delete the node_modules/.cache/.nx
directory.
Linting
Eslint allows for having a parent config file and then extending it in child config files. This project has a generic Typescript linting setup at the top, and then child projects can extend it and add their own rules (for React, Electron, etc.). Read more about it here: https://eslint.org/docs/latest/user-guide/configuring/configuration-files
Typescript
In order for typescript-eslint
to work, we use a tsconfig.base.json
in the parent directory, and then each workspace extends that base with it's own tsconfig.json
file. But in the parent eslint config, you must declare the location of all the tsconfig.json files in a glob pattern. You can read more here: https://typescript-eslint.io/linting/typed-linting/monorepos/#one-tsconfigjson-per-package-and-an-optional-one-in-the-root
Common Gotchas
In order to prevent nested folders in our dist
directory, "rootDir": ".",
needs to be specified in the workspace tsconfig.json
to avoid the parent tsconfig.base.json
from being used.
Prettier
Ignore file
Prettier has a .prettierignore
file that is used to ignore files. This file is used in addition to the .gitignore
file. This is useful for ignoring files that are not checked into git, but still need to be ignored by prettier. Unfortunately, there is no way to extend the .gitignore
file in the .prettierignore
file, so we have to duplicate the entries. See copy-prettier-ignore
in the root package.json
script.