|
| 1 | +# Work with the monorepo |
| 2 | + |
| 3 | +This is a monorepo containing many packages. You will need `pnpm`, not `npm`. |
| 4 | + |
| 5 | +## pnpm installation |
| 6 | + |
| 7 | +[See official documentation](https://pnpm.io/installation). Cheat sheet: `npm install -g pnpm` works. |
| 8 | + |
| 9 | +When pnpm is installed, you can clone the [apostrophe repository](https://github.com/apostrophecms/apostrophe) which is now a monorepo (or just fetch / pull main). |
| 10 | + |
| 11 | +## Monorepo behavior |
| 12 | + |
| 13 | +Inside this repository, there are a few things to see that relate to pnpm and the monorepo: |
| 14 | + |
| 15 | +- `.npmrc` file. Here we ask pnpm when installing dependencies to put every package at the root of the node_modules folder. |
| 16 | + |
| 17 | +```jsx |
| 18 | +public-hoist-pattern[]=* |
| 19 | +``` |
| 20 | + |
| 21 | +Otherwise, pnpm installs dependencies of each dependencies and allow multiple modules to have the same dependency with a different version. |
| 22 | + |
| 23 | +We cannot do that because apostrophe expects to find everything at the root of `node_modules`. |
| 24 | + |
| 25 | +That’s why we use this option. It still installs very fast because it uses symlinks. |
| 26 | + |
| 27 | +(In local development, pnpm has a global store where it stores dependencies and symlinks them in projects, that’s why it’s very fast.) |
| 28 | + |
| 29 | +- `pnpm-workspace.yaml` file. This file defines the structure of the monorepo to pnpm. It’s very simple for us, because we only have packages, but we could have `apps` too, for example, with starter kits. |
| 30 | + |
| 31 | +```jsx |
| 32 | +packages: |
| 33 | + - 'packages/*' |
| 34 | +``` |
| 35 | + |
| 36 | +- In `package.json` file, we have recursive scripts, allowing to run a command that will be run in every package: |
| 37 | + |
| 38 | +```jsx |
| 39 | + "scripts": { |
| 40 | + "lint": "pnpm --recursive run lint", |
| 41 | + "test": "pnpm --recursive run test", |
| 42 | + "eslint": "pnpm --recursive run eslint", |
| 43 | + "mocha": "pnpm --recursive run mocha", |
| 44 | + "clean": "pnpm -r exec rm -rf node_modules && rm -rf node_modules && rm pnpm-lock.yaml" |
| 45 | + }, |
| 46 | +``` |
| 47 | + |
| 48 | +We also have `pnpm` configuration inside `package.json`. We have to tell pnpm which packages have the permission to run post install scripts. This reduces the risk of attacks like `sha1 hulud` which is important to keep us out of the headlines: |
| 49 | + |
| 50 | +```jsx |
| 51 | + "pnpm": { |
| 52 | + "onlyBuiltDependencies": [ |
| 53 | + "sharp", |
| 54 | + "vue-demi", |
| 55 | + "@parcel/watcher", |
| 56 | + "esbuild", |
| 57 | + "unrs-resolver" |
| 58 | + ] |
| 59 | + }, |
| 60 | +``` |
| 61 | + |
| 62 | +- Inside `package.json` you will also see new syntax for internal dependencies: |
| 63 | + |
| 64 | +```jsx |
| 65 | + "devDependencies": { |
| 66 | + "eslint": "^9.39.1", |
| 67 | + "eslint-config-apostrophe": "workspace:^" |
| 68 | + } |
| 69 | +``` |
| 70 | + |
| 71 | +`workspace:` allows us to install a package of the monorepo as a dependency of another package of the monorepo. It’s automatically resolved to the right versionwhen running `pnpm publish`. |
| 72 | + |
| 73 | +There is just one issue with this, in `testbed`, for example, we install dependencies this way, directly from the monorepo. |
| 74 | + |
| 75 | +```jsx |
| 76 | +"apostrophe": "github:apostrophecms/apostrophe#main&path:packages/apostrophe" |
| 77 | +``` |
| 78 | + |
| 79 | +(Note the use of `path`, which allows us to pull in a module from a subdirectory of the repo. Regular npm does not support this so we only use it during testing.) |
| 80 | + |
| 81 | +The problem is that in the monorepo, apostrophe has `workspace` dependencies. While it works perfectly inside the monorepo, when installing dependencies in testbed for example, which is external to the monorepo, `pnpm` needs to know how to rewrite them. |
| 82 | + |
| 83 | +So we use a pnpm feature called `pnpmfile` ([see documentation](https://pnpm.io/pnpmfile)). Basically this allows us to hook into the installation process. We can then rewrite the `workspace:` dependencies. We also use this in pro modules that need apostrophe core for tests and are outside of the monorepo. |
| 84 | + |
| 85 | +See the testbed’s `pnpmfile` or look in the pro modules where it enables testing against our latest `main` etc. |
| 86 | + |
| 87 | +## How do I run just one test file? |
| 88 | + |
| 89 | +Let’s say you are in the root of the monorepo and you only want to run mocha for the `test/styles.js` file from `apostrophe`: |
| 90 | + |
| 91 | +```bash |
| 92 | +pnpm -C packages/apostrophe mocha test/styles |
| 93 | +``` |
| 94 | + |
| 95 | +To run **all** of the tests for one module, try: |
| 96 | + |
| 97 | +```jsx |
| 98 | +pnpm -C packages/apostrophe test |
| 99 | +``` |
| 100 | + |
| 101 | +## Working locally |
| 102 | + |
| 103 | +### Basic symlinks |
| 104 | + |
| 105 | +To test a module that is still in development as part of a starter kit, you simply need to create a symlink to the monorepo package you need. (No, you can’t use `npm link`, but `npm link` has been quite broken for quite a while now and the team activately avoids it. So this is not really new.) |
| 106 | + |
| 107 | +- Be sure your monorepo dependencies are installed: |
| 108 | + |
| 109 | +```jsx |
| 110 | +cd apostrophe && pnpm install |
| 111 | +``` |
| 112 | + |
| 113 | +- go to your starter-kit and install dependencies here too (make sure you have a `.npmrc` with the hoisting option): |
| 114 | + |
| 115 | +```jsx |
| 116 | +cd starter-kit-essentials && pnpm install |
| 117 | +``` |
| 118 | + |
| 119 | +- Then link the module you need, in this case `apostrophe`: |
| 120 | + |
| 121 | +> Examples here assume you have the monorepo checked out in `~/apostrophecms/apostrophe`, mirroring the github org name and repo name. |
| 122 | +> |
| 123 | +
|
| 124 | +```jsx |
| 125 | +rm -rf ./node_modules/apostrophe |
| 126 | +ln -s ~/apostrophecms/apostrophe/packages/apostrophe ./node_modules/apostrophe |
| 127 | +``` |
| 128 | + |
| 129 | +## Internal dependencies |
| 130 | + |
| 131 | +`pnpm` allows this syntax for internal dependencies. |
| 132 | + |
| 133 | +```jsx |
| 134 | +{ |
| 135 | + "dependencies": { |
| 136 | + "foo": "workspace:*", |
| 137 | + "bar": "workspace:~", |
| 138 | + "qar": "workspace:^", |
| 139 | + "zoo": "workspace:^1.5.0" |
| 140 | + } |
| 141 | +} |
| 142 | +``` |
| 143 | + |
| 144 | +We exclusively want to use this one: |
| 145 | + |
| 146 | +```bash |
| 147 | +"qar": "workspace:^", |
| 148 | +``` |
| 149 | + |
| 150 | +This ensures that when we are ready to publish (see below), the module is published with an npm dependency like: |
| 151 | + |
| 152 | +```bash |
| 153 | +"qar": "3.^", |
| 154 | +``` |
| 155 | + |
| 156 | +So that the customer can `npm update` and get the expected result and not a different major version. |
| 157 | + |
| 158 | +## Changesets |
| 159 | + |
| 160 | +[Changeset CLI documentation](github.com/changesets/changesets/blob/main/docs/command-line-options.md) |
| 161 | + |
| 162 | +### Day to day work |
| 163 | + |
| 164 | +For `CHANGELOG` generation, versioning and publishing we are now using [Changesets](https://github.com/changesets/changesets). |
| 165 | + |
| 166 | +This is a powerful tool made for monorepos. |
| 167 | + |
| 168 | +When you have finished to work on a feature, fix or whatever, just run: |
| 169 | + |
| 170 | +```bash |
| 171 | +pnpm changeset |
| 172 | +``` |
| 173 | + |
| 174 | +It will ask you which packages have been updated by your work. Select each package of interest by pressing the spacebar. |
| 175 | + |
| 176 | +It will then ask what kind of change you made for each package (major / minor / patch). Select "major" only for backwards compatibility breaks - such changes likely will not be accepted without serious consultation. Select "major" for any feature addition. "Patch" for fixes only. |
| 177 | + |
| 178 | +Finally, it will ask for a summary (this will be the changelog entry). |
| 179 | + |
| 180 | +It will create a unique file inside the `.changeset` folder, no more `CHANGELOG` conflicts 🥳. |
| 181 | + |
| 182 | +Include this file in your PR. |
0 commit comments