Skip to content

JavaScript targets and configuration

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 Core targets and configuration.

This page is generated from fabr list-all, so it never drifts from the code.

Property Default Description
AUTOPREFIXER @npm:autoprefixer:10.6.2 @npm:browserslist:4.29.5 The PostCSS plugin that adds the vendor prefixes BROWSER_TARGET’s browsers need.
BROWSER_DATA @npm:caniuse-lite:1.0.30001820 @npm:baseline-browser-mapping:2.11.34 The browser data: Can I Use’s browser releases, which @browser and autoprefixer read, and Baseline’s browser versions by year.
BROWSER_TARGET @browser:baseline:2023 The browsers the build’s output must run in: @browser references, each the lowest version supported (@browser:baseline:2023, @browser:safari:16.4). Stylesheets are prefixed for them.
ESBUILD @npm:esbuild:0.28.1 The esbuild that js_bundle bundles with.
JEST @npm:jest:30.3.0 The jest release that the jest test framework uses. Jest 29 and 30 are supported.
JEST_TYPE_PINS @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 Versions for the @types packages that jest’s own dependencies require without a minimum version. Change it together with JEST if another jest release needs different ones.
JS_BUNDLE_FORMAT esm The form a js_bundle is written in: iife (a script), cjs, esm, or esm-split, ES modules with the code they share in chunks they import. By default ES modules for an ES-module JS_TARGET, otherwise a script in the browser and CommonJS for node.
JS_TARGET es6-esm The JavaScript to emit, written <ES version>-<module format>-<environment>: es2022, es2022-esm, es2020-esm-browser. The module format is commonjs (assumed when left out), esm, or dual, which builds a js_package in both formats. The environment is node (assumed when left out) or browser.
JS_TEST_FRAMEWORK node The test framework a project’s tests are written for: node (Node.js’s built-in node:test), jest or vitest. A target overrides it with its test_framework property (framework on a js_test).
NODE @node:24.22.1:bin/node The node binary every JavaScript tool, test and js_script runs on: one executable file, a release’s bin/node (NODE = @node:22.15.0:bin/node;) or a file in the project. Name an exact version; a range selects its lowest.
NPM_REPOSITORY_URL https://registry.npmjs.org/ The registry that @npm refers to. Set it to use a mirror or a private registry for every package.
POSTCSS @npm:postcss:8.5.28 The PostCSS that processes stylesheets.
POSTCSS_MODULES @npm:postcss-modules:9.0.1 The PostCSS plugin that scopes the class names of css-modules.
SASS @npm:sass-embedded:1.100.0 The Sass compiler that compiles .scss and .sass sources.
SASS_PNP_IMPORTER @fabr-build/sass-pnp-importer Internal: how Sass finds stylesheets inside packages. Installed with fabr.
SWC @npm:@swc/core:1.15.43 Internal: the compiler the jest test framework uses to move jest.mock calls above a file’s imports.
TYPESCRIPT @npm:typescript:5.6.3 The TypeScript compiler that compiles your sources, as a version or a range. A dist-tag such as latest is not accepted.

List a flag in a target’s deps to switch its behaviour for that target.

Flag Description
dom The sources use browser APIs. Adds the DOM type declarations to the compile, and runs the target’s tests in a jsdom environment.
es2015 The sources are written against ES2015: using a newer API is a compile error, whatever version JS_TARGET emits. A package built from them needs a runtime that supports ES2015, so the flag also applies to targets that depend on it.
es2016 The sources are written against ES2016. See es2015.
es2017 The sources are written against ES2017. See es2015.
es2018 The sources are written against ES2018. See es2015.
es2019 The sources are written against ES2019. See es2015.
es2020 The sources are written against ES2020. See es2015.
es2021 The sources are written against ES2021. See es2015.
es2022 The sources are written against ES2022. See es2015.
es2023 The sources are written against ES2023. See es2015.
es5 The sources are written against ES5: using a newer API is a compile error, whatever version JS_TARGET emits.
es6 The same as es2015.
esnext The sources may use the newest APIs the compiler knows. See es2015.
js/node_globals The sources use the globals Node.js gives every module: process, Buffer and global. In a js_bundle for a browser, the flag binds process and Buffer to the process and buffer packages, which the bundle must list in its srcs, and global to globalThis. In a bundle for Node.js it does nothing.
layout/node_modules
layout/pnp How the packages the target’s tests load are laid out. layout/pnp runs them through a PnP table over fabr’s cache, with no node_modules tree staged; layout/node_modules, the default, stages the tree. Code that finds files by walking node_modules rather than by importing needs the tree.
ts/allow_implicit_any Allow implicit any (noImplicitAny: false).
ts/allow_implicit_this Allow this to be implicitly any (noImplicitThis: false).
ts/allow_unchecked_side_effect_imports Leave side-effect imports unchecked (noUncheckedSideEffectImports: false), so that import "./typo.css" naming no file is not an error. Needs TypeScript 5.6 or later; an earlier compiler rejects the option.
ts/emit_decorator_metadata Also emit type metadata for decorators (emitDecoratorMetadata: true), as dependency-injection containers built on reflect-metadata need. Imports used only as types in decorated signatures are kept at run time. Turns on ts/experimental_decorators too.
ts/experimental_decorators Compile decorators with the semantics TypeScript used before 5.0 (experimentalDecorators: true). Class fields are then assigned rather than defined, because a defined field would hide the accessor a property decorator installs.
ts/no_es_module_interop Compile without esModuleInterop, for sources that import a callable CommonJS module with import * as x rather than a default import.
ts/no_strict Turn off all of TypeScript’s strict checks for this target’s sources.
ts/no_strict_bind_call_apply Leave the argument types of bind, call and apply unchecked (strictBindCallApply: false), as TypeScript did before 3.2.
ts/no_strict_function_types Compare function parameter types bivariantly (strictFunctionTypes: false), as TypeScript did before 2.6.
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 Allow class properties that aren’t definitely assigned (strictPropertyInitialization: false).
ts/no_use_unknown_in_catch_variables Type a caught error as any rather than unknown (useUnknownInCatchVariables: false), as TypeScript did before 4.4.

Operations: build, run, sources, test

targetdef js_package {
srcs = FILES;
deps = PACKAGES;
provided_deps = PACKAGES;
optional_provided_deps = PACKAGES;
compile_deps = PACKAGES;
exports = FILES;
resources = FILES;
tests = FILES;
test_deps = PACKAGES;
test_expectations = FILES;
test_resources = FILES;
test_env = MAP;
test_framework = STRING default ${JS_TEST_FRAMEWORK};
version = STRING;
metadata = MAP;
}

An npm package compiled from TypeScript or JavaScript sources, with a generated package.json:

js_package mylib {
srcs = src:**/*.ts;
deps = @npm:lodash:4.17.21;
tests = src:**/*.test.ts;
}

fabr build produces the package, fabr test runs its tests, fabr run runs its command-line program, which is any script directly under bin/, and fabr sources gives its sources.

Property Type Required Default Description
srcs FILES The sources to compile: TypeScript, JavaScript and stylesheets. A package.json here supplies fields for the generated one.
deps PACKAGES The packages the sources import, written to dependencies. Also takes flags, and source files to compile with the package without shipping.
provided_deps PACKAGES Packages the sources import but whoever uses this package supplies, such as react for a component library. Written to peerDependencies.
optional_provided_deps PACKAGES As provided_deps, for packages whoever uses this package may leave out: the sources are built against them, and they are written to peerDependencies marked optional.
compile_deps PACKAGES Packages the sources are compiled against that the published package does not need, such as one imported only for its types. Not written to package.json.
exports FILES The package’s public entry points, named by the sources they compile from. They are compiled even if srcs doesn’t list them.
resources FILES Files to ship in the package as they are, without compiling or checking them. The tests find them where the package ships them.
tests FILES The test files. Every source file here is run as a test, so helpers and the setupTests file go in test_deps, and snapshots in test_expectations.
test_deps PACKAGES Packages only the tests import, such as an assertion library and its types. A source file here is compiled with the tests but not run as a test, and not shipped.
test_expectations FILES Snapshot files the tests compare against (__snapshots__/*.snap). fabr test -u updates them.
test_resources FILES Files the tests read when they run. Neither compiled nor shipped.
test_env MAP Environment variables for the test processes, which run with no others.
test_framework STRING ${JS_TEST_FRAMEWORK} The test framework the tests are written for: node, jest or vitest.
version STRING The package’s version.
metadata MAP Other package.json fields (description, license, repository): a string, a block for an object, or several blocks for an array. Fields fabr generates or a published package doesn’t carry are rejected.

Operations: build, run, sources, test

targetdef fabr_plugin {
srcs = FILES;
deps = PACKAGES;
provided_deps = PACKAGES;
optional_provided_deps = PACKAGES;
compile_deps = PACKAGES;
exports = FILES;
resources = FILES;
tests = FILES;
test_deps = PACKAGES;
test_expectations = FILES;
test_resources = FILES;
test_env = MAP;
test_framework = STRING default ${JS_TEST_FRAMEWORK};
version = STRING;
metadata = MAP;
libs = FILES;
optional_libs = FILES;
}

A plugin built in the project: a js_package of its code, with its .fabr files. plugin <name>; uses it as it would an installed plugin, reading libs before anything is built; its code is built, for the machine fabr runs on, only when a rule it implements is first used. It is tested and run as the js_package it is.

fabr_plugin @example/text-lint {
srcs = src:**/*.ts;
exports = src:rules/*.ts;
provided_deps = @fabr-build/core;
libs = lib/LINT.fabr;
}

Files the plugin’s own rules build, among its resources, need a stage the plugin is used at without them (resources<STAGE=1> = …; with plugin X<STAGE=0>;).

Property Type Required Default Description
srcs FILES The sources to compile: TypeScript, JavaScript and stylesheets. A package.json here supplies fields for the generated one.
deps PACKAGES The packages the sources import, written to dependencies. Also takes flags, and source files to compile with the package without shipping.
provided_deps PACKAGES Packages the sources import but whoever uses this package supplies, such as react for a component library. Written to peerDependencies.
optional_provided_deps PACKAGES As provided_deps, for packages whoever uses this package may leave out: the sources are built against them, and they are written to peerDependencies marked optional.
compile_deps PACKAGES Packages the sources are compiled against that the published package does not need, such as one imported only for its types. Not written to package.json.
exports FILES The package’s public entry points, named by the sources they compile from. They are compiled even if srcs doesn’t list them.
resources FILES Files to ship in the package as they are, without compiling or checking them. The tests find them where the package ships them.
tests FILES The test files. Every source file here is run as a test, so helpers and the setupTests file go in test_deps, and snapshots in test_expectations.
test_deps PACKAGES Packages only the tests import, such as an assertion library and its types. A source file here is compiled with the tests but not run as a test, and not shipped.
test_expectations FILES Snapshot files the tests compare against (__snapshots__/*.snap). fabr test -u updates them.
test_resources FILES Files the tests read when they run. Neither compiled nor shipped.
test_env MAP Environment variables for the test processes, which run with no others.
test_framework STRING ${JS_TEST_FRAMEWORK} The test framework the tests are written for: node, jest or vitest.
version STRING The package’s version.
metadata MAP Other package.json fields (description, license, repository): a string, a block for an object, or several blocks for an array. Fields fabr generates or a published package doesn’t carry are rejected.
libs FILES The plugin’s .fabr files: literal paths or patterns, relative to this file, read as written (no substitution, and every guard’s together).
optional_libs FILES The plugin’s .fabr files a project reads only where it includes one, by its path within the plugin’s package (include <package>:<path>;): literal paths or patterns, relative to this file, as libs are.

Operations: build

targetdef js_bundle {
srcs = PACKAGES;
entry = REQUIRED FILES;
deps = PACKAGES;
output = REWRITE;
defines = MAP;
}

Bundle sources and their dependencies into self-contained JavaScript files, with esbuild:

js_bundle handler {
entry = src:handler.ts;
srcs = src:**/*.ts @npm:lodash:4.17.21;
}

The platform follows JS_TARGET, and the module format JS_BUNDLE_FORMAT. release and relwithdebinfo builds are minified; debug and relwithdebinfo builds have source maps.

Property Type Required Description
srcs PACKAGES What may go into the bundle: source files, and packages to include in it.
entry REQUIRED FILES yes The file to bundle. A pattern matching several files produces one bundle for each. Entry files need not also be in srcs.
deps PACKAGES Packages the environment the bundle runs in already has. Imports of them are left in the output and their code is not included.
output REWRITE Renames for the output files. An entry is named <name>-[hash].js, chunks chunks/[name]-[hash].js and assets [name]-[hash].[ext], their placeholders filled in by esbuild after the rename, so it matches them as written: *-'[hash]'.js -> $1.'[hash]'.min.js, or *-'[hash]'.js -> $1.js to drop the hash. All entries share one pattern.
defines MAP Constants to substitute at compile time (esbuild’s define). Each value is JavaScript source text, so a string needs its own quotes: process.env.NODE_ENV = '"production"';, but DEBUG = false;.

Operations: test

targetdef js_test {
tests = FILES;
deps = PACKAGES;
compile_deps = PACKAGES;
framework = STRING default ${JS_TEST_FRAMEWORK};
resources = FILES;
env = MAP;
expectations = FILES;
}

Tests that don’t belong to a package, such as an end-to-end suite:

js_test e2e {
tests = test:**/*.test.ts;
deps = test:**/*.ts mylib @npm:@types/node:22.15.3;
}

A source file named setupTests at the root of the source tree is loaded in every test process before the test file.

Property Type Required Default Description
tests FILES The test files. Every source file here is run as a test, so helpers and the setupTests file go in deps, and snapshots in expectations.
deps PACKAGES Packages the tests import, and source files to compile with them that aren’t themselves tests.
compile_deps PACKAGES Packages the tests are compiled against whose flags say nothing of the test run: dom here is the DOM’s types alone.
framework STRING ${JS_TEST_FRAMEWORK} The test framework the tests are written for: node, jest or vitest.
resources FILES Files the tests read when they run.
env MAP Environment variables for the test processes, which run with no others.
expectations FILES Snapshot files the tests compare against (__snapshots__/*.snap). fabr test -u updates them.

Operations: run

targetdef js_script {
deps = PACKAGES;
entry = REQUIRED FILES;
args = STRING;
env = MAP;
}

A Node.js program that can be run: with fabr run, as a command in a generate target, or as the tool of a serve target.

js_script migrate { entry = scripts/migrate.ts; deps = @npm:pg:8.11.5; }
js_script docs_tool { entry = @npm:astro:7.1.1; deps = @npm:@astrojs/starlight:0.41.3; }

The second form adds dependencies to a packaged tool, such as plugins it loads by name. entry and deps are installed together, with one version of each package.

Property Type Required Description
deps PACKAGES The packages the program imports, and other files it needs.
entry REQUIRED FILES yes The script file to run, or a package to run by its command-line program. Where a package has several, name one: @npm:typescript:5.6.3:tsc.
args STRING Arguments passed to the program ahead of any given when it is run.
env MAP Environment variables the program is launched with, wherever it runs: a generate or patched target’s own env is set over them.
targetdef npm_repository {
url = REQUIRED STRING;
access = STRING;
}

An npm registry, to fetch packages from and publish them to:

npm_repository @internal { url = https://npm.example.com/; access = private; }

Packages in it are then written @internal:<package>:<version>. Credentials come from .npmrc.

Property Type Required Description
url REQUIRED STRING yes The registry’s URL.
access STRING The access requested when a package is first published: public, or private (restricted means the same).
targetdef node_distribution {
url = REQUIRED STRING;
}

Node.js releases as nodejs.org publishes them, for the target platform (macOS or Linux, x64 or arm64). @node:24.22.1 is that release and @node:24.22.1:bin/node its binary. Each archive is checked against the release’s SHASUMS256.txt, and a build nodejs.org doesn’t publish (musl) is fetched from unofficial-builds.nodejs.org. fabr declares @node for you; declaring it yourself points it at a mirror:

node_distribution @node { url = "https://mirror.example.com/nodejs"; }
Property Type Required Description
url REQUIRED STRING yes The URL releases are published under, laid out as nodejs.org/dist is (or a mirror).
targetdef browser_repository {
data = REQUIRED FILES;
}

Web browsers, by their Can I Use names (safari, ios_saf, chrome, and_chr, firefox, edge). @browser:safari:17.2 is Safari 17.2 or later; @browser:baseline:2023 is every core browser at the version that supports all of Baseline 2023. A browser has no files: a target requires one to say its output must run there. fabr declares @browser for you.

Property Type Required Description
data REQUIRED FILES yes The browser data packages: caniuse-lite’s browser releases and baseline-browser-mapping’s Baseline years.

Operations: build

targetdef js_compile {
srcs = FILES;
deps = PACKAGES;
package_name = STRING;
module_extension = STRING;
rewrite_imports = REWRITE;
resources = FILES;
declarations = FILES;
}

Compile TypeScript and JavaScript sources to JavaScript and type declarations. A js_package, js_test or js_script does this for its own sources; declare one to compile sources on their own.

Property Type Required Description
srcs FILES The sources: .ts, .tsx, .js, .jsx, and .d.ts declarations.
deps PACKAGES The packages the sources import.
package_name STRING The package name the sources can import themselves by.
module_extension STRING The extension the emitted JavaScript takes instead of .js (.mjs), with the imports between the files written to match.
rewrite_imports REWRITE Rules that rename import specifiers (*.scss -> *.css), applied when resolving an import and to the import written in the output.
resources FILES Files that aren’t compiled but that the sources import, such as stylesheets, so that those imports resolve.
declarations FILES Declarations another compile emitted for some of the sources, named as the sources are: those sources are compiled against, and neither checked nor emitted.

Operations: build

targetdef js_installation {
deps = PACKAGES;
}

The installation of packages: a node_modules tree of every package deps hold or reach, with the loose files among deps at their own paths.

Property Type Required Description
deps PACKAGES The packages installed, and loose files.

Operations: build

targetdef sass_compile {
srcs = FILES;
deps = PACKAGES;
}

Compile Sass sources to CSS. A js_package or js_bundle does this for the Sass among its own sources; declare one to compile Sass on its own.

Each .scss or .sass source produces a .css file of the same name (x.module.scss produces x.module.css), with a source map in builds that have them. Partials (_x.scss) produce nothing.

Property Type Required Description
srcs FILES The Sass sources.
deps PACKAGES Packages whose stylesheets the sources load with @use or @import.

Operations: build

targetdef css_postcss {
srcs = FILES;
deps = PACKAGES;
package_name = STRING;
}

Scope the css-modules in a set of CSS files. A js_package or js_bundle does this for its own stylesheets; declare one to process CSS on its own.

Each x.module.css produces x.css with its class names scoped, and a TypeScript module x.css.ts exporting them. Other stylesheets are copied unchanged.

Property Type Required Description
srcs FILES The stylesheets: .css files, or the output of a sass_compile.
deps PACKAGES Packages whose stylesheets a composes can name (composes: x from 'pkg/base.css').
package_name STRING The package name that scoped class names are derived from, together with each file’s path.

Ready-made targets declared by these libraries, usable by name in any project.

npm_repository @npm {
url = ${NPM_REPOSITORY_URL};
access = public;
}

The public npm registry, or the registry NPM_REPOSITORY_URL names. Packages published to it are public; to publish private packages, declare an npm_repository with access = private;.

node_distribution @node {
url = https://nodejs.org/dist;
}

Node.js releases, from nodejs.org.

browser_repository @browser {
data = BROWSER_DATA;
}

Web browsers, from the browser data BROWSER_DATA pins.

js_script TSC_DRIVER {
entry = @fabr-build/js-tools:fabr-tsc;
deps = ${TYPESCRIPT};
}

Internal: the program that runs the TypeScript compiler. To choose the compiler version, set TYPESCRIPT.

js_script JS_BUNDLER {
entry = @fabr-build/js-tools:fabr-bundle;
deps = ${ESBUILD};
}

Internal: the program js_bundle runs, which drives esbuild.

js_script SASS_DRIVER {
entry = @fabr-build/js-tools:fabr-sass;
deps = ${SASS} ${SASS_PNP_IMPORTER};
}

Internal: the program sass_compile runs, which drives Sass.

js_script POSTCSS_DRIVER {
entry = @fabr-build/js-tools:fabr-postcss;
deps = ${POSTCSS} ${POSTCSS_MODULES} AUTOPREFIXER BROWSER_DATA;
}

Internal: the program css_postcss runs, which drives PostCSS.

js_script JS_COVERAGE_DRIVER {
entry = @fabr-build/js-tools:fabr-coverage;
}

Internal: the program a test run measured for coverage runs after its tests, which turns the V8 coverage they left into lcov.

js_script JS_NODE_TEST_RUNNER {
entry = @fabr-build/js-tools:fabr-test-runner;
}

The program that runs tests under the node framework. A replacement is run in the directory of compiled tests as <runner> --report=<file> --env=<node|jsdom> [--update-snapshots] [--setup=<file>]… <test files…>. It must exit with a non-zero status when a test fails, and write its results to the report file in CTRF form.

js_script JS_JEST_RUNNER {
entry = @fabr-build/js-tools:fabr-jest-runner;
deps = ${JEST} ${SWC} JEST_TYPE_PINS;
}

The program that runs tests under the jest framework, invoked as JS_NODE_TEST_RUNNER is.

js_script JS_VITEST_RUNNER {
entry = @fabr-build/js-tools:fabr-vitest-runner;
}

The program that runs tests under the vitest framework, invoked as JS_NODE_TEST_RUNNER is.