![]() |
||
| Bazel | NodeJs | Yarn |
| Rule | Description |
|---|---|
| node_repositories | Install node toolchain. |
| yarn_modules | Install a set node module dependencies using yarn. |
| node_module | Define a node module from a set of source files (having an optional main (or index) entry point). |
| node_binary | Run a node module. |
| node_test | Run a node binary as a bazel test. |
| mocha_test | Run a mocha test script. |
WORKSPACE rule that downloads and configures node based on your
operating system. Includes node (8.15.1) and yarn (1.0.1).
RULES_NODE_COMMIT = '...' # Update to current HEAD
RULES_NODE_SHA256 = '...'
http_archive(
name = "org_pubref_rules_node",
url = "https://github.com/pubref/rules_node/archive/%s.zip" % RULES_NODE_COMMIT,
strip_prefix = "rules_node-%s" % RULES_NODE_COMMIT,
sha256 = RULES_NODE_SHA256,
)
load("@org_pubref_rules_node//node:rules.bzl", "node_repositories")
node_repositories()Install a set of module dependencies into a yarn_modules folder as
an external workspace. Requires either a package.json file or
deps as input.
# In WORKSPACE
load("@org_pubref_rules_node//node:rules.bzl", "yarn_modules")
# Use a package.json file as input. Location of the package json
# is arbitrary.
yarn_modules(
name = "yarn_modules",
package_json = "//:package.json",
)
# Shortcut form without a separate package.json file
yarn_modules(
name = "yarn_modules",
deps = {
"react": "15.3.2",
"react-dom": "15.3.2",
},
)How It Works:
-
Create an external workspace
@yarn_modulesat$(bazel info output_base)/external/yarn_modules. -
Invoke
yarn installto create anode_modulesfolder and populate it with the necessary dependencies. -
Read the generated
yarn.lockfile, parse it, and write out a@yarn_modules//:BUILDfile. This file contains anode_modulerule foreach entry in theyarn.lockfile, anode_modulerule with the special name_all_, and anode_binaryrule foreach executable script in thenode_modules/.binfolder.
Note 1: You can inspect all the targets by running
bazel query @yarn_modules//:*.
Note 2: The workspace name
yarn_modulesis arbitrary, choose whatever you like (other thannode_modulesitself, that one doesn't work).
At this point you can use these rule targets as deps for your
node_module rules. Example:
node_module(
name = "my_module",
package_json = "package.json",
srcs = glob(["**/*.js"]),
deps = [
"@yarn_modules//:_all_",
],
)| Type | Name | Description | |
|---|---|---|---|
| optional | label |
package_json |
A package.json file containing the dependencies that should be installed. |
| optional | string_dict |
deps |
A mapping of name --> version for the dependencies that should be installed. |
| optional | string_list |
post_install |
A list of command-line arguments that should be invoked after the yarn install step. See #55. |
Either
package_jsonordepsmust be present, but not both.
BUILD file rule that creates a folder which conforms to the nodejs Folders as Modules packaging structure. Example:
node_module(
name = "my_module",
main = "index.js",
srcs = [
"lib/util.js",
"lib/math.js",
],
version = "1.2.0",
description = "Example node module",
deps = [
"@yarn_modules//:lodash",
"@yarn_modules//:fs-extra",
],When used in a node_binary rule, this ultimately materializes to:
node_modules/my_module
node_modules/my_module/package.json
node_modules/my_module/index.js
node_modules/my_module/lib/util.js
node_modules/my_module/lib/math.js
node_modules/lodash
node_modules/fs-extra
When used by other node_module rules, you can import the module as:
const myModule = require("my_module");There are three basic ways to create a node_module rule:
node_module(
name = "my_module_1",
package_json = "package.json", # label to the 'package.json' file to use directly
)In this scenario, assumes the package.json file has an entry that
specifies the main entrypoint (or not, if you follow the
Files as Modules
pattern).
node_module(
name = "my_module_2",
main = "app.js", # label to the entrypoint file for the module
version = "1.0.0", # optional arguments to populate the generated package.json file
...
)In this scenario, a package.json file will be generated for the
module that specifies the file you provide to the main attribute.
node_module(
name = "my_module_3",
index = "index.js", # label to the 'index.js' file to use as the index
)In this scenario, no
package.jsonfile is generated.
Build up a dependency tree via the deps attribute:
node_module(
name = "my_module_3",
...
deps = [
"@yarn_modules//:_all_", # special token '_all_' to have access to all modules
":my_module_1",
],
)
| Type | Name | Default | Description | |
|---|---|---|---|---|
| optional | label |
package_json |
None |
Explicitly name a package.json file to use for the module. |
| optional | label |
main |
None |
Source file named in the generated package.json main property. |
| optional | label |
index |
None |
Source file to be used as the index file (supresses generation of a package.json file). |
| optional | label_list |
srcs |
[] |
Source files to be included in the module. |
| optional | label_list |
deps |
[] |
node_module rule dependencies. |
For reference, by default a node_module rule //src/js:my_module
generates node_modules/src/js/my_module.
| Type | Name | Default | Description | |
|---|---|---|---|---|
| optional | string |
namespace |
None |
See 1 |
| optional | string |
module_name |
${ctx.label.package}/{ctx.label.name} |
See 2 |
| optional | string |
separator |
/ |
See 3 |
1 Use to scope the module with some organization prefix. Example: namespace = '@foo' generates node_modules/@foo/src/js/my_module.
2 Override the module name. Example: name = 'barbaz' with namespace (above) generates node_modules/@foo/barbaz
3 Example: separator = '-' generates node_modules/src-js-my_module.
These are only relevant if you don't explicitly name a package.json file.
| Type | Name | Default | Description | |
|---|---|---|---|---|
| optional | string |
version |
1.0.0 |
Version string |
| optional | string |
url |
None |
Url where the module tgz archive was resolved |
| optional | string |
sha1 |
None |
Sha1 hash of of the resolved tgz archive |
| optional | string |
description |
None |
Module description |
| optional | string_dict |
executables |
None |
A mapping from binary name to internal node module path. Example executables = { 'foo': 'bin/foo' }. |
| Type | Name | Default | Description | |
|---|---|---|---|---|
| optional | string |
layout |
relative |
Changes the way files are included in the module. One of relative or workspace. |
Consider a file with the label //src/js/my_module/app.js. With
layout = 'relative' (the default), the location of the file becomes
node_modules/src/js/my_module/app.js (skylark: file.short_path
relative to module_name). Under layout = 'workspace', the it
becomes node_modules/src/js/my_module/src/js/my_module/app.js
(skylark: file.path). This is relevant only for protocol buffers
where the generated sources import their own dependencies relative to
the workspace, which needs to be preserved in the generated module.
The node_binary rule writes a script to execute a module entrypoint.
load("@org_pubref_rules_node//node:rules.bzl", "node_binary")
node_binary(
name = "foo",
entrypoint = ":my_module_1",
)In example above, we're specifying the name of a node_module to
use as the entrypoint.
node_binary(
name = "foo",
main = "foo.js",
deps = [
":my_module_1
],
)In this second example, we're specifying the name of a file to use as
the entrypoint (under the hood, it will just build a node_module
(called foo_module) for your single main foo.js file entrypoint,
becoming equivalent to the first example).
node_binary(
name = "foo",
entrypoint = ":my_module_2",
executable = "baz",
)In this third example (above), we're specifying the name of the node
module to start with (my_module_2) and the name of the executable
within my_module_2 to run (baz). In this case the node_module
rule definition for my_module_2 must have a string_dict with an
entry for baz (like executables = { 'baz': 'bin/baz' }.
A node_binary rule named foo will create a folder having exactly
two entries:
- An executable shell script named
foo. - A folder which bundles up all the needed files in
foo_files/.
Within foo_files/, there will also be exactly two entries:
- The
nodeexecutable itself. - The
node_modules/folder with all the built/copied modules (including the entrypoint module).
To generate a tarred/gzipped archive of the above example that you can
ship as a single 'executable' self-contained package, invoke $ bazel build :{target}_deploy.tar.gz. This is similar in intent to the java
{target}_deploy.jar implicit build rule.
$ bazel build :foo_deploy
Target //:foo_deploy.tar.gz up-to-date:
bazel-bin/foo_bundle.tgz
$ du -h bazel-bin/foo_bundle.tgz
33M bazel-bin/foo_bundle.tgzThe node_test rule is identical to node_binary, but sets the test = True flag such that it can be used as a bazel test.
Runs a mocha test identified by the start script given in main or
module given in entrypoint.
Note: The mocha_test rule depends on
@mocha_modules//:_all_, so you'll need to add this dependency in yourWORKSPACEfile:
yarn_modules(
name = "mocha_modules",
deps = {
"mocha": "3.5.3",
}
)mocha_test(
name = "test",
main = "test.js",
)That's it! Please refer to the various workspaces in tests/ and the
source for more detail.
