Fabr
Declarative build orchestration
Declare the inputs.
Derive everything else.
Targets carry only their sources and dependencies. Every step — compile, bundle, test, generate — is a pure transform in one graph, cached by its inputs. No lockfiles, no config files, nothing running blind on the side.
01 — The model
Everything is files or properties.
File sets are what you build and manipulate; properties are the parameters they build under. A target is just a derived file set, and you address it like a directory — app/package.json works whether app is a folder, a build output, or a package pulled from npm.
Properties are assigned once and resolved lazily. That's the whole language.
plugin @fabr-build/js;
JS_TARGET = es2021-commonjs;
js_package app {
srcs = ./src:*.ts;
deps = @npm:react:18.3.1 base;
}
js_package base {
srcs = ./base:*.ts;
}info:Building base (required by third)
info:Compiling base
info:Building base [JS_TARGET=es6-esm] (required by second < third)
info:Compiling base [JS_TARGET=es6-esm]
info:Building second [JS_TARGET=es6-esm] (required by third)
info:Compiling second [JS_TARGET=es6-esm]
info:Building third
info:Compiling third
info:Built third02 — Constraints
Two builds of the same library. Zero extra config.
Request a dependency in the form you need it — base<JS_TARGET=es6-esm> — and fabr builds an additional version of that subgraph alongside the original. Not a replacement. Not a second checkout.
Cross-compilation works the same way: <TARGET=x86_64-linux-gnu>. How the model fits together →
And the ordinary things, done properly
03 — Specifications
Every step is a pure transform
Compiles, bundles, even arbitrary generate commands land in the same graph, keyed by their inputs.
Deterministic, no lockfile
Versions resolve from what you require — per target by MVS, or pinned project-wide with a catalog. Nothing to drift or merge-conflict.
A live dev loop
Add -w and fabr rebuilds, retests or relaunches on change; a serve target hot-syncs without a restart.
One tool to run them all
Packaging, compiling, bundling, testing — it all works out of the box, with no per-tool configuration needed.
One file, two minutes
$ npm i -g @fabr-build/cli @fabr-build/js
$ cat > PROJECT.fabr <<'EOF'
plugin @fabr-build/js;
js_package mylib { srcs = src:**/*.ts; }
EOF
$ fabr build mylibQuickstart for JS/TS →