When I was setting up our production monorepo at modfy.video, I found most typescript monorepo guides were quite lacking in addressing a lot of more detailed problems you run into or how to solve them with modern solutions.
This guide aims to do that for 2021, with the best in class tooling at this time. That said when using modern advanced tooling, you can and will run into some compatibility problems so this guide might be slightly esoteric for you.
This guide is really optimized towards typescript monorepos that also contain packages that can be deployed but really should work for any typescript monorepos.
For this guide, we will be using
pnpmbut this should mostly work the space with
yarnjust swap out
yarnworkspaces. (This will likely not work well with
npmand would not recommend using that)
To get started we need to setup our base directory it will contain a few key files, which will be explained in more detail below.
Your base directory once completed will look something like
We can start by setting up our
Feel free to swap
Some basic dependencies we can install are
These are definitely not all the dependencies but others will be based on your config
.gitignorecan look like this
pnpmworkspaces are really easy you need
Full documentation can be found here https://pnpm.io/workspaces
There are a few options for orchestration tools you can use like rushjs but for this guide we'll just use lerna. Specifically tho, we are not using lerna for package management or linking but just for orchestration.
Similar to the about workspace file we need a
lerna.jsonwhere we set the packages
Note as we don't care about lerna for package management, the npmClient doesn't really matter.
The only lerna command we care about is
lerna run <command>this lets us run a script across all our packages. So
lerna run buildwill build all the packages in our repository
The example below is for work with react, please change the configuration accordingly if you don't need react at all.
For typescript monorepos, we should use a relatively new typescript feature called project references, you can learn more about it here https://www.typescriptlang.org/docs/handbook/project-references.html
Few things to not about it are:
- The only
tsccommand you have is
tsc --buildwhich is typescript's multistage build
- You also have to use the
compositeflag which has a few added requirements https://www.typescriptlang.org/docs/handbook/project-references.html
To use project references you have to manually add the path to each reference like the following
Finally it is good to add these dependencies as global dependencies
Feel free to use your own prettier and eslint config here, but this is just the one I like and use.
Here's a configuration for basic testing with jest
Now that we have setup the base repo, we can setup the individual packages
We will cover few broad types of packages here:
- Packages that depend on other packages
- Packages with testing
- Packages with build steps
Regardless of the type of package, all packages will consist of same basic config
tsconfig.jsonit should be structured like
package.jsonit can be structured normally but should ideally contain these scripts
It depends on the use case but if this is like an interfaces package, it likely requires no other configuration. (not even a build script)
For packages that might need a build script to run regardless, there will be more guidance below.
@projectName/package-bwe should add the following steps to let typescript know about this dependency.
package-bwe add the following to the
package-awe reference this package like
For packages that are using
For testing you need to have to separate tsconfigs, this can be structured like default + build, or default + test. For this example, we will use default + build
Essentially we don't want to build our tests, so we can just ignore them to not cause errors
After this whenever you are building use the
tsc --build tsconfig.build.json
Obviously there are tons of typescript build tools and this category is very broad, even in our monorepo we have four-five different typescript build tools
Think of this more as a broad set of tools you can use to nicely achieve this
esbuild- I cannot stress how awesome esbuild is, its really great and fairly easy to get started with https://esbuild.github.io/
vite- I certainly didn't know vite had a library mode, but it does and it is very good. This would definitely be my recommendation for building any frontend packages for
tsup- This is a minimal configuration build tool which wraps around esbuild and has some nice features.
(All these tools are built upon esbuild, it is really mind blowingly fast)
The only catch with
viteis you don't get a
.d.tsfile. You can generate a
.d.tsfile by adding
tsconfigand then running
If you are using
tsupyou can use the
-dts-resolveflag to generate the same.
All this being said, I would follow this issue on
swcanother fast compiler because it might come with the ability to generate
.d.tsfiles in the future. https://github.com/swc-project/swc/issues/657#issuecomment-585652262
This is a vite config for
reactand it has a few steps
For vite specifically we need to bundle all the
.d.tsfiles into a single declaration file
Tsup is the easiest and just that
tsup src/* --env.NODE_ENV production --dts-resolve
The only caveat is less configurable than esbuild itself.
These packages all have to follow the build steps laid out above but this is something I wanted to explicitly address cause I did not see any other guide talk about this.
npmjs(to the best of my knowledge), luckily here is where
pnpmcomes in clutch.
pnpmsupports the following config, https://pnpm.io/package_json#publishconfig
The catch is you have to use
pnpm publishif you use
npm publishit will not work.
General things to note about publishing, you need access to public and the files you want to include
You will likely have to use these broad categories together when in production, so feel free to mix and match.
- Creating new package with a template, lerna has a cli thing for this but I couldn't seem to be able to configure it. (We use a hacky js script)
- Versioning and publishing packages automatically, lerna had a thing for this too but it isn't great. When a single package goes to
v0.1not all packages have to go to
Would love to hear others solution to these and I can update this space with them
Unfortunately, monorepos are still kinda weird and complicated but I hope I gave you some of the tooling we use to make it easier. I also apologise if this felt a bit disorganized but it is a result of we came up with this structure with many many iterations and if we started new it probably would be a bit cleaner.
Here is an example repo: https://github.com/CryogenicPlanet/typescript-monorepo-example
Finally if you are at all interested in video or video editing come checkout modfy.video