Skip to content

Quick start (JavaScript / TypeScript)

This guide gets you from an empty directory to a compiled, tested, runnable TypeScript package.

Fabr ships as two npm packages: @fabr-build/cli (the fabr command) and @fabr-build/js (the JavaScript/TypeScript rules, loaded from your build script). Install them together — globally.

Terminal window
npm install -g @fabr-build/cli @fabr-build/js

Create a PROJECT.fabr at your project root:

plugin @fabr-build/js;
# Optional: which TypeScript compiler to use, and the output target.
TSC = @npm:typescript:5.4.5:tsc;
JS_TARGET = es2021-commonjs;
js_package mylib {
srcs = src:**/*.ts;
}

And a source file, src/index.ts:

export const greet = (name: string): string => `Hello, ${name}!`;

Now build it:

Terminal window
fabr build mylib

Fabr compiles the TypeScript, generates a package.json, and produces the package. Inspect the result without digging through the cache:

Terminal window
fabr ls mylib # list the built files
fabr cat mylib/index.js # print a built file — path into a target as if it were a directory
fabr cp mylib ./out # copy the package out -> ./out/mylib/ (cp -R rules)

Everything you build can be inspected in this way as if the targets were plain directories.

Declare an npm dependency with a @npm: reference — @npm:<package>:<version>:

js_package mylib {
srcs = src:**/*.ts;
deps = @npm:lodash:4.17.21 @npm:@types/node:20.12.7;
}

Versions are exact requirements, resolved deterministically at each usage using the MVS algorithm. Mutable dist-tags like latest are rejected — they would make the build non-deterministic.

When several targets share dependencies, you can declare them once in a catalog so they resolve jointly and every consumer sees one consistent set of versions:

catalog @pkg {
deps = @npm:typescript:5.4.5 @npm:@types/node:20.12.7
@npm:chai:4.3.6 @npm:@types/chai:4.3.1;
}
TSC = @pkg:typescript:tsc; # reference a catalog member as @pkg:<name>
js_package mylib {
srcs = src:**/*.ts;
deps = @pkg:@types/node;
}

For package tests, just add a tests property with the sources and (if needed) any additional test dependencies in test_deps. Tests will be automatically excluded from your main package build.

js_package mylib {
srcs = src:**/*.ts;
tests = src:**/*.test.ts;
test_deps = @pkg:chai @pkg:@types/chai;
}

Fabr ships its own test runner (built on node:test). It provides the describe/it/before/… globals; assertions are imported normally:

import { expect } from "chai";
import { greet } from "./index";
describe("greet", () => {
it("greets by name", () => {
expect(greet("world")).to.equal("Hello, world!");
});
});

Run them:

Terminal window
fabr test mylib

fabr test mylib is exactly mylib built with the test operation. A per-target report summary prints before the build-status line, and a failing test fails the target.

A js_script target defines a runnable Node program — entry is the script file to launch (or a package whose bin is the entry), and deps assemble the rest of the install (packages mount under node_modules, loose files land at their own paths):

js_script tool {
entry = src:main.js;
deps = @npm:chalk:5.3.0;
}
Terminal window
fabr run tool arg1 arg2

fabr run stages the install and launches it with inherited stdio — stdin, tty, pipes, and the exit code all pass straight through, in your current working directory. Everything after the target name is passed to the program verbatim (put any fabr options before the target).

Command What it does
fabr build <target> Build the target (the default operation).
fabr test <target> Compile and run the target’s tests.
fabr run <target> [args…] Launch a runnable with inherited stdio.
fabr ls [-l] <ref> List the files a reference resolves to (-l adds hash + size).
fabr cat <ref> Write the resolved files’ bytes to stdout.
fabr cp <ref…> <dir> Copy the resolved files into a directory.

Two flags work on most commands:

  • -D<PROP>=<VALUE> overrides a configuration property, e.g. fabr -DBUILD_TYPE=release build mylib.
  • -w keeps fabr running and rebuilds (or re-tests, or relaunches) as your sources change — see Watch mode & dev servers.

Next steps: