DEV Community

Francesco Menghi
Francesco Menghi

Posted on • Updated on

Publish packages from within a Monorepo

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

David recently shared a link 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
Enter fullscreen mode Exit fullscreen mode

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, I have changed 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, 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:

  "pipeline": {
    "build": {
      "dependsOn": ["^build"]
    "lint": {
      "outputs": []
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

This package is published to the npmjs registry. To call it inside a .eslintrc.js file we use extends:

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

  overrides: [
        // more rules here

Enter fullscreen mode Exit fullscreen mode

Since @senecacdot/eslint-config-telescope lives inside the monorepo, we have automated the release of the package thanks to pnpm publish.

Release to npmjs registry

There is a GitHub Actions workflow that handles the release of Telescope and, at the end of it, I added:

      - uses: pnpm/action-setup@v2.0.1
          version: 6.30.1
      - name: NPM Publish
        uses: actions/setup-node@v2
          node-version: '16.x'
          registry-url: ''
          cache: 'pnpm'
      - run: pnpm install
      - run: pnpm -r publish --no-git-checks --access=public
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (1)

jackmellis profile image

Thanks for this. I've been fighting lerna publish + github actions for a week now and it turns out the solution was to just switch from npm to pnpm for the publish command.