19

Using npm workspaces I have a folder structure like this

+-- package.json
+-- package-lock.json
+-- client
|   `-- package.json
+-- shared
|   `-- package.json
`-- server
    `-- package.json

Normally when creating a production build for a nodejs app I would run npm ci --only=production and then copy node_modules into a build artifact. I'm not sure how to do something like that when working with workspaces.

If I run npm ci --only=production --workspace server it splits the dependencies across ./node_modules and ./server/node_modules. Maybe I should copy (merge?) both node_modules into a build artifact?

Another option could be to copy ./package-lock.json and ./server/package.json into a fresh directory and run npm ci --only=production. It does seem to work but I don't know enough about npm to know if this is a good idea.

The requirements are:

  1. node_modules should only include production dependencies for the chosen package
  2. The dependency versions should be determined by package-lock.json.
joshhunt
  • 5,197
  • 4
  • 37
  • 60
  • Did you ever figure out how to do this? – Reed Hermes Mar 22 '22 at 21:26
  • 1
    @ReedHermes I kind of got it working with `npm ci --only=production --workspace server` but it does mean the artifact has an extra directory level which is a bit annoying. I was having a few issues with npm workspaces like this https://github.com/npm/cli/issues/3847 so ending up dropping them for now. – joshhunt Mar 22 '22 at 22:35
  • on the flip side, is there a way to only install root depencies in the root level package.json? – Titan Jul 14 '22 at 10:52

2 Answers2

0

The split between node_modules and server/node_modules is due to hoisting. If there were a way to disable hoisting, then all dependencies of server would be installed to server/node_modules. However, current versions of npm do not provide a way to disable hoisting for workspaces.

There is discussion about adding such a feature in the following issues:

John
  • 29,546
  • 11
  • 78
  • 79
  • There is already a nested install strategy so an option to not hoist seems redundant. However, the nested install strategy appears to be broken for workspaces: https://github.com/npm/cli/issues/6537 – morganney Jun 24 '23 at 15:20
-1

I would use a combination of install configuration options.

Using Node v18.16.0 and npm v9.7.2 here is the output of npm install --help (npm >= v9.7.2 is required to correctly omit dev dependencies):

$ npm install --help
Install a package

Usage:
npm install [<package-spec> ...]

Options:
[-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer|--save-bundle]
[-E|--save-exact] [-g|--global]
[--install-strategy <hoisted|nested|shallow|linked>] [--legacy-bundling]
[--global-style] [--omit <dev|optional|peer> [--omit <dev|optional|peer> ...]]
[--strict-peer-deps] [--no-package-lock] [--foreground-scripts]
[--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root] [--install-links]

aliases: add, i, in, ins, inst, insta, instal, isnt, isnta, isntal, isntall

Run "npm help install" for more info

In this case, I would suggest a combination of

So from the workspaces root:

npm install --install-strategy=nested --omit=dev --workspace=server

You may or may not need -install-strategy.

This should leave a node_modules directory at the root of the workspaces with only the dependencies defined in the server workspace's package.json file with those defined in devDependencies omitted. You can also --omit=peer to remove peerDependencies from the server workspace as well.

NOTE: Depending on your dependency graph, this could still leave multiple node_modules directories that would require combining to create the artifact, i.e. --install-strategy=nested does not appear to be working correctly for npm workspaces. However, the installed deps should correctly be isolated to the workspace passed in --workspace. Here is an example repo showing how those dependencies could be combined.

morganney
  • 6,566
  • 1
  • 24
  • 35
  • 1
    This doesn't work. Even with `--install-strategy=nested`, `npm install` splits the server dependencies between `node_modules` and `server/node_modules`. And even if it did work, it would have the downsides of the nested strategy (very deep directory structures, no de-duplicating). – John Jun 23 '23 at 16:23
  • I suppose item 1 has an implicit “root” node_modules. Looking at the title one could argue the suggested answer does provide installed production deps for only one workspace that could be used to build an artifact. Might be useful down the road. – morganney Jun 23 '23 at 19:39