Quick start (JavaScript / TypeScript)
This guide takes you from an empty directory to a TypeScript package that is compiled, tested and
runnable. The whole project is a PROJECT.fabr file and your sources: there is no package.json,
tsconfig.json, test configuration or lockfile to write. To convert a project you already have,
see Migrating an npm, Yarn or pnpm project.
Install
Section titled “Install”Fabr needs Node.js 22.19 or later to run. Install the fabr command globally:
npm install -g @fabr-build/cliThat also installs @fabr-build/js, the plugin with the JavaScript and TypeScript rules, and
@fabr-build/js-tools, the compiler driver, test runners and other programs the plugin runs.
Those programs, your tests and your own scripts run on a Node.js release that fabr downloads on the first build, 24.22.1 unless you choose another, rather than on the Node.js you installed, so the version on your machine doesn’t affect what is built; see The Node.js version.
A minimal project
Section titled “A minimal project”Create PROJECT.fabr in an empty directory:
plugin @fabr-build/js;
js_package mylib { srcs = src:**/*.ts;}and a source file, src/index.ts:
export const greet = (name: string): string => `Hello, ${name}!`;Then build it:
fabr build mylibFabr downloads the TypeScript compiler, compiles the source, and assembles a package with a
generated package.json. The package isn’t written into your project; it is kept in fabr’s
cache, and you look at it with fabr:
fabr ls mylib # list the files in the packagefabr cat mylib/index.js # print one of themfabr cp mylib ./out # copy the package to ./out/mylib/srcs = src:**/*.ts means “the .ts files under src, named without the src/ prefix”, which
is why the package contains index.js and not src/index.js.
Two settings are worth making explicit from the start: the compiler version, and the JavaScript
to emit. Both have defaults (typescript 5.6.3 and es6-esm):
TYPESCRIPT = @npm:typescript:5.6.3;JS_TARGET = es2022-esm;TypeScript compilation lists what else you would have put in
tsconfig.json and where it goes.
Dependencies
Section titled “Dependencies”Declare an npm dependency with an @npm: reference, @npm:<package>:<version>, in the target’s
deps:
js_package mylib { srcs = src:**/*.ts; deps = @npm:lodash:4.17.21 @npm:@types/lodash:4.17.7;}There is no install step and no lockfile: fabr fetches packages as the build needs them, and the same build file always selects the same versions. When several targets share dependencies, declare them once in a catalog and refer to its members by name:
catalog @pkg { deps = @npm:typescript:5.6.3 @npm:@types/node:22.15.3 @npm:chai:4.3.6 @npm:@types/chai:4.3.1;}
TYPESCRIPT = @pkg:typescript;
js_package mylib { srcs = src:**/*.ts; deps = @pkg:@types/node;}Dependencies covers version ranges, how versions are chosen, resolving version conflicts, patching a package, and private registries.
Testing
Section titled “Testing”Add a tests property naming the test files, and test_deps for anything only the tests need.
Test files are left out of the built package.
js_package mylib { srcs = src:**/*.ts; tests = src:**/*.test.ts; deps = @pkg:@types/node; test_deps = @pkg:chai @pkg:@types/chai;}By default, tests run with Node.js’s built-in test runner. describe, it and the hooks are
globals, and assertions come from whichever library you add:
import { expect } from "chai";import { greet } from "./index";
describe("greet", () => { it("greets by name", () => { expect(greet("world")).to.equal("Hello, world!"); });});fabr test mylibA summary such as mylib: 1 test passed is printed for each target, followed by any failed tests,
and fabr test exits with status 1 if any test failed. A run is cached, passing or failing, so
running fabr test again without changes reports the same result without re-running the tests;
fabr test --retry-failed mylib runs failed tests again. Other options:
fabr test -t 'greets' mylib # only the tests whose names match a regular expressionfabr test --coverage mylib # also measure coverage, writing coverage/lcov.infoTo run an existing jest or vitest suite, or for snapshots, DOM tests and coverage, see Testing.
Running programs
Section titled “Running programs”A js_script is a Node.js program: entry is the file to run, and deps the packages it
imports. It runs on the same downloaded Node.js release as your tests.
js_script tool { entry = src:main.ts; deps = @npm:chalk:5.3.0 @pkg:@types/node;}fabr run tool arg1 arg2The program runs in your current directory with your terminal, and everything after the target name is passed to it. Options for fabr itself go before the target.
The commands you’ll use
Section titled “The commands you’ll use”| Command | What it does |
|---|---|
fabr build <target> |
Build the target. |
fabr test <target> |
Compile and run the target’s tests. |
fabr run <target> [args…] |
Build a program and run it. |
fabr ls <reference> |
List the files a target produces. |
fabr cat <reference> |
Print them. |
fabr cp <reference…> <dir> |
Copy them into a directory. |
fabr deps <target> |
Show the dependency versions the target resolves to. |
fabr audit <target> |
Check those dependencies for known security advisories. |
fabr list-targets |
List the project’s targets and what each can do. |
Two options work with most of them:
-D<NAME>=<VALUE>changes a setting for one run:fabr build -DBUILD_TYPE=release mylib.-wkeeps fabr running and rebuilds, retests or restarts as your sources change; see Watch mode & dev servers.
The command-line reference has the rest.
Next steps
Section titled “Next steps”- Dependencies: version ranges, catalogs, conflicts, patches and private registries.
- Testing: the three test frameworks, snapshots, DOM tests and coverage.
- TypeScript compilation: what replaces
tsconfig.json, and how fabr’s output differs fromtsc’s. - Module resolution: what an import can reach.
- Stylesheets: CSS, Sass and css-modules.
- Bundling: one-file builds for browsers and for Node.js.
- Publishing packages: the generated
package.json, andfabr sync. - Projects with several packages: the layout that replaces workspaces.
- Continuous integration: caching, output and exit statuses.
- Targets and configuration: every
js_*target and setting. - Watch mode & dev servers: the live rebuild, retest and relaunch loop.
- Known limitations: rough edges worth knowing about up front.