vic/with-inputs
{ "defaultBranch": "main", "description": "A flake-inputs adapter for Nix projects that don't use `flake.nix`.", "fullName": "vic/with-inputs", "homepage": "https://dendritic.oeiuwq.com/ecosystem/with-inputs/", "language": "Nix", "name": "with-inputs", "pushedAt": "2026-03-19T00:59:58Z", "stargazersCount": 6, "updatedAt": "2026-03-19T01:00:02Z", "url": "https://github.com/vic/with-inputs"}with-inputs - A flake-inputs adapter for Nix projects that don’t use flake.nix.
Section titled “with-inputs - A flake-inputs adapter for Nix projects that don’t use flake.nix.”with-inputs and vic’s dendritic libs made for you with Love++ and AI—. If you like my work, consider sponsoring
with-inputs.nix
Section titled “with-inputs.nix”Provides exactly the same inputs resolution experience as real Nix flakes —
follows, nested follows, per-sub-input overrides, inputs.self, and
dependency introspection — using pre-fetched sources from npins,
local checkouts, or any other source.
This library is not an inputs lock mechanism nor an inputs fetcher, for those we have plenty of options: npins, unflake, nixlock, nixtamal.
Examples with different Nix pinning tools
Section titled “Examples with different Nix pinning tools”Download our default.nix into your project ./with-inputs.nix.
curl https://raw.githubusercontent.com/vic/with-inputs/refs/heads/main/default.nix -o with-inputs.nixOr use npins or builtins.fetchTarball with a fixed revision of it.
npins add github vic with-inputslet sources = import ./npins; # example with npins. use any other sources. with-inputs = import sources.with-inputs sources { # keep reading for follows and local inputs };
outputs = inputs: { }; # your flake-like outputs functioninwith-inputs outputsFollows and local checkout overrides
Section titled “Follows and local checkout overrides”The second argument to with-inputs is an attribute set that
can be used to drive input resolution, for example to use local
checkout or to specify flake-like follows.
See [tests.nix]!(./tests.nix) and vic/vix:follows.nix for usage examples.
{ # Local checkout — loaded as a flake if a flake.nix is present mylib.outPath = ./mylib;
# Local checkout with sub-input overrides applied when loading its flake.nix someLib = { outPath = ./someLib; inputs.nixpkgs.follows = "nixpkgs"; };
# Direct import — value used as-is (function, module result, attrset, etc) systems = import ./systems.nix;
# Top-level follows: alias one input to another nixpkgs-stable.follows = "nixpkgs";
# Nested follows: traverse sub-inputs something.follows = "a/b/c"; # → allInputs.a.inputs.b.inputs.c
# Empty follows: intentionally disconnect an input unwanted.follows = "";
# Per-sub-input follows (mirrors flake.nix `inputs.foo.inputs.bar.follows`) home-manager.inputs.nixpkgs.follows = "nixpkgs"; disko.inputs.nixpkgs.follows = "nixpkgs";
# Combined: keep the source, override some of its sub-inputs someFlake = { inputs.nixpkgs.follows = "nixpkgs"; inputs.utils.follows = "flake-utils"; };
# Takes the original sources.otherFlake and avoids flake call otherFlake = source: source // { flake = false; };}This second argument can also be a function resolvedInputs -> flakeInputs, this is
useful for example to shim dependencies like systems or flake-utils [example].
self shape
Section titled “self shape”All standard inputs.self.* patterns work:
inputs.self # the assembled selfinputs.self.inputs # resolved inputsinputs.self.inputs.self # circular, lazy-safeinputs.self.inputs.nixpkgs # any resolved inputinputs.self.outputs # raw outputs attrsetinputs.self.nixosConfigurations # shorthand for inputs.self.outputs.nixosConfigurationsResolved flake input shape
Section titled “Resolved flake input shape”Every source with a flake.nix is fully resolved into the standard flake shape:
inputs.nixpkgs.outPath # store / local pathinputs.nixpkgs.sourceInfo # raw sourceInfo from sourcesinputs.nixpkgs._type # "flake"inputs.nixpkgs.inputs # nixpkgs' own resolved sub-inputsinputs.nixpkgs.outputs # nixpkgs' outputs attrset (explicit)inputs.nixpkgs.lib # shorthand — same as inputs.nixpkgs.outputs.libDependency introspection works just like in flake-parts:
inputs.someFlake.inputs.nixpkgs # someFlake's resolved nixpkgsinputs.someFlake.inputs.nixpkgs.lib # and its lib, etc.Unresolvable follows
Section titled “Unresolvable follows”When a follows target doesn’t exist in resolved inputs, the entry becomes
null. Sub-flakes that declare that input as required will have their outputs
call skipped (outputs stays {}), preventing evaluation errors — exactly like
real flakes when a dependency is absent.
Input declaration quick reference
Section titled “Input declaration quick reference”| Input declaration | Meaning |
|---|---|
foo.outPath = ./path; | Local checkout, loaded as flake if flake.nix present |
foo = { outPath = ./path; inputs.dep.follows = "x"; }; | Local checkout with sub-input overrides |
foo = import ./path; | Direct value, used as-is |
foo = pinned-source; | Direct value from npins or similar |
b.follows = "a"; | Alias to allInputs.a |
b.follows = "a/x/y"; | Nested alias via .inputs. chain |
b.follows = ""; | Empty — resolves to {} |
a.inputs.b.follows = "x"; | Override sub-input b of source a |
a.inputs.b.follows = "x/y"; | Override with nested follows |
a = { inputs.b.follows = "x"; inputs.c.follows = "y"; }; | Meta-spec: keep source, override several sub-inputs |
A value is treated as a spec (not a direct value) when its only keys are
follows and/or inputs, and every inputs.* value is a { follows = …; }.
Anything with outPath, lib, packages, _type, etc. is a direct value.
Contributing
Section titled “Contributing”PR are welcome, make sure to run tests:
nix-unit tests.nix