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.
Global properties
Section titled “Global properties”| 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. |
Targets
Section titled “Targets”js_package
Section titled “js_package”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. |
fabr_plugin
Section titled “fabr_plugin”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. |
js_bundle
Section titled “js_bundle”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;. |
js_test
Section titled “js_test”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. |
js_script
Section titled “js_script”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. |
npm_repository
Section titled “npm_repository”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). |
node_distribution
Section titled “node_distribution”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). |
browser_repository
Section titled “browser_repository”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. |
js_compile
Section titled “js_compile”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. |
js_installation
Section titled “js_installation”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. |
sass_compile
Section titled “sass_compile”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. |
css_postcss
Section titled “css_postcss”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. |
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;}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
Section titled “@browser”browser_repository @browser { data = BROWSER_DATA;}Web browsers, from the browser data BROWSER_DATA pins.
TSC_DRIVER
Section titled “TSC_DRIVER”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_BUNDLER
Section titled “JS_BUNDLER”js_script JS_BUNDLER { entry = @fabr-build/js-tools:fabr-bundle; deps = ${ESBUILD};}Internal: the program js_bundle runs, which drives esbuild.
SASS_DRIVER
Section titled “SASS_DRIVER”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.
POSTCSS_DRIVER
Section titled “POSTCSS_DRIVER”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_COVERAGE_DRIVER
Section titled “JS_COVERAGE_DRIVER”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_NODE_TEST_RUNNER
Section titled “JS_NODE_TEST_RUNNER”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_JEST_RUNNER
Section titled “JS_JEST_RUNNER”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_VITEST_RUNNER
Section titled “JS_VITEST_RUNNER”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.