// reference
API reference
Every public function and the options it accepts — sourced from
lib.nix /
writers.nix. For the narrative guide, see
API.md on GitHub.
Constructors
nixx.<ctor> ''body'' makes a block tagged with a language. Call it as a function to attach options:
bun ''body'' { compile = true; }.
| constructor | __lang | mkApps? | in a runner |
|---|---|---|---|
| sh · bash | bash | ✓ | inline |
| uv | python-uv | ✓ | heredoc → uv run |
| bun | bun | ✓ | heredoc → bun run |
| ts | typescript | ✓ | heredoc → tsx |
| node | node | ✓ | heredoc → node |
| deno | deno | ✓ | heredoc → deno run |
| py | python | — | heredoc → python3 |
| perl | perl | — | heredoc → perl |
| ruby | ruby | — | heredoc → ruby |
| lua | lua | — | heredoc → lua |
mkApps ships binaries for 6 langs; py/perl/ruby/lua have no binary builder but run fine as task/script bodies.
Block options
Language-specific options attach per-block by calling the block. packages is global only (per-block throws). nixx.app { … } block is a back-compat helper that does the same thing.
bash
| option | type | default | description |
|---|---|---|---|
| strict | bool | true | set -euo pipefail preamble (delegated to writeShellApplication). |
uv (python-uv)
| option | type | default | description |
|---|---|---|---|
| projectRoot | path | null | null | Dir with pyproject.toml + uv.lock; uv run --frozen resolves from it. Preferred over requirements. |
| requirements | [ str ] | [ ] | Inline PEP 723 deps for one-off scripts. Ignored when projectRoot is set. |
| pythonReq | str | ">=3.11" | requires-python constraint in the PEP 723 header. |
| frozen | bool | true | Pass --frozen (use the lock as-is); false re-resolves. |
| lintIgnore | [ str ] | [ ] | ruff rule codes to ignore at the build gate. |
bun
| option | type | default | description |
|---|---|---|---|
| projectRoot | path | null | null | Dir with package.json + bun.lock; deps are baked in at build time. |
| compile | bool | true | bun build --compile → one self-contained binary. false emits a bun-shebang .ts (runtime auto-install). |
node
| option | type | default | description |
|---|---|---|---|
| nodeModules | drv | null | null | Derivation providing /lib/node_modules; put on NODE_PATH (Node has no inline-dep mechanism). |
| syntaxCheck | bool | true | node --check build gate. |
ts (typescript)
| option | type | default | description |
|---|---|---|---|
| nodeModules | drv | null | null | Derivation providing /lib/node_modules (NODE_PATH); tsx runs the .ts directly. |
deno
No language-specific options — declare deps inline via npm: / jsr: import specifiers.
writers · mkApps
mkApps { … } { name = block; … }
Global options (first attrset) apply to every app. The block's __lang dispatches to a builder.
global options
| option | type | default | description |
|---|---|---|---|
| packages | [ drv ] | [ ] | Global — adds each pkg's /bin to PATH for every app. Per-block packages throws; set it here. |
| vars | attrset | { } | Nix values spliced into bodies via @nix(…) markers. |
dispatch (lang → builder)
| __lang | builder | extra options picked |
|---|---|---|
| bash | writeBashApplication | strict |
| python-uv | writeUvApplication | projectRoot, frozen, requirements, pythonReq, lintIgnore |
| bun | writeBunApplication | projectRoot, compile |
| node | writeNodeApplication | nodeModules, syntaxCheck |
| typescript | writeTsxApplication | nodeModules |
| deno | writeDenoApplication | (none — inline via npm:/jsr:) |
returns
| option | type | default | description |
|---|---|---|---|
| { name = drv } | attrset | — | Each attr is a store binary at /nix/store/…/bin/<name>; attr name = binary name. |
writers · mkTasks
mkTasks { … } { name = block | task {…} block; … }
A just-style runner. One bash process, so an export in an early task persists into later ones; cwd and shell options reset at each task's entry.
options
| option | type | default | description |
|---|---|---|---|
| name | str | "tasks" | Runner binary name (also the bash completion function name). |
| packages | [ drv ] | [ ] | Global — baked into the runner's runtimeInputs and re-exposed on the devShell/extendShell prompt. Single source of truth (see README). |
| inputsFrom | [ drv ] | [ ] | Other derivations whose stdenv setup hooks + build inputs apply to the dev shell — for tools that need an env/stdenv hook, not just a binary on PATH. |
| vars | attrset | { } | Nix values for @nix(…) markers. |
| env | attrset | { } | Exported in every task body. |
| envCheck | bool | false | Global default for every bash task: abort before a body runs if a required env var is unset/empty. false = check only with --env-check; true = always. Per-block overrides; --env-list <task> prints needs without running. |
| defaultDeps | [ str ] | [ ] | Task names run before every task; the default-dep tasks themselves are exempt (no loop). |
returns
| option | type | default | description |
|---|---|---|---|
| runner | drv | — | writeShellApplication (shellcheck-gated) — nix run .#<name>. |
| devShell | mkShell | — | Runner + packages + a bash tab-completion hook for all task names. |
| extendShell | shell → mkShell | — | Merge the runner + packages + hook into your shell. |
| tasks | attrset | — | name → block (each carries .text, .env, .cwd, .deps, …). |
| meta | [ … ] | — | [{ name, text, file, line, indent, description, group }] — feeds linter source-mapping. |
Per-task options
sh ''…'' { deps = [ … ]; env = { … }; cwd = ./d; }
Task options ride on the block itself — the same block ''…'' { … } call that attaches language options in mkApps. There is no separate task wrapper. Per-task packages throws (use the global). Positional args reach bash bodies as $@; non-bash bodies get them via env.
options
| option | type | default | description |
|---|---|---|---|
| description | str | null | null | One-line summary shown by --list. |
| group | str | null | null | Groups tasks under a header in --list output. |
| deps | [ str ] | [ ] | Prerequisite task names (just-style; each runs at most once per invocation). |
| env | attrset | { } | Exported before the body; merged with the runner's global env, per-task wins on conflict. |
| cwd | path | null | null | Working dir. The runner resets to the invocation dir, then cds here — a dep's cwd never leaks in. |
| envCheck | bool | (global) | Override the runner's envCheck default for this task: true = always check, false = only with --env-check. |
lib · mkTasks (pure)
nixx.mkTasks { … } { name = block; … }
No pkgs, no packages option, no devShell — use it when you only need the runner script text or a body's .text.
options
| option | type | default | description |
|---|---|---|---|
| name | str | "tasks" | Same semantics as the pkgs-bound mkTasks option. |
| vars | attrset | { } | Same semantics as the pkgs-bound mkTasks option. |
| defaultDeps | [ str ] | [ ] | Same semantics as the pkgs-bound mkTasks option. |
| env | attrset | { } | Same semantics as the pkgs-bound mkTasks option. |
returns
| option | type | default | description |
|---|---|---|---|
| tasks | attrset | — | name → block. |
| runner | str | — | The bash runner as a script string (no derivation — no pkgs). |
| meta | [ … ] | — | Source-mapping metadata. |
lib · mkScript / mkScripts
nixx.mkScript { … } block · nixx.mkScripts { … } { name = block; … }
A standalone mkScript is evaluated (a bare ${VAR} needs ''$); mkScripts source-reads, so it pays zero ${} tax.
mkScript options
| option | type | default | description |
|---|---|---|---|
| lang | str | null | null | Overrides the block's __lang; falls back to bash. |
| vars | attrset | { } | @nix(…) values. |
| shebang | str | null | null | Overrides the lang profile's shebang. |
| strict | bool | null | null | set -euo pipefail (bash profile default true; null → profile). |
| packages | [ drv ] | [ ] | bash only — exported as PATH. Ignored for non-bash langs. |
| requirements | [ str ] | [ ] | uv — PEP 723 inline deps. |
| pythonReq | str | null | null | uv — requires-python. |
mkScripts options
| option | type | default | description |
|---|---|---|---|
| lang | str | "bash" | Applied to every script in the set. |
| vars | attrset | { } | @nix(…) values. |
Returns { scripts, meta } — scripts is name → text; meta feeds the linter remap.
Vars markers
Splice an actual Nix value into a source-read body — native ${…} does not run there. Values come from vars = { … } on mkApps/mkTasks/mkScript.
| option | type | default | description |
|---|---|---|---|
| @nix(name) | raw | toString of the Nix value, spliced verbatim — for paths / derivations / numbers. | |
| @sh:q(name) | bash | POSIX single-quoted shell literal (shq) — for arbitrary strings. Pass values into a non-bash body via env instead. | |
| (a path value) | store | Auto-imported into the store (reproducible; exec bit preserved). |
shellint
nix run nixx#shellint -- ./ · (for pkgs).shellint { src = ./.; … }
Source-driven static lint (no eval): tree-sitter-nix locates every bash ''…'' block and runs three block-scoped passes. It's the static half of the pair — the runtime sibling is the envCheck option above.
passes
| option | type | default | description |
|---|---|---|---|
| nix-boundary | fatal | — | Shell-only expansions that break Nix (${`{#x}`} ${`{x[@]}`} ${`{x^^}`} …) must be ''${`{…}`}; a bare ${`{VAR}`} with no enclosing with fails Nix eval. Escaped ''${`{…}`}, with-scoped ${`{VAR}`}, and real Nix interpolations are skipped. |
| shellcheck | fatal | — | The bash body (function-wrapped, so local is valid) is shellcheck'd; @nix()/@sh:q() markers are expanded to placeholders first. SC2154/SC2153 (external env) and SC2329 (the wrapper) excluded by default; more via excludeShellChecks. |
| env | warn | — | Lists the external env each block requires (block-bound names subtracted). Never fatal. |
config (check builder)
| option | type | default | description |
|---|---|---|---|
| src | path | — | Tree (or file) to lint; dirs are walked for *.nix. |
| exclude | [ str ] | [ ] | find(1) path globs to skip. |
| passes | attrset | { all = true } | Toggle passes: { nix; shellcheck; envcheck; }. |
| excludeShellChecks | [ str ] | [ ] | Extra shellcheck codes to ignore. |
--fix
nix run nixx#shellint -- --fix ./ auto-fixes the nix-boundary both ways:
escapes a shell-only ${#x}/${x[@]} to ''${…}, and de-escapes an ''${VAR} back to ${VAR} (only inside a with). Edits in place (--dry-run shows a diff); each file is re-parsed and reverted if it isn't ERROR-free, so a mis-fix can't corrupt it. --fix is app-only — the check stays read-only.
for pkgs
| option | type | default | description |
|---|---|---|---|
| inputs.nixx.lib.for pkgs | attrset | — | lib + writers(pkgs) + pkgs in one set, for with inputs.nixx.lib.for pkgs; — the one canonical entry point. The single line of ceremony that un-prefixes the constructors and defers Nix's static undefined-variable check (any with makes the scope dynamic), so a bare ${`{VAR}`} survives the source-read. |