JavaScript reference
The target types, configuration properties, and flags contributed by the @fabr-build/js plugin
(plugin @fabr-build/js;). They cover building, bundling, testing, and styling JavaScript and
TypeScript. Core definitions (like script and generate) are in the Core reference.
This page is generated from fabr list-all, so it never drifts from the code.
Configuration
Section titled “Configuration”CSS_COMPILER
Section titled “CSS_COMPILER”Default: @fabr-build/js-tools/css-driver
The CSS compiler css_compile runs (fabr’s driver: Sass lowering, driven by
the rule’s manifest).
ESBUILD
Section titled “ESBUILD”Default: @npm:esbuild:0.28.1
The esbuild library the bundle driver requires for JS bundling.
Default: @npm:jest:30.3.0
The jest release the jest-compatibility runner takes its libraries from. Pinned like any tool (TSC, SASS, …) — fabr never hardcodes a tool version; override this one default to move. Jest 29/30 are supported; the runner checks at startup rather than failing obscurely later.
JEST_TYPE_PINS
Section titled “JEST_TYPE_PINS”Default: @npm:@types/node:20.12.7 @npm:@types/babel__generator:7.27.0 @npm:@types/babel__template:7.4.4 @npm:@types/babel__traverse:7.28.0 @npm:@types/istanbul-lib-report:3.0.3 @npm:@types/yargs-parser:21.0.3
The @types/* packages inside jest’s closure that are required WITHOUT a
version lower bound (‘*’, the DefinitelyTyped house style), so nothing in the
closure picks a version for them. Fabr does not invent one — a floorless
requirement is satisfied by whatever the floored ones select, and here nothing
does — so they are named here. They are compile-time type declarations that
nothing in a test process ever loads; the versions matter only in that they
must exist. Overridable alongside JEST if a different jest release needs
different ones.
JS_BUNDLER
Section titled “JS_BUNDLER”Default: @fabr-build/js-tools/bundle-driver
The bundler js_bundle runs (fabr’s esbuild driver: reads the rule’s
options manifest, maps it onto esbuild).
JS_TARGET
Section titled “JS_TARGET”Default: es6-esm
The default target for JS output.
This is constructed as a pair of
JS_TEST_RUNNER
Section titled “JS_TEST_RUNNER”Default: @fabr-build/js-tools/test-runner
The test runner the js test rules run (fabr’s own by default). A target
overrides it for itself with its test_runner property; set this to switch a
whole project (e.g. to the jest-compatibility runner below).
NPM_REPOSITORY_URL
Section titled “NPM_REPOSITORY_URL”Default: https://registry.npmjs.org/
Primary URL for the remote NPM repository. You’ll usually want to override this if you’re using a private mirror/cache.
Default: @npm:sass-embedded:1.100.0
The Sass compiler the css driver requires. sass-embedded by default.
Default: @npm:@swc/core:1.15.43
The transform the jest runner hoists jest.mock calls with. swc rather than
jest’s own babel plugin on two measured grounds: the babel plugin also
VALIDATES a mock factory’s free variables and rejects correct code that other
jest transformers accept (18 of 40 files in a real suite), and a test file
gets its own process, so the transform’s first-call cost is paid per file and
never amortised — ~124ms with babel against ~8.5ms with swc. Native, so it
rides the same os/cpu platform gating as esbuild and lightningcss.
Default: @npm:typescript:5.4.5:tsc
The default typescript compiler to use (if compiling typescript). You’ll usually want to override this to set a specific version. (Note these must be pinned versions or ranges; mutable dist-tags like ‘latest’ are not supported as they make the build non-deterministic.)
List a flag in a target’s deps to switch its behaviour for that target.
Whether the sources require the DOM runtime.
es2015
Section titled “es2015”Alias for es2015
es2016
Section titled “es2016”es2017
Section titled “es2017”es2018
Section titled “es2018”es2019
Section titled “es2019”es2020
Section titled “es2020”es2021
Section titled “es2021”es2022
Section titled “es2022”es2023
Section titled “es2023”The ECMAScript level a target’s SOURCES are written against — what APIs the
code may use — as opposed to JS_TARGET’s level, which is what gets emitted
and which runtime the output must run on. List one among a target’s deps:
js_package mylib { srcs = src:**/*.ts; deps = es2023; }It sets the compile’s lib (so using an API above the declared level is an
error, not a silent success), and — because useDefineForClassFields turns
on at emit target ES2022 and changes what class fields DO — sources declared
below es2022 keep assignment semantics even when emitted for a later target.
Declaring none leaves the level following JS_TARGET, as before.
A package built from es2023 sources genuinely requires an es2023 runtime, so the flag rides its dependency list outward like any other runtime dependency.
esnext
Section titled “esnext”ts/allow_implicit_any
Section titled “ts/allow_implicit_any”Allow implicit any (noImplicitAny: false) for this target’s sources.
ts/allow_implicit_this
Section titled “ts/allow_implicit_this”Allow an implicitly-any this (noImplicitThis: false): for sources using
dynamic-this callbacks.
ts/emit_decorator_metadata
Section titled “ts/emit_decorator_metadata”Additionally emit design-time type metadata for those decorators — the shape a reflect-metadata DI container reads. Note it retains type-only imports as runtime ones. Implies ts/experimental_decorators, which TypeScript requires.
ts/experimental_decorators
Section titled “ts/experimental_decorators”Compile decorators with the legacy semantics TypeScript used before 5.0
(experimentalDecorators: true). Class fields keep assignment semantics with
it: a real field would shadow the prototype accessor a property decorator
installs.
ts/no_es_module_interop
Section titled “ts/no_es_module_interop”Compile with classic CJS interop (esModuleInterop: false): for sources written
with import * as x of callable CJS modules rather than default imports.
ts/no_strict
Section titled “ts/no_strict”Turn TypeScript strict mode off wholesale for this target’s sources.
ts/no_strict_bind_call_apply
Section titled “ts/no_strict_bind_call_apply”Leave bind/call/apply argument types unchecked
(strictBindCallApply: false), as TypeScript did before 3.2.
ts/no_strict_function_types
Section titled “ts/no_strict_function_types”Compare function parameter types bivariantly (strictFunctionTypes: false),
as TypeScript did before 2.6.
ts/no_strict_null_checks
Section titled “ts/no_strict_null_checks”Treat null and undefined as members of every type (strictNullChecks: false),
as TypeScript did before 2.0.
ts/no_strict_property_initialization
Section titled “ts/no_strict_property_initialization”Allow class properties with no definite assignment
(strictPropertyInitialization: false).
ts/no_use_unknown_in_catch_variables
Section titled “ts/no_use_unknown_in_catch_variables”Type a caught error as any rather than unknown
(useUnknownInCatchVariables: false): for sources written before TypeScript 4.4.
Targets
Section titled “Targets”js_package
Section titled “js_package”Operations: build, run, test
targetdef js_package { srcs = FILES; deps = FILES; provided_deps = FILES; resources = FILES; tests = FILES; test_deps = FILES; test_expectations = FILES; test_resources = FILES; test_runner = FILES; version = STRING; metadata = MAP;}A standard JavaScript/TypeScript npm package: compile a source tree and produce
an installable package (with a generated package.json). srcs are the sources
to compile; deps are its dependencies (packages, or plain-source siblings);
tests are compiled and run under fabr test. It builds a package for build,
runs its bin for run, and runs its tests for test.
| Property | Type | Required | Description |
|---|---|---|---|
srcs |
FILES | ||
deps |
FILES | ||
provided_deps |
FILES | Host-provided (peer) dependencies: compiled and resolved against exactly like ‘deps’, but the host supplies the single shared copy at runtime, so they are emitted as peerDependencies (not dependencies) in the generated package.json rather than bundled. Used by a plugin for @fabr-build/core. | |
resources |
FILES | Files that ship as package content WITHOUT being compiled, including already-built JavaScript (note there are definitionally no checks on the included files.) | |
tests |
FILES | Test source files, plus any supporting files (mocks, snapshots) May contain a setupTests.ts/setupTests.js; if present it will run before every test. | |
test_deps |
FILES | Extra packages for compiling and running the tests only (assertion libraries and their @types); never part of the package’s own dependencies. The describe/it globals are provided (and typed) by the test runner. A plain SOURCE here is test support: compiled with the test types in scope, never run as a test, never shipped (and kept out of the package’s own compile). | |
test_expectations |
FILES | Recorded expectations the tests compare against — jest’s __snapshots__/*.snap, or any equivalent a runner reads. |
|
test_resources |
FILES | Files the tests need at runtime, staged verbatim: neither compiled nor shipped as package content. | |
test_runner |
FILES | The runner fabr test runs these tests under; defaults to the JS_TEST_RUNNER global. Any runnable honoring the runner contract will do — in practice, fabr’s own runner or one of its compatibility flavours (see the jest runner in the driver-tools section). |
|
version |
STRING | ||
metadata |
MAP | Descriptive package.json fields (description, license, author, homepage, …), overlaid onto the generated manifest. A value is a string, a nested block (an object: repository = { type = git; url = …; };), or a list of blocks (an array of objects: maintainers = { name = a; } { name = b; };). Reuse a shared map by bare reference (metadata = COMMON;) or splice it into a block and override per package, later entries winning: metadata = { COMMON; description = This package; }; A key fabr computes itself (name, version, type, main, types, bin, dependencies, devDependencies, peerDependencies) is rejected rather than silently overridden. |
js_bundle
Section titled “js_bundle”Operations: build
targetdef js_bundle { srcs = FILES; entry = REQUIRED FILES; deps = FILES; output = REWRITE; defines = MAP;}Bundle a JavaScript/TypeScript source tree into a self-contained artifact.
srcs is the extra sources allowed INTO the bundle (plain files land at the
working root; packages mount under node_modules and are inlined); entry is
the source(s) to bundle (a glob gives N independent bundles) — entry files are
included automatically, so they need not also be listed in srcs; deps
is what the RUNTIME provides — externalized by package identity, so their
imports survive verbatim (the lambda case: handler + vendored libs in srcs,
@aws-sdk/* in deps). output optionally renames each entry’s default output
(identity modulo .ts/.tsx -> .js), e.g. *.entry.js -> *.min.js. defines
are compile-time constant substitutions (esbuild define): each value is code
text verbatim, so a string constant is shell-quoted like esbuild’s own CLI
(process.env.NODE_ENV = '"production"';) while DEBUG = false; stays a bare
literal. Minify and sourcemaps follow BUILD_TYPE; platform/format follow JS_TARGET.
js_test
Section titled “js_test”Operations: test
targetdef js_test { tests = FILES; deps = FILES; test_runner = FILES; resources = FILES; expectations = FILES;}Standalone JavaScript test target: tests are the test files, compiled and
run under fabr’s own test runner; deps are everything they need but that
isn’t itself a test — packages, and plain-source support (e.g. a test harness,
which compiles as a sibling so ./harness resolves, but is never run).
| Property | Type | Required | Description |
|---|---|---|---|
tests |
FILES | Test source files, plus any supporting files (mocks, snapshots) May contain a setupTests.ts/setupTests.js; if present it will run before every test. | |
deps |
FILES | ||
test_runner |
FILES | The runner these tests run under; defaults to the JS_TEST_RUNNER global. | |
resources |
FILES | Files the tests need at runtime, staged verbatim (see js_package’s test_resources — unprefixed here, since everything in a js_test is tests). | |
expectations |
FILES | Recorded expectations the tests compare against (see js_package). |
js_script
Section titled “js_script”Operations: run
targetdef js_script { deps = FILES; entry = REQUIRED FILES; args = STRING;}Define a runnable JavaScript program (run it with fabr run, collect its
output with the generic generate target, or check it with a golden test).
entry is either a plain script FILE (contributed to the install at its
resolved name and launched under node), or a PACKAGE — external or built —
that becomes the runnable via the usual npm rules (entry = @npm:astro:7.1.1;
— its sole declared bin is the default entry, and a projection selects among
several — entry = @npm:typescript:5.4.5:tsc — exactly as under fabr run);
package-mode exists to decorate a packaged tool with additional dependencies:
entry and deps resolve jointly at one collection point, so the whole
environment shares one version selection and mounts into one node_modules
(loose dep filesets land at their own paths). args are fixed leading
arguments. It yields a runnable, it does not itself execute.
npm_repository
Section titled “npm_repository”targetdef npm_repository { url = REQUIRED STRING; access = STRING;}npmjs.org compatible repository, specified by URL. access is the access
level packages published here request on their first publish: public, or
private (npmjs’s paid private packages; npm’s wire term restricted is
accepted as a synonym).
js_compile
Section titled “js_compile”Operations: *
targetdef js_compile { srcs = FILES; deps = FILES; package_name = STRING;}Compile a JavaScript/TypeScript source tree to plain JS — the one TS-compile
path. srcs are the sources (.ts/.tsx/.js/.jsx, plus ambient .d.ts);
deps are the packages the sources are compiled against (mounted as
node_modules). Composed automatically by js_package/js_test/
js_script; also declarable directly.
| Property | Type | Required | Description |
|---|---|---|---|
srcs |
FILES | ||
deps |
FILES | ||
package_name |
STRING | The package name these sources are importable as, which enables source that imports its own package by name to work correctly. |
js_test_run
Section titled “js_test_run”Operations: *
targetdef js_test_run { staged = FILES; writable = FILES; argv = REQUIRED STRING; test_files = REQUIRED STRING; outputs = REQUIRED STRING;}One execution of a test runner over a staged installation — the cache unit of
a test run. Composed automatically by js_package/js_test (which assemble
the installation and the invocation); there is nothing useful to declare by
hand. staged is the complete installation, argv the runner invocation, and
outputs the selectors for what the run leaves behind (the report, plus any
refreshed expectation files under TEST_EXPECTATIONS=update).
| Property | Type | Required | Description |
|---|---|---|---|
staged |
FILES | ||
writable |
FILES | The subset of staged the runner is allowed to rewrite (the recorded expectations, under TEST_EXPECTATIONS=update): staged as writable copies rather than links into the content store. |
|
argv |
REQUIRED STRING | yes | The runner invocation, without report or files: the step invokes it once per test_files entry (each execution admitted separately by the process funnel), appending a per-invocation report name and the file. |
test_files |
REQUIRED STRING | yes | |
outputs |
REQUIRED STRING | yes |
css_compile
Section titled “css_compile”Operations: build
targetdef css_compile { srcs = FILES; deps = FILES;}Lower a styled source tree to plain CSS. srcs are the styled sources
(.scss/.sass, css-modules .module.{scss,css}, plain .css); deps are
scss packages mounted for Sass @use/@import resolution (the Sass loadPaths
analogue of node_modules). Lowering is all it does: each source becomes the
same name with a .css extension, css-modules included (x.module.scss →
x.module.css, left for the bundler to scope). Sass partials (_foo.scss) are
skipped, per Sass’s own directory-compile convention. Composed automatically by
js_bundle for any styled sources; also declarable directly. Uses SASS.
Provided targets
Section titled “Provided targets”Ready-made targets declared by these libraries, usable by name in any project.
npm_repository @npm { url = ${NPM_REPOSITORY_URL}; access = public;}Convenience provider for the public npmjs.com repository. Publishing here
publishes publicly — this is the public registry, To publish restricted-
access/private packages, declare a separate repository instance
with access = private;.
@fabr-build/js-tools/bundle-driver
Section titled “@fabr-build/js-tools/bundle-driver”js_script @fabr-build/js-tools/bundle-driver { entry = ../bundleDriver/bundle-driver.js; deps = ${ESBUILD};}The bundler driver js_bundle runs (the default JS_BUNDLER): reads the
rule’s options manifest and maps it onto esbuild. Like all the driver tools
here, an ordinary runnable target over compiled entries shipped in this
installation — a replacement honoring the same manifest contract can be
swapped in by overriding the global.
@fabr-build/js-tools/css-driver
Section titled “@fabr-build/js-tools/css-driver”js_script @fabr-build/js-tools/css-driver { entry = ../cssDriver/css-driver.js; deps = ${SASS};}The CSS compiler css_compile runs (the default CSS_COMPILER): Sass
lowering, driven by the rule’s options manifest.
@fabr-build/js-tools/test-runner
Section titled “@fabr-build/js-tools/test-runner”js_script @fabr-build/js-tools/test-runner { entry = ../testRunner/runner.js; deps = ../testRunner/Report.js ../testRunner/globals.js ../testRunner/RunTests.js ../testRunner/test-globals.d.ts;}Fabr’s own test runner (‘fabr test’ / js_test / js_package tests): wraps
node:test, preloads the describe/it globals, and writes the CTRF report. Its
test-globals.d.ts rides in the install — the test rules mount it into the
test compile as ambient types, so a swapped runner carries its own globals
typings. A replacement must honor the runner contract: invoked in the staged
test install as <runner> --report=<file> <test files...>, exit status red
on failure, report written as CTRF.
@fabr-build/js-tools/jest-runner
Section titled “@fabr-build/js-tools/jest-runner”js_script @fabr-build/js-tools/jest-runner { entry = ../jestRunner/runner.js; deps = ${JEST} ${SWC} JEST_TYPE_PINS ../jestRunner/Assets.js ../jestRunner/child.js ../jestRunner/Config.js ../jestRunner/Environment.js ../jestRunner/Failures.js ../jestRunner/Hoist.js ../jestRunner/JestObject.js ../jestRunner/Registry.js ../jestRunner/RuntimeFacade.js ../jestRunner/SnapshotResolver.js ../jestRunner/Tools.js ../testRunner/Report.js ../testRunner/RunTests.js;}Fabr’s test runner with a JEST COMPATIBILITY LAYER: it drives JEST-CIRCUS
itself. Circus owns the framework (describe/it, hooks, .each/.only/.concurrent,
per-test timeouts, expect with its snapshot state and assertion counting);
fabr owns the module system (node’s real loader, the mock registry, the
load-time hoist, the jest object). Neither jest’s orchestrator nor
jest-runtime runs: circus is handed a facade over the eleven methods it
actually calls, so modules load through node and require(esm) keeps working
— the capability jest’s own vm-based loader lacks.
Select it per target (test_runner = @fabr-build/js-tools/jest-runner;) or
project-wide (JS_TEST_RUNNER = @fabr-build/js-tools/jest-runner;). A target
using it should declare @npm:@types/jest among its test_deps for the
describe/it/expect/jest globals’ types — this runner deliberately ships no
ambient globals of its own, which would collide with them.
A test file that imports describe/it from node:test DIRECTLY is invisible to
it — those registrations go to node:test, which this runner does not drive —
so a file that registers no tests is reported as a failure rather than
silently passing. Use the globals, or @jest/globals.