Skip to content

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.

Default: @fabr-build/js-tools/css-driver

The CSS compiler css_compile runs (fabr’s driver: Sass lowering, driven by the rule’s manifest).

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.

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.

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).

Default: es6-esm

The default target for JS output. This is constructed as a pair of -

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).

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.

Alias for es2015

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.

Allow implicit any (noImplicitAny: false) for this target’s sources.

Allow an implicitly-any this (noImplicitThis: false): for sources using dynamic-this callbacks.

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.

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.

Compile with classic CJS interop (esModuleInterop: false): for sources written with import * as x of callable CJS modules rather than default imports.

Turn TypeScript strict mode off wholesale for this target’s sources.

Leave bind/call/apply argument types unchecked (strictBindCallApply: false), as TypeScript did before 3.2.

Compare function parameter types bivariantly (strictFunctionTypes: false), as TypeScript did before 2.6.

Treat null and undefined as members of every type (strictNullChecks: false), as TypeScript did before 2.0.

Allow class properties with no definite assignment (strictPropertyInitialization: false).

Type a caught error as any rather than unknown (useUnknownInCatchVariables: false): for sources written before TypeScript 4.4.

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.

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.

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).

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.

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).

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.

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

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.scssx.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.

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;.

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.

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.

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.

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.