Mono-repository with typescript and yarn workspaces

jonocairns

Jono Cairns

Posted on June 23, 2021

Mono-repository with typescript and yarn workspaces

Monoliths have their place in many systems. Usually, they stem from the ease of use, allowing cross-cutting changes to be completed in a single pull request. They also have a lot of downsides, but that’s a whole other can of worms.

Here are the main points we had trouble with in our monolith:

  1. How do you share code between sub-projects?
  2. How do you manage typescript compilation for dependant projects? E.g. if a depends on b and I make a change to a, I want b to be compiled as well with the latest version of a.

This was an example of my repository structure:

projectA/
projectB/
projectC/
packages/
Enter fullscreen mode Exit fullscreen mode

Inside my packages/ directory, I had some core shared libraries, like types / shared utils, etc.

Ok — so how do you get it all to work together?

Since we were already using yarn, it seemed like yarn workspaces would be an ideal fit. If I can get the same thing done with the same tools that's a big win.

  • Add a root tsconfig.json
{
  "compilerOptions": {
    "noImplicitAny": true,
  },
  "exclude": [
    "**/node_modules",
    "**/.*/",
    "**/jest.config.js",
    "**/.build"
  ],
}
Enter fullscreen mode Exit fullscreen mode
  • In your root package.json add your ‘packages’
{
  ...
  "private": true,
  "workspaces": {
    "packages": [
      "packages/*",
      "projectA",
      "projectB",
      "projectC",
    ]
  },
}
Enter fullscreen mode Exit fullscreen mode
  • in each of your projects + packages, you need to give them the correct name in their respective package.json files e.g. in projectA/package.json
{
  "name": "@company/projectA",
  ...
  "scripts": {
    "watch": "tsc -b -w --preserveWatchOutput",
    ...
  },
  "dependancies": {
    "@company/types",
    "@company/logging",
    "@company/clients"
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Now run yarn install at the root, and then at each package.

  • Now is a very important part. You need to add a tsconfig.json file for each of the projects. This is to assist typescript with the dependency tree. Below is an example of projectA as it depends on 3 packages — clients, types, and logging. This tells typescript to recompile if it senses any changes in the parent package.

{
  "extends": "../tsconfig",
  "compilerOptions": {
    "outDir": ".build"
  },
  "rootDir": ".",
  "references": [
    {
      "path": "../packages/clients"
    },
    {
      "path": "../packages/types"
    },
    {
      "path": "../packages/logging"
    },
  ],
}
Enter fullscreen mode Exit fullscreen mode
  • Now, along with the correct tsc options provided (primarily -b) any changes to projectA will recompile the dependencies if required and also compile the project itself.

Now while running in the context of projectA you will be able to make changes to the packages it depends on e.g. /packages/types and it will recompile and display errors if there are any, along with being able to change things in projectA as normal.

💖 💪 🙅 🚩
jonocairns
Jono Cairns

Posted on June 23, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related