From 84677166dfce8670a6bb592ab4d0fb597aee1bda Mon Sep 17 00:00:00 2001 From: zimbatm Date: Fri, 20 May 2022 16:02:45 +0200 Subject: [PATCH] introduce treefmt.withConfig This is useful if you want to configure treefmt with nix, and precisely pass all the commands from nixpkgs. Co-authored-by: Sridhar Ratnakumar --- default.nix | 26 ++++++++++- flake.nix | 15 +++++- module-options.nix | 113 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 module-options.nix diff --git a/default.nix b/default.nix index 050aba6..a2d59d6 100644 --- a/default.nix +++ b/default.nix @@ -13,6 +13,18 @@ let cargoToml = with builtins; (fromTOML (readFile ./Cargo.toml)); + # Use the Nix module system to validate the treefmt config file format. + evalModule = config: + lib.evalModules { + modules = [ + { + _module.args = { inherit nixpkgs lib treefmt; }; + } + ./module-options.nix + config + ]; + }; + # What is used when invoking `nix run github:numtide/treefmt` treefmt = rustPackages.rustPlatform.buildRustPackage { inherit (cargoToml.package) name version; @@ -34,6 +46,12 @@ let cargoLock.lockFile = ./Cargo.lock; meta.description = "one CLI to format the code tree"; + + passthru.withConfig = config: + let + mod = evalModule config; + in + mod.config.build.wrapper; }; # Add all the dependencies of treefmt, plus more build tools @@ -68,7 +86,13 @@ let }); in { - inherit treefmt devShell; + inherit treefmt devShell evalModule; + + # module that generates and wraps the treefmt config with Nix + module = ./module-options.nix; + + # reduce a bit of repetition + inherit (treefmt.passthru) withConfig; # A collection of packages for the project docs = nixpkgs.callPackage ./docs { }; diff --git a/flake.nix b/flake.nix index 570ccd2..878f57b 100644 --- a/flake.nix +++ b/flake.nix @@ -18,9 +18,22 @@ }; in { - inherit packages; + # This contains a mix of packages, modules, ... + legacyPackages = packages; devShells.default = packages.devShell; + + # In Nix 2.8 you can run `nix fmt` to format this whole repo. + # + # Because we load the treefmt.toml and don't define links to the + # packages in Nix, the formatter has to run inside of `nix develop` + # to have the various tools on the PATH. + # + # It also assumes that the project root has a flake.nix (override this by setting `projectRootFile`). + formatter = packages.treefmt.withConfig { + settings = nixpkgs.lib.importTOML ./treefmt.toml; + projectRootFile = "flake.nix"; + }; }; }; } diff --git a/module-options.nix b/module-options.nix new file mode 100644 index 0000000..b41d85c --- /dev/null +++ b/module-options.nix @@ -0,0 +1,113 @@ +{ lib, nixpkgs, treefmt, ... }: +let + # A new kind of option type that calls lib.getExe on derivations + exeType = lib.mkOptionType { + name = "exe"; + description = "Path to executable"; + check = (x: lib.isString x || builtins.isPath x || lib.isDerivation x); + merge = loc: defs: + let res = lib.mergeOneOption loc defs; in + if lib.isString res || builtins.isPath res then + "${res}" + else + lib.getExe res; + }; + + # The schema of the treefmt.toml data structure. + configSchema = with lib; { + excludes = mkOption { + description = "A global list of paths to exclude. Supports glob."; + type = types.listOf types.str; + default = [ ]; + example = [ "./node_modules/**" ]; + }; + + formatter = mkOption { + type = types.attrsOf (types.submodule [{ + options = { + command = mkOption { + description = "Executable obeying the treefmt formatter spec"; + type = exeType; + }; + + options = mkOption { + description = "List of arguments to pass to the command"; + type = types.listOf types.str; + default = [ ]; + }; + + includes = mkOption { + description = "List of files to include for formatting. Supports globbing."; + type = types.listOf types.str; + }; + + excludes = mkOption { + description = "List of files to exclude for formatting. Supports globbing. Takes precedence over the includes."; + type = types.listOf types.str; + default = [ ]; + }; + }; + }]); + default = { }; + description = "Set of formatters to use"; + }; + }; + + configFormat = nixpkgs.formats.toml { }; +in +{ + # Schema + options = { + settings = configSchema; + + package = lib.mkOption { + description = "Package wrapped in the build.wrapper output"; + type = lib.types.package; + default = treefmt; + }; + + projectRootFile = lib.mkOption { + description = '' + File to look for to determine the root of the project in the + build.wrapper. + ''; + example = "flake.nix"; + }; + + # Outputs + build = { + configFile = lib.mkOption { + description = '' + Contains the generated config file derived from the settings. + ''; + type = lib.types.path; + }; + wrapper = lib.mkOption { + description = '' + The treefmt package, wrapped with the config file. + ''; + type = lib.types.package; + }; + }; + }; + # Config + config.build = { + configFile = configFormat.generate "treefmt.toml" config.settings; + + wrapper = nixpkgs.writeShellScriptBin "treefmt" '' + find_up() ( + ancestors=() + while [[ ! -f "$1" ]]; do + ancestors+=("$PWD") + if [[ $PWD == / ]]; then + echo "ERROR: Unable to locate the projectRootFile ($1) in any of: ''${ancestors[*]@Q}" >&2 + exit 1 + fi + cd .. + done + ) + tree_root=$(find_up "${config.projectRootFile}") + exec ${config.package}/bin/treefmt --config-file ${config.build.configFile} "$@" --tree-root "$tree_root" + ''; + }; +};