I'd like to organise a large NPM monorepo such that I can run commands on many (but not all) workspaces according to a category or pattern.
This could be by subdirectory, or by a pattern in the workspace names like a prefix or suffix, but I can't see any way to do either in NPM alone.
In Yarn >= 2, this is possible using --include
or --exclude
flags with glob patterns on yarn workspaces foreach
, for example:
yarn workspaces foreach --include "@namespace/plugin-*" --include "@namespace/preset-*" run build
...and that builds all your workspaces with that namespace and either the plugin-
or preset-
prefix.
In lerna, the --scope
flag also took globs allowing patterns, for example:
lerna run --scope "@namespace/plugin-*" --scope "@namespace/preset-*" run build
But in NPM, as far I can see in NPM's workspaces documentation for v7 and for v8, it doesn't seem to have the same feature:
--workspace
which may be used multiple times to create an array but appears to only accept exact workspace names,--workspaces
which appears to run in all workspaces,...but I can't see any way to either run on all workspaces in a directory or all workspaces matching a pattern. I really don't want to have to list every workspace by name in every script, as there's going to be a lot of them.
It's not documented (at time of asking), but NPM can filter workspace commands to subdirectories of workspaces by giving the --workspace
flag a path as its value. For example:
npm run --workspace packages/presets --workspace packages/plugins build
This will find workspaces that are directly in $project_root/packages/presets/
or $project_root/packages/plugins/
, but not workspaces in subdirectories of these subdirectories.
Deeper-nested subdirectories can be included using /**
, like:
npm run --workspace packages/** build
The above examples would work with workspaces organised into subdirectories, defined in package.json like:
"workspaces": [
"packages/plugins/*",
"packages/presets/*",
...more
],
...or a catch-all like:
"workspaces": [
"packages/**",
...more
],
While pointing --workspace
to subdirectories of workspaces instead of individual workspaces like this doesn't seem to be documented anywhere at time of writing, it does appear to be an intentional, planned, supported feature. I found a note proposing it deep in one of the rfcs:
Note: Globs are not supported as a valid --workspace argument value, the proposed alternative is to use npm --workspace= in which is a folder containing multiple workspaces.
I couldn't find anything explicitly stating that this proposal was implemented, but I tried reorganising my monorepo into subdirectories and using a subdirectory with the --workspace
flag, and it worked.
Their reason for choosing paths rather than glob patterns in workspace names (like yarn does and lerna did) appears to be because of the:
many pitfalls of supporting globs in the variety of supported shells and operational systems