From 285031a8014c72b6bc9b4e4c80d1dbe9fe874b24 Mon Sep 17 00:00:00 2001 From: Victor Borja Date: Tue, 1 Jul 2025 12:54:03 -0600 Subject: [PATCH 1/2] feat: initFilter allows looking other file types, ignore other than /_. --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++-- checkmate.nix | 39 ++++++++++++++++++++++++++++++++------- default.nix | 13 ++++++++++--- 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9013092..cd1a4ac 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ > Helper functions for import of [Nixpkgs module system](https://nix.dev/tutorials/module-system/) modules under a directory recursively -Module class agnostic; can be used for NixOS, nix-darwin, home-manager, flake-parts, NixVim. +- Flake callable; Easy to use, intuitive for the most common use case: `inputs.import-tree ./modules` +- Module class agnostic; can be used for NixOS, nix-darwin, home-manager, flake-parts, NixVim. +- Can be used outside flakes as a dependencies-free lib; Just import our `./default.nix`. +- Can be used to list other file types, not just `.nix`. See `.initFilter`, `.files` API. +- Extensible API. import-tree objects are customizable. See `.addAPI`. ## Quick Usage (with flake-parts) @@ -29,7 +33,9 @@ you can use the import-tree API (read below for more). ## Ignored files -Paths that have a component that begins with an underscore are ignored. +By default, paths having a component that begins with an underscore (`/_`) are ignored. + +This can be changed by using `.initFilter` API.
@@ -177,6 +183,8 @@ Or, in a simpler but less readable way: (import-tree.filter (lib.hasInfix ".mod.")).filter (lib.hasSuffix "default.nix") ./some-dir ``` +See also `import-tree.initFilter`. + ### `import-tree.match` and `import-tree.matchNot` `match` takes a regular expression. The regex should match the full path for the path to be selected. Matching is done with `builtins.match`. @@ -297,6 +305,37 @@ import-tree.leafs Returns a fresh import-tree with empty state. If you previously had any path, lib, filter, etc, you might need to set them on the new empty tree. +### `import-tree.initFilter` + +*Replaces* the initial filter which defaults to: Include files with `.nix` suffix and not having `/_` infix. + +_NOTE_: initFilter is non-accumulating and is the *first* filter to run before those accumulated via `filter`/`match`. + +You can use this to make import-tree scan for other file types or change the ignore convention. + +```nix +# import-tree.initFilter : (path -> bool) -> import-tree + +import-tree.initFilter (p: lib.hasSuffix ".nix" p && !lib.hasInfix "/ignored/") # nix files not inside /ignored/ +import-tree.initFilter (lib.hasSuffix ".md") # scan for .md files everywhere, nothing ignored. +``` + +### `import-tree.files` + +A shorthand for `import-tree.leafs.result`. Returns a list of matching files. + +This can be used when you dont want to import the tree, but just get a list of files from it. + +Useful for listing files other than `.nix`, for example, for passing all `.js` files to a minifier: + +_TIP_: remember to use `withLib` when *not* using import-tree as a module import. + +```nix +# import-tree.files : [ ] + +((import-tree.withLib lib).initFilter (lib.hasSuffix ".js")).files # => list of all .js files +``` + ### `import-tree.result` Exactly the same as calling the import-tree object with an empty list `[ ]`. diff --git a/checkmate.nix b/checkmate.nix index f22d5d9..557607a 100644 --- a/checkmate.nix +++ b/checkmate.nix @@ -92,12 +92,12 @@ in }; addPath."test `addPath` prepends a path to filter" = { - expr = (lit.addPath ./tree/x).leafs.result; + expr = (lit.addPath ./tree/x).files; expected = [ ./tree/x/y.nix ]; }; addPath."test `addPath` can be called multiple times" = { - expr = ((lit.addPath ./tree/x).addPath ./tree/a/b).leafs.result; + expr = ((lit.addPath ./tree/x).addPath ./tree/a/b).files; expected = [ ./tree/x/y.nix ./tree/a/b/b_a.nix @@ -106,31 +106,56 @@ in }; addPath."test `addPath` identity" = { - expr = ((lit.addPath ./tree/x).addPath ./tree/a/b).leafs.result; + expr = ((lit.addPath ./tree/x).addPath ./tree/a/b).files; expected = lit.leafs [ ./tree/x ./tree/a/b ]; }; - reset."test `new` returns a clear state" = { + new."test `new` returns a clear state" = { expr = lib.pipe lit [ (i: i.addPath ./tree/x) (i: i.addPath ./tree/a/b) (i: i.new) (i: i.addPath ./tree/modules/hello-world) (i: i.withLib lib) - (i: i.leafs.result) + (i: i.files) ]; expected = [ ./tree/modules/hello-world/mod.nix ]; }; + initFilter."test can change the initial filter to look for other file types" = { + expr = (lit.initFilter (p: lib.hasSuffix ".txt" p)).leafs [ ./tree/a ]; + expected = [ ./tree/a/a.txt ]; + }; + + initFilter."test initf does filter non-paths" = { + expr = + let + mod = (it.initFilter (x: !(x ? config.boom))) [ + { + options.hello = lib.mkOption { + default = "world"; + type = lib.types.str; + }; + } + { + config.boom = "boom"; + } + ]; + res = lib.modules.evalModules { modules = [ mod ]; }; + in + res.config.hello; + expected = "world"; + }; + addAPI."test extends the API available on an import-tree object" = { expr = let extended = lit.addAPI { helloOption = self: self.addPath ./tree/modules/hello-option; }; in - extended.helloOption.leafs.result; + extended.helloOption.files; expected = [ ./tree/modules/hello-option/mod.nix ]; }; @@ -139,7 +164,7 @@ in let first = lit.addAPI { helloOption = self: self.addPath ./tree/modules/hello-option; }; second = first.addAPI { helloWorld = self: self.addPath ./tree/modules/hello-world; }; - extended = second.addAPI { res = self: self.helloOption.leafs.result; }; + extended = second.addAPI { res = self: self.helloOption.files; }; in extended.res; expected = [ ./tree/modules/hello-option/mod.nix ]; diff --git a/default.nix b/default.nix index 797955c..e8ecd1a 100644 --- a/default.nix +++ b/default.nix @@ -3,6 +3,7 @@ let { lib ? null, pipef ? null, + initf ? null, filterf, mapf, paths, @@ -28,7 +29,7 @@ let leafs = lib: root: let - initialFilter = andNot (lib.hasInfix "/_") (lib.hasSuffix ".nix"); + treeFiles = t: (t.withLib lib).files; listFilesRecursive = x: if isImportTree x then @@ -39,9 +40,11 @@ let lib.filesystem.listFilesRecursive x else [ x ]; - treeFiles = t: (t.withLib lib).leafs.result; + nixFilter = andNot (lib.hasInfix "/_") (lib.hasSuffix ".nix"); + initialFilter = if initf != null then initf else nixFilter; pathFilter = compose (and filterf initialFilter) toString; - filter = x: if isPathLike x then pathFilter x else filterf x; + otherFilter = and filterf (if initf != null then initf else (_: true)); + filter = x: if isPathLike x then pathFilter x else otherFilter x; in lib.pipe [ paths root ] @@ -118,12 +121,16 @@ let # Configuration updates (non-accumulating) withLib = lib: mergeAttrs { inherit lib; }; + initFilter = initf: mergeAttrs { inherit initf; }; pipeTo = pipef: mergeAttrs { inherit pipef; }; leafs = mergeAttrs { pipef = (i: i); }; # Applies empty (for already path-configured trees) result = (self f) [ ]; + # Return a list of all filtered files. + files = (self f).leafs.result; + # returns the original empty state new = callable; }; From 2b371b2c1a442f7803211fc305a5cebeaf4b5ed5 Mon Sep 17 00:00:00 2001 From: Victor Borja Date: Tue, 1 Jul 2025 19:45:50 +0000 Subject: [PATCH 2/2] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cd1a4ac..ac5c0ab 100644 --- a/README.md +++ b/README.md @@ -324,7 +324,7 @@ import-tree.initFilter (lib.hasSuffix ".md") # scan for .md files everywhere, n A shorthand for `import-tree.leafs.result`. Returns a list of matching files. -This can be used when you dont want to import the tree, but just get a list of files from it. +This can be used when you don't want to import the tree, but just get a list of files from it. Useful for listing files other than `.nix`, for example, for passing all `.js` files to a minifier: