Skip to content

Commit f990afc

Browse files
authored
Refactor node_binary rule. (#41)
* Improved support for various execution contexts. * Move node_modules tree creation into own rule. * parse yarn modules: create a node_binary rules foreach executable target in the module. * Redo mocha_test as an sh_test. * Add the node_test rule.
1 parent dc9f8ba commit f990afc

37 files changed

+1233
-505
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ os:
88
- osx
99

1010
env:
11+
- V=0.7.0
12+
- V=0.6.1
1113
- V=0.5.4
1214
- V=0.5.3
13-
# Not compatible under 5.3
1415

1516
before_install:
1617
- OS=linux

Makefile

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1-
test_all:
2-
(cd tests/helloworld && bazel test //:helloworld_test)
3-
(cd tests/lyrics && bazel test //:lyrics_test)
4-
(cd tests/express && bazel test //:server_test)
5-
(cd tests/namespace && bazel test //:question_test)
6-
(cd tests/typescript && bazel test //:typescript_test)
7-
(cd tests/mocha && bazel test //:test)
8-
(cd tests/mocha && bazel test //tests:test)
1+
test_helloworld:
2+
(cd tests/helloworld && bazel test //...)
3+
4+
test_lyrics:
5+
(cd tests/lyrics && bazel test //...)
6+
7+
test_express:
8+
(cd tests/express && bazel test //...)
9+
10+
test_namespace:
11+
(cd tests/namespace && bazel test //...)
12+
13+
test_typescript:
14+
(cd tests/typescript && bazel test //...)
15+
16+
test_webpack:
17+
(cd tests/webpack && bazel test //...)
18+
19+
test_mocha:
20+
(cd tests/mocha && bazel test //...)
21+
22+
test_all: test_helloworld test_lyrics test_express test_namespace test_typescript test_webpack test_mocha

README.md

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
| Rule | Description |
1616
| ---: | :---------- |
1717
| [node_repositories](#node_repositories) | Install node toolchain. |
18-
| [yarn_modules](#yarn_modules) | Install a set node_modules dependencies using yarn. |
19-
| [node_module](#node_module) | Define a node module from a set of source files and a main (or index) source file. |
20-
| [node_binary](#node_binary) | Build a node_modules tree and execute an entrypoint module script. |
21-
| [mocha_test](#mocha_test) | Run a mocha test script. |
18+
| [yarn_modules](#yarn_modules) | Install a set node module dependencies using yarn. |
19+
| [node_module](#node_module) | Define a node module from a set of source files (having an optional main (or index) entry point). |
20+
| [node_binary](#node_binary) | Run a node module. |
21+
| [node_test](#node_test) | Run a node binary as a bazel test. |
22+
| [mocha_test](#mocha_test) | Run a mocha test script. |
2223

2324
<table><tr>
2425
<td><img src="https://www.kernel.org/theme/images/logos/tux.png" height="48"/></td>
@@ -84,13 +85,14 @@ populate it with the necessary dependencies.
8485
3. Read the generated `yarn.lock` file, parse it, and write out a
8586
`@yarn_modules//:BUILD` file. This file contains a `node_module`
8687
rule foreach entry in the `yarn.lock` file, a `node_module` rule
87-
with the special name `_all_`, and an `sh_binary` rule foreach
88+
with the special name `_all_`, and a `node_binary` rule foreach
8889
executable script in the `node_modules/.bin` folder.
8990

9091
> Note 1: You can inspect all the targets by running `bazel query @yarn_modules//:*`.
9192
9293
> Note 2: The workspace name `yarn_modules` is arbitrary, choose
93-
whatever you like *other than* `node_modules` (that one doesn't work).
94+
whatever you like (*other than* `node_modules` itself, that one
95+
doesn't work).
9496

9597
At this point you can use these rule targets as `deps` for your
9698
`node_module` rules. *Example*:
@@ -152,7 +154,7 @@ node_modules/fs-extra
152154
When used by other `node_module` rules, you can import the module as:
153155

154156
```javascript
155-
const myModule = require("my-module");
157+
const myModule = require("my_module");
156158
```
157159

158160
There are three basic ways to create a `node_module` rule:
@@ -249,6 +251,7 @@ These are only relevant if you don't explicitly name a `package.json` file.
249251
| optional | `string` | `url` | `None` | Url where the module tgz archive was resolved
250252
| optional | `string` | `sha1` | `None` | Sha1 hash of of the resolved tgz archive
251253
| optional | `string` | `description` | `None` | Module description
254+
| optional | `string_dict` | `executables` | `None` | A mapping from binary name to internal node module path. Example `executables = { 'foo': 'bin/foo' }`.
252255

253256
### node_module attributes that affect the relative path of files included in the module
254257

@@ -267,9 +270,7 @@ the workspace, which needs to be preserved in the generated module.
267270

268271
## node_binary
269272

270-
The `node_binary` rule builds a `node_modules/` tree based on its
271-
`node_module` dependencies and writes a script to execute a module
272-
entrypoint.
273+
The `node_binary` rule writes a script to execute a module entrypoint.
273274

274275
```python
275276
load("@org_pubref_rules_node//node:rules.bzl", "node_binary")
@@ -299,38 +300,55 @@ the entrypoint (under the hood, it will just build a `node_module`
299300
becoming equivalent to the first example).
300301

301302

303+
```python
304+
node_binary(
305+
name = "foo",
306+
entrypoint = ":my_module_2",
307+
executable = "baz",
308+
)
309+
```
310+
311+
In this third example (above), we're specifying the name of the node
312+
module to start with (`my_module_2`) and the name of the executable
313+
within `my_module_2` to run (`baz`). In this case the `node_module`
314+
rule definition for `my_module_2` must have a `string_dict` with an
315+
entry for `baz` (like `executables = { 'baz': 'bin/baz' }`.
316+
302317
### Output structure of files generated for a `node_binary` rule
303318

304-
A `node_binary` rule named `foo` will create a folder having exactly two entries:
319+
A `node_binary` rule named `foo` will create a folder having exactly
320+
two entries:
305321

306322
1. An executable shell script named `foo`.
307-
1. A folder which bundles up all the needed files in `foo_bundle/`.
323+
1. A folder which bundles up all the needed files in `foo_files/`.
308324

309-
Within `foo_bundle/`, there will also be exactly two entries:
325+
Within `foo_files/`, there will also be exactly two entries:
310326

311327
1. The `node` executable itself.
312-
1. The `node_modules/` folder with all the built/copied modules.
313-
314-
The bash shell script `foo` performs the following:
315-
316-
`cd $(dirname $0)/foo_bundle && exec node node_modules/entrypoint`
328+
1. The `node_modules/` folder with all the built/copied modules
329+
(including the entrypoint module).
317330

318331

319332
### Building a deployable bundle
320333

321-
To generate a tarred gzipped archive of the above example that you can
322-
ship as a single 'executable' file, invoke `$ bazel build
323-
:{target}_bundle.tgz`. This is similar in intent to the java
334+
To generate a tarred/gzipped archive of the above example that you can
335+
ship as a single 'executable' self-contained package, invoke `$ bazel
336+
build :{target}_deploy.tar.gz`. This is similar in intent to the java
324337
`{target}_deploy.jar` implicit build rule.
325338

326339
```sh
327-
$ bazel build :foo_bundle.tgz
328-
Target //:foo_bundle.tgz up-to-date:
340+
$ bazel build :foo_deploy
341+
Target //:foo_deploy.tar.gz up-to-date:
329342
bazel-bin/foo_bundle.tgz
330343
$ du -h bazel-bin/foo_bundle.tgz
331344
33M bazel-bin/foo_bundle.tgz
332345
```
333346

347+
## node_test
348+
349+
The `node_test` rule is identical to node_binary, but sets the `test =
350+
True` flag such that it can be used as a bazel test.
351+
334352
## mocha_test
335353

336354
Runs a mocha test identified by the start script given in `main` or
@@ -353,13 +371,9 @@ mocha_test(
353371
name = "test",
354372
main = "test.js",
355373
)
356-
357-
mocha_test(
358-
name = "test",
359-
entrypoint = ":my_module",
360-
)
361374
```
362375

363376
## Conclusion
364377

365-
That's it! Please refer to the various workspaces in `tests/` and the source for more detail.
378+
That's it! Please refer to the various workspaces in `tests/` and the
379+
source for more detail.

WORKSPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ workspace(name = "org_pubref_rules_node")
33
load("//node:rules.bzl", "node_repositories")
44

55
node_repositories()
6+

node/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
package(default_visibility = ["//visibility:public"])
2+
3+
exports_files([
4+
"internal/mocha_test.sh",
5+
])

node/internal/mocha_test.bzl

Lines changed: 48 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,56 @@
11
load("//node:internal/node_module.bzl", "node_module")
2-
load("//node:internal/node_binary.bzl", "copy_modules", "binary_attrs")
3-
4-
5-
def _create_launcher(ctx, output_dir, node, mocha):
6-
entry_module = ctx.attr.entrypoint.node_module
7-
# if package is under root
8-
entrypoint = '%s_test/node_modules/%s' % (ctx.label.name, entry_module.name)
9-
# if test is under inner package
10-
if ctx.label.package:
11-
entrypoint = '%s/%s' % (ctx.label.package, entrypoint)
12-
cmd = [
13-
node.short_path,
14-
] + ctx.attr.node_args + [
15-
mocha.short_path,
16-
] + ctx.attr.mocha_args + [
17-
entrypoint,
18-
] + ctx.attr.script_args + [
19-
'$@',
20-
]
21-
22-
lines = [
23-
'#!/usr/bin/env bash',
24-
'set -e',
25-
' '.join(cmd)
26-
]
27-
ctx.file_action(
28-
output = ctx.outputs.executable,
29-
executable = True,
30-
content = '\n'.join(lines),
2+
load("//node:internal/node_modules.bzl", "node_modules")
3+
4+
def mocha_test(
5+
name = None,
6+
# Main test script entrypoint
7+
main = None,
8+
# Additional module deps for the test
9+
deps = [],
10+
# The mocha binary executable target
11+
mocha_bin = "@mocha_modules//:mocha_bin",
12+
# The script runner for the sh_test
13+
script = "@org_pubref_rules_node//node:internal/mocha_test.sh",
14+
# Any additional args to pass directly to mocha
15+
args = [],
16+
# Test size
17+
size = "small",
18+
# Test visibility
19+
visibility = None,
20+
# Remainder of args go to 'node_module'
21+
**kwargs):
22+
23+
"""Given a rule name and a main test script entrypoint file, package
24+
that test script up as a module, then package that module in a
25+
node_modules tree. Run a bash script that invokes the mocha_bin
26+
executable with the name of the testable entrypoint module.
27+
28+
"""
29+
30+
node_module(
31+
name = name + "_module",
32+
main = main,
33+
visibility = visibility,
34+
**kwargs
3135
)
3236

33-
34-
def mocha_test_impl(ctx):
35-
output_dir = ctx.label.name + '_test'
36-
node = ctx.executable._node
37-
mocha = ctx.executable._mocha_bin
38-
39-
all_deps = ctx.attr.deps + [ctx.attr.entrypoint]
40-
files = copy_modules(ctx, output_dir, all_deps)
41-
42-
_create_launcher(ctx, output_dir, node, mocha)
43-
44-
mocha_deps_all = ctx.attr._mocha_deps.node_module
45-
transitive_mocha_files = mocha_deps_all.files.to_list()
46-
for dep in mocha_deps_all.transitive_deps:
47-
transitive_mocha_files += dep.files.to_list()
48-
49-
runfiles = [
50-
node,
51-
mocha,
52-
ctx.outputs.executable
53-
] + transitive_mocha_files + files
54-
55-
return struct(
56-
runfiles = ctx.runfiles(
57-
files = runfiles,
58-
collect_data = True,
59-
),
37+
node_modules(
38+
name = name + "_modules",
39+
target = name + "_modules",
40+
visibility = visibility,
41+
deps = deps + [name + "_module"],
6042
)
61-
62-
63-
_mocha_test = rule(
64-
mocha_test_impl,
65-
attrs = binary_attrs + {
66-
"_mocha_bin": attr.label(
67-
default = Label("@mocha_modules//:mocha_bin"),
68-
allow_files = True,
69-
executable = True,
70-
cfg = "host",
71-
),
72-
"_mocha_deps": attr.label(
73-
providers = ["node_module"],
74-
default = Label("@mocha_modules//:_all_"),
75-
),
76-
"mocha_args": attr.string_list(),
77-
},
78-
test = True,
79-
)
80-
81-
82-
def mocha_test(name = None, main = None, entrypoint = None, node_args = [], mocha_args = [], deps = [], visibility = None, size = "small", **kwargs):
83-
84-
if not entrypoint:
85-
if not main:
86-
fail('Either an entrypoint node_module or a main script file must be specified')
87-
entrypoint = name + '_module'
88-
node_module(
89-
name = entrypoint,
90-
main = main,
91-
deps = [],
92-
visibility = visibility,
93-
**kwargs
94-
)
95-
96-
_mocha_test(
43+
44+
native.sh_test(
9745
name = name,
98-
entrypoint = entrypoint,
99-
deps = deps,
46+
srcs = [script],
47+
args = args + [
48+
"{name}_modules/node_modules/{name}_module".format(name = name),
49+
],
50+
data = [
51+
mocha_bin,
52+
name + "_modules",
53+
],
10054
size = size,
101-
node_args = node_args,
102-
mocha_args = mocha_args,
10355
visibility = visibility,
10456
)

node/internal/mocha_test.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
set -eu
2+
external/mocha_modules/mocha_bin $@

0 commit comments

Comments
 (0)