npmnpm-workspaces

How to filter NPM commands to a pattern of workspaces (like filter, include, exclude, glob, grep or subdirectory)?


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:

...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.


Solution

  • 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