I have a large JavaScript (TypeScript) monorepo with many subpackages:
root/
package.json
packages/
packageA/
package.json
packageB/
package.json
...
packageZ/
package.json
Libraries that I share across all subpackages are defined in root/package.json
, two examples being vitest and typescript:
root/package.json:
...
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"typescript": "^4.0.0",
"vitest": "^1.0.0"
},
"dependencies": {
"lodash": "^4.0.0"
}
So far, so good. The code in the subpackages (workspaces) has access to the libraries defined at the root; e.g., I can call lodash functions.
But say I then want to define a script in a subpackage:
packageA/package.json:
"scripts": {
"test": "vitest $*",
"build": "tsc --build"
}
If I try to run these scripts from a workspace, I get errors because yarn doesn't know how to find the executable:
$ yarn workspace packageA test
command not found: vitest
$ yarn workspace packageA build
command not found: tsc
Ok, I can define the dependencies directly in packageA
:
packageA/package.json:
"scripts": {
"test": "vitest $*",
"build": "tsc --build"
},
"devDependencies": {
"typescript": "^4.0.0",
"vitest": "^1.0.0"
}
Now yarn is fine with it:
$ yarn workspace packageA test
... stuff happens ...
$ yarn workspace packageA build
... stuff happens ...
... but I don't want to define these dependencies once for every subpackage (dozens of times) if I can help it. I'd rather define them once, at the root, both for simplicity and to ensure I'm always running exactly the same version in every package.
Is there a way to make this work? How can I refer to these executables at the root from a yarn workspace?
Looking at the options for yarn run
there is --top-level
(or -T
) option to use the root binaries:
Check the root workspace for scripts and/or binaries instead of the current one
So you can configure your workspace's package.json like this:
"name": "a",
"scripts": {
"test": "run -T vitest $*",
"build": "run -T tsc --build"
}
Allowing you to run yarn workspace a test
, and yarn workspace a build
.
There is a discussion on GitHub that provides an answer. Also, a similar question and answer from stackoverflow: Yarn 2 root binaries unavailable in all workspaces
If you want to stick with yarn, another option is adding all scripts in the root package.json file. Something like this:
"scripts": {
"test:a": "yarn workspace a run test",
"build:a": "yarn workspace a run build"
}
Then you can simply yarn run test:a
or yarn test:a
, or yarn build:a
.