Skip to main content
rbs builds Node.js and TypeScript projects without requiring Node, npm, or yarn on your machine. The workspace declares which Node.js version it uses, rbs downloads that exact toolchain, and every target builds and runs against it — the same way on every developer’s machine and in CI. Three rules cover most Node.js work: For browser apps (React, bundling, dev servers), see Web Apps. Component testing with vitest_test is covered at the end of this page.

Set up the toolchain

Declare the Node.js toolchain once in your workspace’s WORKSPACE.rbs:
rbs downloads the official Node.js release for your platform on first use and caches it. Supported versions include 22.15.1 (default), 22.11.0, 20.19.6, 20.11.0, and 18.19.0.

Declare npm dependencies

rbs resolves npm packages itself, directly from the npm registry — there is no package.json, no npm install, and no npm/yarn CLI involved. Each package your workspace uses is declared in WORKSPACE.rbs:
Transitive dependencies are resolved automatically, exactly like npm would: rbs walks the dependency tree, resolves semver ranges, and downloads everything in parallel into a shared cache. version is usually an exact pin, but semver ranges ("^4.18.0") and "latest" also work — exact pins are recommended for reproducibility. To declare several packages at once:

The lockfile

The first resolution writes rbs.lock at the workspace root, pinning every resolved package (including transitives). Later builds resolve instantly from the lockfile instead of hitting the registry.
Commit rbs.lock to version control. It makes dependency resolution reproducible and fast for everyone on the branch.

Referencing dependencies from targets

In a BUILD.rbs file, a target’s deps can reference npm packages two ways:
Both forms resolve to the same cached package. The @external://package:version form is explicit about the version at the point of use; the :name_repo form points back at the nodejs_repository declaration.
When a target runs, your code sees an ordinary node_modules directory and imports resolve exactly as in any Node.js project — rbs stages and shares the packages behind the scenes.

nodejs_binary

Builds a runnable Node.js program with a hermetic launcher: the target carries its own Node.js toolchain and dependencies, so rbs run works on any machine with no setup.

Attributes

nodejs_library

Groups sources into a reusable unit. Libraries record their own npm dependencies, and any binary or test that depends on the library gets those packages automatically.
Code in a consuming target imports library files by the library’s target name followed by the file’s path within the library:

Attributes

TypeScript

TypeScript works out of the box in all three rules: when srcs contain .ts or .tsx files, rbs compiles them with tsc before staging. The only requirement is that the workspace declares the typescript package as a host dependency:
The default compiler options target ES2020 with CommonJS modules, strict mode, and esModuleInterop enabled. Override individual options per target:
Or point the target at an existing config with tsconfig = "tsconfig.json".
rbs also generates a TypeScript configuration for your editor (under .rbs/lsp/) so imports of npm packages and workspace libraries resolve in the IDE without any manual setup. This file is regenerated by builds — treat it as read-only. If you need different compiler behavior, set ts_compiler_options or tsconfig on the target rather than editing generated files.

Testing with nodejs_test

nodejs_test runs your tests with one of two runners:
  • jest (default) — real Jest: describe/it/expect suites, with Jest-collected coverage.
  • node — plain Node.js: the test file is a self-executing script (e.g. using assert) that exits non-zero on failure; coverage is collected with NYC (Istanbul).
First, install the test dependencies once in WORKSPACE.rbs:
Then declare test targets:
With test_runner = "node", name your test files *.test.js so the coverage tool excludes them from the coverage report.

Coverage gates

Set minimum coverage thresholds on the target and enforce them with rbs coverage — the run fails if any metric drops below its threshold:
Coverage is real, measured lcov data — local library dependencies of the test are instrumented too, so the numbers reflect everything the tests actually exercise.

Attributes

Component testing with vitest_test

For React/DOM component tests, use vitest_test from the web-vite ruleset. It runs real Vitest with a jsdom environment by default, transforms TS/TSX with Vitest’s own pipeline (no separate compile step), and collects V8 lcov coverage. Install its dependencies once in WORKSPACE.rbs:
Then declare test targets — srcs must list both the test files (named *.test.* or *.spec.*) and the sources they exercise:
@testing-library/react v16 requires @testing-library/dom as a peer dependency — list it explicitly in deps, as above.

Attributes

To test components that import a shared workspace library, declare the library under the same import scope the app rules use:
The tests can then import { Button } from '@acme/ui' just like the application code does. See Web Apps for how libraries and apps are wired together.