3.8 KiB
Nix Conventions
This document records local Nix style and design conventions. Use these conventions when changing this flake.
Names
Reserve lib for nixpkgs.lib
Use lib only for the base nixpkgs.lib value.
Do not use lib for project helper functions.
Use dotfiles.lib for pure project helpers passed through module arguments.
Use dotfiles.<name> for project assembly helpers passed through module arguments.
Avoid:
{
lib = dotfilesHelpers;
}
Use:
{
lib = nixpkgs.lib;
dotfiles.lib = dotfilesHelpers;
}
Imports
Use closed interfaces for helper imports
Use exact function argument patterns for helper imports with closed interfaces. A helper import has a closed interface when the caller owns the argument set. The imported file names each accepted attribute. It rejects stale or accidental extra attributes.
Avoid:
{
inputs,
lib,
self,
...
}:
Use:
{
inputs,
lib,
self,
}:
Pass import arguments explicitly
Pass only the values an imported helper file needs.
Bind lib to the base nixpkgs.lib value at the call site.
Use inherit for values that keep the same name.
Avoid:
import ./flake inputs
Use:
import ./flake {
inherit inputs self;
lib = nixpkgs.lib;
}
Keep NixOS module interfaces open
NixOS modules accept extra arguments because the module system owns their argument shape.
Name the arguments the module uses.
Keep ... so the module accepts standard module arguments and special arguments.
Avoid:
{
config,
lib,
pkgs,
registry,
}:
Use:
{
config,
lib,
pkgs,
registry,
...
}:
Use @ for forwarding wrappers
Use @ when a wrapper passes the complete argument set to another module function.
Name the arguments the wrapper uses before ....
Forward the bound argument set unchanged.
Avoid:
{ dotfiles, ... }:
dotfiles.guest { name = "sample"; } { inherit dotfiles; }
Use:
args@{ dotfiles, ... }:
dotfiles.guest { name = "sample"; } args
let Bindings
Use let for important intermediate values
Use a let binding when it names an important intermediate value.
Use a let binding when it avoids repeated complex expressions.
Do not use a let binding only to shorten a standard library call.
Avoid:
let
inherit (lib) mapAttrs;
in
mapAttrs f attrs
Use:
lib.mapAttrs f attrs
Standard Library Calls
Qualify standard library calls in flake assembly
Use qualified standard library calls inside flake assembly code.
Use lib.mapAttrs instead of inheriting mapAttrs from lib.
This keeps the source of helper functions visible.
Avoid:
let
inherit (lib) mapAttrs;
in
mapAttrs f attrs
Use:
lib.mapAttrs f attrs
Flake Assembly
Keep assembly code under flake/
Keep flake assembly code under flake/.
Keep flake.nix focused on inputs and public outputs.
The outputs function returns each public output as an attribute.
An external flake reads public outputs through inputs.<name>.<output>.
nix flake show also displays public outputs.
Avoid:
# flake.nix
let
mkHosts = hostsDir: ...;
in
{
nixosConfigurations = mkHosts (self + "/hosts");
}
Use:
# flake.nix
let
flakeAssembly = import ./flake {
inherit inputs self;
lib = nixpkgs.lib;
};
in
{
nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts");
}
Expose only supported flake outputs
Use public outputs for the supported interface of this flake.
Do not expose assembly helpers as public outputs unless an external consumer needs them.
Keep internal helpers in flake/ or dotfiles special arguments.
Avoid:
{
lib = flakeAssembly;
nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts");
}
Use:
{
nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts");
}