🧶 Improving Code Sharing with Yarn WorkspacesPublished 2022-01-09
Sharing code between
TypeScript projects has always been more trouble than it should be.
Say you create a common library and you want to use it in
N > 1 of your other projects.
You could publish it to
NPM and install it like any other dependency. This works ok but definitely not if you're still frequently iterating on your shared library. If that's the case, you start hitting a bunch of friction:
- Having to switch repos and working sets to make changes to the shared library
- Having to create and deploy a new package for the shared library after each update. This is overly complicated.
- Having to upgrade the dependency on the library across all projects that use it
The remedy this, what I've ended up doing in the past is installing my common libraries as
git submodules rather than
npm packages. This lets me have the source of my shared libraries in my current working tree for easy browsing, editing and comitting. Having the full source in my current project and depending on that, rather than an
npm package, also means there's no re-packaging, re-deploying, or upgrading steps to go through after modifying the common library.
- app |+ src |- git_modules |+ common_lib_1 |+ common_lib_2
There are still drawbacks to this approach, however.
- Managing dependencies of sub-modules. E.g., If the common libs have their own
node_modulesthose can conflict with the parent project's node modules or cause duplicate copies of the same code to be loaded.
- Imports. I generally end up having to add a bunch of import aliases in my root project to get things seeing one another.
- Building. Some packages may require a build step prior to be usable.
Yarn Workspaces solve (1) & (2) from above.
Yarn Workspaces makes
Yarn aware that all of the packages within the working tree are being used in the same application.
This allows me to
(1) keep the source of my dependencies close by by using them as
(2) install all required packages, without conflicts, in a single
yarn install run
(3) refer to the packages provided by my
git submodules just as if they were installed as normal
If your dependencies, as
git submodules, require special build steps you can look into something like
TurboRepo to manage those. I've not yet looked into this myself as
Parcel, with its 0 config principles, has fit the bill just fine.
Why would you want to do this?
- Your dependencies can be linked together, which means that your workspaces can depend on one another while always using the most up-to-date code available. This is also a better mechanism than yarn link since it only affects your workspace tree rather than your whole system.
- All your project dependencies will be installed together, giving Yarn more latitude to better optimize them.
- Yarn will use a single lockfile rather than a different one for each project, which means fewer conflicts and easier reviews.