Publish packages from within a Monorepo
Tags: monorepo, opensource
Published: 2022-03-01 02:47:24


In this blog post I will go over the details of how we manage and publish packages inside our Telescope monorepo.

[David](https://github.com/humphd/) recently shared a [link](https://monorepo.tools/) where I found a good definition for monorepo that I wanted to share:

> A monorepo is a single repository containing multiple distinct projects, with well-defined relationships.

Within Telescope, we have just that:

```
└── src
   ├── api
   ├── backend
   ├── docs
   ├── mobile
   └── web
```

We have all of the APIs, backend, frontend, mobile app and the new Docusaurus docs all inside the same repo.

We recently made the switch to Turborepo and to try out its [features](https://turborepo.org/docs), I have [changed](https://github.com/Seneca-CDOT/telescope/pull/2862) the way we lint our project. We used to run eslint from the root of Telescope, but now we have individual `.eslintrc.js` files inside each project and let Turborepo manage it all.

Since [version 1.1](https://turborepo.org/blog/turbo-1-1-0#turbojson-support), Turborepo config moved from `package.json` to its own `turbo.json` file.

This is our `turbo.json` that defines the `lint` command inside its `pipeline`:

```json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"]
    },
    "lint": {
      "outputs": []
    }
  }
}
```

When we run `pnpm turbo run lint`, Turborepo will go over each package defined in `pnpm-workspace.yaml` and run the lint command. This process is very efficient because it keeps a cache and reuses it if there are no changes to the files in the project.

For example, if I make changes to the docs, Turborepo will only run eslint in the docs, and use the previously created cache for everywhere else.

We have a new `@senecacdot/eslint-config-telescope` package that contains the rules we reuse with every `.eslintrc.js` and it lives inside the `tools` directory:

```
└── tools
   └── eslint
      ├── index.js
      └── package.json
```

This package is [published](https://www.npmjs.com/package/@senecacdot/eslint-config-telescope) to the npmjs registry. To call it inside a `.eslintrc.js` file we use `extends`:

```js
module.exports = {
  extends: '@senecacdot/eslint-config-telescope',

  overrides: [
		// more rules here
  ],
};

```

Since `@senecacdot/eslint-config-telescope` lives inside the monorepo, we have automated the release of the package thanks to [pnpm publish](https://pnpm.io/cli/publish).

### Release to npmjs registry

There is a GitHub Actions [workflow](https://github.com/Seneca-CDOT/telescope/blob/master/.github/workflows/release.yml) that handles the release of Telescope and, at the end of it, I added:

```yaml
      - uses: pnpm/action-setup@v2.0.1
        with:
          version: 6.30.1
      - name: NPM Publish
        uses: actions/setup-node@v2
        with:
          node-version: '16.x'
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'
      - run: pnpm install
      - run: pnpm -r publish --no-git-checks --access=public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```

The key part here is `pnpm -r publish`. The `-r` stands for recursive: pnpm will go look at each `package.json` to see if a package has an updated version and, if so, will publish it to the registry.