Fabr is a declarative, deterministic build orchestration tool. It aims at fast, accurate, and — above all — reliable builds, while keeping even complex build scenarios (multiple languages, multi-stage pipelines) simple to specify.
The ideas it's built on:
- Every build step is a pure transform from its declared inputs to its outputs — a compile, a bundle, or an arbitrary command all have the same shape — so every step lands in one dependency graph, cached by its inputs, with nothing running blind on the side.
- Reproducible, with no lockfiles. A build is a pure function of its inputs: the same inputs always produce the same outputs. Dependency versions are chosen deterministically (Go-style minimal version selection) from the requirements you actually wrote — nothing is pinned in a side file that can drift.
- Target, rule, and configuration are kept strictly apart. A target says what you want and what it needs; a rule knows how; configuration (build type, target platform, …) is separate. The same target builds, tests, or runs depending only on the operation you ask for.
- Each build step runs in an isolated view of just its declared inputs — staged into its own work
directory with a clean environment, so a step sees only what it asked for. (Host tools are still
found on
PATHrather than declared: see Known limitations.)
Fabr is in active development, but it is already fully self-hosting — fabr builds fabr and runs its own tests — and has a solid baseline of support for JavaScript and TypeScript (compile, bundle, test, run, publish). Other ecosystems, and some of the broader ambitions above, are still to come. Expect rough edges; see Known limitations for the ones worth knowing up front.
Install the CLI globally; it brings the JavaScript/TypeScript rules (@fabr-build/js) with it:
npm install -g @fabr-build/cliDon't install @fabr-build/js as a global package of its own — npm then gives it a second copy of
@fabr-build/core, and fabr can only work with one.
Create a PROJECT.fabr at your project root:
plugin @fabr-build/js; # load the JavaScript/TypeScript rules
JS_TARGET = es2021-commonjs; # configuration
js_package mylib { # a target: what to build and what it needs
srcs = src:**/*.ts;
tests = src:**/*.test.ts;
}
and a source file, src/index.ts:
export const greet = (name: string): string => `Hello, ${name}!`;Then build, test, and inspect it — targets can be inspected as if they were plain directories:
fabr build mylib # compile and package
fabr test mylib # compile and run the tests
fabr ls mylib # list the built files
fabr cat mylib/index.js # print a built file to stdoutNothing here says how to compile TypeScript or lay out a package — that knowledge lives in the
@fabr-build/js plugin's rules. The script only states the target and its inputs. See the
Quick start for dependencies, catalogs, and running programs.
Full documentation lives at fabr.build — the introduction, language syntax, the command-line and target-type references, and guides.
Fabr builds and tests itself — that's the primary workflow once you have a working fabr. A
Yarn/TypeScript devchain exists only to bootstrap the very first fabr from source:
yarn bootstrap # devchain-build fabr, then have the built fabr rebuild + test all packages
yarn dist # devchain build + Jest tests + lint (the pre-submit gate)Contributions are welcome — see PLUGINS.md to add support for a new ecosystem, and the Getting involved page. Please discuss design-touching changes before implementing.
Fabr is free software, licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later).