{ pkgs, mkLangs, withLangs, }: let system = pkgs.stdenv.hostPlatform.system; check = name: cond: pkgs.runCommand "ezshell-check-${name}" {} ( if cond then "touch $out" else throw "ezshell test failed: ${name}" ); hasOutPath = deriv: list: builtins.any (p: (p.outPath or null) == deriv.outPath) list; in { # mkLangs — single-pkgs core python-version-resolves = check "python-version-resolves" ( hasOutPath pkgs.python311 (mkLangs { languages.python = { enable = true; version = "3.11"; }; }).packages ); python-bad-version-throws = check "python-bad-version-throws" (!(builtins.tryEval ( builtins.deepSeq (mkLangs { languages.python = { enable = true; version = "9.99"; }; }).packages true )).success); disabled-language-contributes-nothing = check "disabled-language-contributes-nothing" ( (mkLangs {languages.python.enable = false;}).packages == [] ); rust-package-override-replaces-bundle = check "rust-package-override" ( hasOutPath pkgs.hello (mkLangs { languages.rust = { enable = true; package = pkgs.hello; }; }).packages ); both-languages-enabled-together = check "both-languages-enabled-together" ( let inherit (mkLangs { languages = { python.enable = true; rust.enable = true; }; }) packages ; in hasOutPath pkgs.python312 packages && hasOutPath pkgs.rust-analyzer packages ); no-languages-yields-empty-hooks = check "no-languages-yields-empty-hooks" ( (mkLangs {}).hookPresets == {} ); hooks-generated-for-rust = check "hooks-generated-for-rust" ( let h = (mkLangs {languages.rust.enable = true;}).hookPresets; in h ? rust-rustfmt && h ? rust-clippy ); # `hooks` is attrsOf types.hook: an explicit override replaces the whole # set (no per-field merge) — omitting clippy here disables just it. rust-hooks-override-replaces-the-whole-set = check "rust-hooks-override-replaces-the-whole-set" ( let h = (mkLangs { languages.rust = { enable = true; hooks.rustfmt = { entry = "/bin/true"; files = "\\.rs$"; stages = ["pre-commit"]; }; }; }).hookPresets; in h.rust-rustfmt.entry == "/bin/true" && h.rust-rustfmt.stages == ["pre-commit"] && !(h ? rust-clippy) ); # Consumers can add entirely new named hooks, not just tweak the built-in # ones — the whole point of using attrsOf types.hook over fixed sub-options. rust-hooks-support-custom-additions = check "rust-hooks-support-custom-additions" ( let h = (mkLangs { languages.rust = { enable = true; hooks.taplo = { entry = "${pkgs.taplo}/bin/taplo"; args = ["lint"]; files = "\\.toml$"; }; }; }).hookPresets; in h ? rust-taplo ); rust-hook-rejects-a-bad-shape = check "rust-hook-rejects-a-bad-shape" (!(builtins.tryEval ( builtins.deepSeq (mkLangs { languages.rust = { enable = true; hooks.rustfmt = {stages = ["pre-push"];}; # missing required `entry` }; }).hookPresets true )).success); nix-formatter-and-lsp-defaults = check "nix-formatter-and-lsp-defaults" ( let inherit (mkLangs {languages.nix.enable = true;}) packages; in hasOutPath pkgs.nixfmt packages && hasOutPath pkgs.nixd packages ); # `formatter`/`lsp` are `{enable; package;}` groups (not string presets) -- # overriding `package` directly is the only way to pick an alternate tool. nix-formatter-and-lsp-accept-package-overrides = check "nix-formatter-and-lsp-accept-package-overrides" ( let inherit (mkLangs { languages.nix = { enable = true; formatter.package = pkgs.alejandra; lsp.package = pkgs.nil; }; }) packages ; in hasOutPath pkgs.alejandra packages && hasOutPath pkgs.nil packages ); nix-formatter-disabled-excludes-it = check "nix-formatter-disabled-excludes-it" ( let inherit (mkLangs { languages.nix = { enable = true; formatter.enable = false; }; }) packages ; in !(hasOutPath pkgs.nixfmt packages) ); mklangs-exposes-resolved-options = check "mklangs-exposes-resolved-options" ( let result = mkLangs { languages.rust = { enable = true; package = pkgs.hello; }; }; in result.languages.rust.package.outPath == pkgs.hello.outPath ); mklangs-exposes-resolved-options-for-unmentioned-languages = check "mklangs-exposes-resolved-options-for-unmentioned-languages" ( let result = mkLangs {languages.rust.enable = true;}; in result.languages ? python && result.languages.python.enable == false ); # withLangs — flake-only multi-system composition sugar withlangs-devshell-has-language-packages = check "withlangs-devshell-has-language-packages" ( hasOutPath pkgs.python312 (withLangs { languages.${system}.python.enable = true; }).devShells.${ system }.default.nativeBuildInputs ); withlangs-composes-with-an-existing-devshell = check "withlangs-composes-with-an-existing-devshell" ( let base = { devShells.${system}.default = pkgs.mkShellNoCC {packages = [pkgs.hello];}; }; composed = withLangs (base // {languages.${system}.python.enable = true;}); inputs = composed.devShells.${system}.default.nativeBuildInputs; in hasOutPath pkgs.hello inputs && hasOutPath pkgs.python312 inputs ); withlangs-hooks-keyed-by-system = check "withlangs-hooks-keyed-by-system" ( (withLangs { languages.${system}.rust.enable = true; }).hooks.${ system } ? rust-rustfmt ); withlangs-preserves-caller-supplied-nixhooks-settings = check "withlangs-preserves-caller-supplied-nixhooks-settings" ( let result = withLangs { languages.${system}.rust.enable = true; hooks.${system}.settings.parallel = true; }; in result.hooks.${system}.settings.parallel == true && result.hooks.${system} ? rust-rustfmt ); withlangs-exposes-resolved-options-keyed-by-system = check "withlangs-exposes-resolved-options-keyed-by-system" ( let result = withLangs { languages.${system}.rust = { enable = true; package = pkgs.hello; }; }; in result.languages.${system}.rust.package.outPath == pkgs.hello.outPath ); }