I have a monorepo that uses npm workspaces.
root/
app/
package.json
server/
package.json
store/
package.json
utils/
package.json
The app/package.json
file references private non-published dependencies in its package.json
like this:
{
"dependencies": {
"@my-scope/server": "file:../server",
"@my-scope/store": "file:../store",
"@my-scope/utils": "file:../utils"
}
}
This is working fine when running locally; npm hoists all the node_modules
dependencies to the top of the repo and adds symlinks between our dependent packages. However, things break once I try to produce a .zip
file archive for deploying to Azure. I of course don't want to deploy my entire monorepo, just my app package. However, with all the node_modules
dependencies hoisted up to the parent directory, this won't work.
I've tried running this inside of the app directory:
npm install --workspaces=false
It produces this which is close to what I'm looking for:
app/
node_modules/
direct-dependency-a/
direct-dependency-b/
@my-scope/
server/ -> ../../../server
store/ -> ../../../store
utils/ -> ../../../utils
...but it does not install any of the dependencies of the @my-scope/*
packages.
How can I produce a completely standalone deployment-ready directory from a npm workspaces monorepo?
I found the key. In addition to running an install with --workspaces=false
, adding the --install-links
option to the npm install
command causes the packages to be installed correctly. It copies the files from the monorepo package instead of linking, and it installs its dependencies as well.
Caveat: as @RafaelLeite points out in the comments, this doesn't use lockfiles since your lockfile is hosted at the root of the monorepo, and that can cause "unsupervised" version bumps when installing, so this is still not a perfect solution.