Skip to content

feat(treefmt): add the treefmt integration - #98

Merged
clhodapp merged 2 commits into
feat/facterfrom
feat/treefmt
Oct 18, 2026
Merged

clhodapp merged 2 commits into
feat/facterfrom
feat/treefmt

Conversation

@clhodapp

@clhodapp clhodapp commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Adds caisson.treefmt, the first of the survey's strong candidates, and the publishing mode it needs. This PR is based on #97. Merge #94 through #97 first, then change the base of this PR to main.

The treefmt integration

A treefmt configuration is the formatters of a tree, written as treefmt-nix modules. It is evaluated per system, once for each system in force, on the selected package set. The integration is named treefmt and resolves the ecosystem source treefmt-nix, as the darwin integration resolves nix-darwin.

treefmt-nix's own evalModule runs on the library of a nixpkgs. Instead, caisson evaluates treefmt-nix's published submodule-modules with its own evalModules on the composed library, with pkgs set to the selected set. Those modules need treefmt-nix's special arguments, which treefmt-nix publishes only inside the type its submoduleWith returns. caisson reads them from that type's functor.payload.specialArgs, an attribute of the module system that isn't documented as public.

The outputs are:

  • wrapper, the program nix fmt runs;
  • configFile, the treefmt.toml;
  • programs, the formatters by name;
  • check, a function from a source tree to a derivation that fails when the tree is not formatted.

Only the configuration named default is published, in two outputs:

Output Value
formatter.<system> its wrapper, which nix fmt runs
checks.<system>.treefmt its check over the root of the tree, root on mkLib, which nix flake check builds; it fails when the tree is not formatted

The check is published where mkLib is given a root. A tree may declare treefmt configurations under other names beside default. For this, exportsTo may be a list of exports, and an export may carry only, a function of the manifest that says whether the configuration is published in that output. exportsOf reads them in the integrations library.

The pinned-world test checks four things:

  • formatter holds the exact derivation treefmt-nix's mkWrapper builds from the same module;
  • with a root, checks.<system> holds only treefmt, the configuration's check over the root;
  • without a root, no check is published;
  • a second configuration, docs, is evaluated.

Built for real, the published check succeeds on a formatted tree and fails on an unformatted one, with the diff.

Publishing keyed by system

Flakes key packages, formatter and others by system first, and caisson's publishing produced only flat names. exportsTo.bySystem publishes each configuration under the system of its evaluation, as <attrset>.<system>.<name>, named by the usual rules over its path without the systems on it. Combined with single, the output holds the configuration named default at each system, as formatter.<system> does. An output that mixes entries keyed by system with entries that aren't is refused, with their paths. A unit test covers all three cases. nvf, nixvim and kubenix will publish their packages this way.

Checks

🤖 Generated with Claude Code

clhodapp and others added 2 commits October 18, 2026 10:32
A treefmt configuration is the formatters of a tree, written as
treefmt-nix modules and evaluated at every system in force on the
selected package set. caisson evaluates treefmt-nix's
`submodule-modules` with its own `evalModules` on the composed library,
with `pkgs` set to that package set and treefmt-nix's special arguments
read from the type its `submoduleWith` returns. The outputs are the
wrapper, the configuration file, the formatters and the check over a
source tree. The configuration named `default` is published as
`formatter.<system>`, which `nix fmt` runs; the pinned-world suite
checks that it is the derivation treefmt-nix's `mkWrapper` builds.

Publishing gains `exportsTo.bySystem`: an output keyed by the system of
each evaluation first, `<attrset>.<system>.<name>`, or with `single`
the one configuration named `default` at each system.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ly that configuration

A tree with a treefmt configuration named `default` now also publishes
`checks.<system>.treefmt`, the configuration's `check` over the root of
the tree (`root` on `mkLib`), so `nix flake check` fails when the tree
is not formatted. A tree without a root, such as a library composed
inside a test, publishes no check.

Every treefmt configuration used to be an entry for `formatter`, which
holds a single value, so a second configuration, under any other name,
was refused when the top published. Now only the configuration named
`default` is published; one under another name is evaluated, passed
up, and found through the manifest.

`exportsTo` may be a list of exports, for an integration published in
more than one output, and an export may carry `only`, a function of the
manifest saying whether the configuration is published there.
`exportsOf` replaces `exportsToOf` in the integrations library.

The pinned-world test declares a second configuration, `docs`, which
the previous integration refused, and checks the check is published
over the root and absent without one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@clhodapp
clhodapp merged commit 825a72c into main Oct 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant