TL;DR
Set up a pnpm workspace and use npm_link_all_packages
multiple times to set up separate node_modules
folders in your bin tree. Then refer to the dependencies in these node_modules
folders by :node_modules/$DEPENDENCY
in your deps
.
How does rules_js create node_modules folders?
rules_js uses the npm_link_all_packages
rule to set up node_modules
folders based on a pnpm-lock.yaml
lockfile. This is similar to the pnpm install
command of the pnpm package manager. The difference is that pnpm install
creates the node_modules
folders in the source tree and npm_link_all_packages
creates them in the bin tree.
Since you want that each package can have different external dependencies, they each need to have their own node_modules
. pnpm supports a single lock file for multiple packages if you set up a pnpm workspace. The resulting lock file can be used by rules_js
to set up a Bazel workspace with multiple packages that each get their own node_modules
folder in the bin tree. The npm_link_all_packages
rule will automatically set up the correct node_modules
folder based on the Bazel package name and the pnpm-lock.yaml
.
So you have to set up a pnpm workspace and use npm_link_all_packages
multiple times to set up separate node_modules
folders in your bin tree.
How exactly do I set this up?
To achieve this, you can put the following pieces in your workspace root:
pnpm-workspace.yaml
to configure the list of pnpm workspace packages
pnpm-lock.yaml
to record how all all direct and indirect npm dependencies are resolved
WORKSPACE.bazel
to setup rules_js
package.json
to avoid confusing pnpm
, and in case you want global scripts or settings for your IDE or similar
BUILD.bazel
to create the virtual store with npm_link_all_packages
And in each pnpm workspace package $PACKAGE
you put:
$PACKAGE/package.json
to configure the direct npm dependencies of $PACKAGE
$PACKAGE/BUILD.bazel
to create the node_modules folder
in the bin tree with npm_link_all_packages
, and for your js_library
or similar
So every pnpm workspace package is also a bazel package (since it has a BUILD.bazel
).
How can I refer to my npm dependencies in deps
?
In the deps
of js_library
and similar, you can point to the dependencies in the node_modules
folders generated by npm_link_all_packages
. Typically, the js_library
is in the same Bazel package as the npm_link_all_package
, so you can just use :node_modules/$DEPENDENCY
. If the js_library
is in a sub-package of $PACKAGE
, you can use //$PACKAGE:node_modules/$DEPENDENCY
instead. Then in your JavaScript files, you can import from "$DEPENDENCY"
or require("$DEPENDENCY")
. This will work out at runtime because the node_modules
folder will be at an appropriate place in the runfiles for node
to find it.
How can the packages in my workspace refer to each other?
If you want one of your packages $PACKAGE
to depend on another one of your packages $OTHER
, you put a "$OTHER": "workspace:*"
into the $PACKAGE/package.json
as usual with pnpm. You also have to make sure that the default target of $OTHER
(for example, //some/path/to/other:other
) is an npm_package
rule. Then you can use :node_modules/$OTHER
or //$PACKAGE:node_modules/$OTHER
in the deps of a js_library
or similar to refer to $OTHER
just like you would refer to an npm dependency.
How can I create the pnpm-lock.yaml
?
Note that this is a working pnpm setup with some additional BUILD.bazel
files, so you can:
- use
pnpm install --lockfile-only
to create the pnpm-lock.yaml
- use
pnpm install
to create the pnpm-lock.yaml
and also create node_modules
folders in the source tree. These folders are not needed for rules_js
but can be helpful for your IDE.