From 9617fcc6b7d0c1fc9d93b5f14a7afc41616355ff Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 1 Jul 2025 23:26:08 +0000
Subject: [PATCH 1/2] Initial plan
From d91f461b303af3116fab4936c94341320aac5e2e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 1 Jul 2025 23:32:03 +0000
Subject: [PATCH 2/2] Comprehensive rewrite of README.md with improved
structure and accessibility
Co-authored-by: vic <331+vic@users.noreply.github.com>
---
README.md | 610 ++++++++++++++++++++++++++++--------------------------
1 file changed, 313 insertions(+), 297 deletions(-)
diff --git a/README.md b/README.md
index a5addf4..566e941 100644
--- a/README.md
+++ b/README.md
@@ -1,446 +1,462 @@
-# 🌲🌴 import-tree 🎄🌳
+# 🌲 import-tree 🌳
-> Helper functions for import of [Nixpkgs module system](https://nix.dev/tutorials/module-system/) modules under a directory recursively
+[](https://github.com/vic/import-tree/actions/workflows/test.yml)
+[](https://opensource.org/licenses/Apache-2.0)
-- 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`.
+> **Powerful and extensible helper for importing [Nixpkgs module system](https://nix.dev/tutorials/module-system/) modules from directory trees**
-## Quick Usage (with flake-parts)
+**import-tree** simplifies the management of Nix module collections by automatically discovering and importing `.nix` files from directory structures. Whether you're organizing NixOS configurations, home-manager setups, or flake-parts modules, import-tree provides an intuitive and extensible API to streamline your module imports.
-This example shows how to load all nix files inside `./modules`, following the
-[Dendritic Pattern](https://github.com/mightyiam/dendritic)
+Perfect for implementing the [Dendritic Pattern](https://github.com/mightyiam/dendritic) where each file represents a discrete, composable module, making your Nix configurations more modular and maintainable.
-```nix
-{
- inputs.import-tree.url = "github:vic/import-tree";
- inputs.flake-parts.url = "github:hercules-ci/flake-parts";
-
- outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules);
-}
-```
+## Features at a Glance
-## Quick Usage (outside a modules evaluation)
-
-If you want to get a list of nix files programmatically outside of a modules evaluation,
-you can use the import-tree API (read below for more).
-
-```nix
-(import-tree.withLib pkgs.lib).leafs ./modules # => list of .nix files
-```
+### 🚀 **Easy to Use**
+- **One-liner imports**: `inputs.import-tree ./modules` for the most common use case
+- **Flake-ready**: Works seamlessly with modern Nix flakes
+- **Zero dependencies**: Can be used outside flakes by importing `./default.nix`
-## Ignored files
+### 🎯 **Universal Compatibility**
+- **Module system agnostic**: Works with NixOS, nix-darwin, home-manager, flake-parts, NixVim
+- **Path flexible**: Accepts files, directories, or nested lists of paths
+- **Mixed usage**: Can import other file types beyond `.nix` files
-By default, paths having a component that begins with an underscore (`/_`) are ignored.
+### 🔧 **Highly Customizable**
+- **Extensible API**: Add custom methods with `.addAPI`
+- **Smart filtering**: Built-in filters with customizable patterns
+- **Transformation support**: Map functions over discovered files
+- **Ignore patterns**: Sensible defaults with full customization
-This can be changed by using `.initFilter` API.
+### 📦 **Library-Friendly**
+- **Pre-configured trees**: Library authors can ship ready-to-use configurations
+- **Community conventions**: Support for domain-specific naming and organization patterns
+- **API composition**: Chain and combine multiple import-tree instances
-
-
+## Quick Start
-## API usage
+### For Flake Users
-The following goes recursively through `./modules` and imports all `.nix` files.
+The simplest way to use import-tree with flake-parts to import all modules from a directory:
```nix
-# Usage as part of any nix module system.
-{config, ...} {
- imports = [ (import-tree ./modules) ];
+{
+ inputs = {
+ import-tree.url = "github:vic/import-tree";
+ flake-parts.url = "github:hercules-ci/flake-parts";
+ };
+
+ outputs = inputs:
+ inputs.flake-parts.lib.mkFlake { inherit inputs; }
+ # This imports all .nix files from ./modules recursively
+ (inputs.import-tree ./modules);
}
```
-For more advanced usage, `import-tree` can be configured via its extensible API.
-
-
+### For Non-Flake Users
-## Obtaining the API
-
-When used as a flake, the flake outputs attrset is the primary callable.
-Otherwise, importing the `default.nix` that is at the root of this repository will evaluate into the same attrset.
-This callable attrset is referred to as `import-tree` in this documentation.
-
-## `import-tree`
-
-Takes a single argument: path or deeply nested list of path.
-Returns a module that imports the discovered files.
-For example, given the following file tree:
-
-```
-default.nix
-modules/
- a.nix
- subdir/
- b.nix
-```
-
-The following
+You can use import-tree in traditional Nix setups by importing the library directly:
```nix
-{lib, config, ...} {
+let
+ # Import the library
+ import-tree = import (fetchGit "https://github.com/vic/import-tree");
+
+ # Get list of module files (requires lib for non-module usage)
+ moduleFiles = (import-tree.withLib pkgs.lib).files ./modules;
+in
+{
+ # Use in any module system
imports = [ (import-tree ./modules) ];
}
```
-Is similar to
+### Basic Usage in Module Systems
+
+Use import-tree anywhere you need to import modules:
```nix
-{lib, config, ...} {
+# NixOS configuration
+{ config, pkgs, ... }: {
imports = [
- {
- imports = [
- ./modules/a.nix
- ./modules/subdir/b.nix
- ];
- }
+ # Import all .nix files from ./modules
+ (import-tree ./modules)
+
+ # Can also import from multiple directories
+ (import-tree [ ./modules ./extra-modules ])
];
}
```
-If given a deeply nested list of paths the list will be flattened and results concatenated.
-The following is valid usage:
+## Understanding the Default Behavior
-```nix
-{lib, config, ...} {
- imports = [ (import-tree [./a [./b]]) ];
-}
-```
+### File Discovery
-Other import-tree objects can also be given as arguments (or in lists) as if they were paths.
+By default, import-tree:
+- **Recursively scans** directories for `.nix` files
+- **Ignores paths** containing components that start with underscore (`/_`)
+- **Returns a module** that imports all discovered files
-As an special case, when the single argument given to an `import-tree` object is an
-attribute-set containing an `options` attribute, the `import-tree` object
-assumes it is being evaluated as a module. This way, a pre-configured `import-tree` can
-also be used directly in a list of module imports.
+### Default Ignore Pattern
-This is useful for authors exposing pre-configured `import-tree`s that users can directly
-add to their import list or continue configuring themselves using its API.
+Files and directories starting with underscore are ignored by default:
+- `_private.nix` ❌ (ignored)
+- `modules/_internal/config.nix` ❌ (ignored)
+- `modules/public.nix` ✅ (included)
-```nix
-let
- # imagine this configured tree comes from some author's flake or library.
- # library author can extend an import-tree with custom API methods
- # according to the library's directory and file naming conventions.
- configured-tree = import-tree.addAPI {
- # the knowledge of where modules are located inside the library structure
- # or which filters/regexes/transformations to apply are abstracted
- # from the user by the author providing a meaningful API.
- maximal = self: self.addPath ./modules;
- minimal = self: self.maximal.filter (lib.hasInfix "minimal");
- };
-in {
- # the library user can directly import or further configure an import-tree.
- imports = [ configured-tree.minimal ];
-}
-```
+This convention allows you to keep private/internal files alongside public modules without accidentally importing them.
-## Configurable behavior
+## Advanced Usage & API
-`import-tree` objects with custom behavior can be obtained using a builder pattern.
-For example:
+### Filtering Files
+
+Control which files are imported using the `.filter` method:
```nix
+# Only import files containing "feature" in their path
+import-tree.filter (lib.hasInfix "feature") ./modules
+
+# Chain multiple filters (all must match)
lib.pipe import-tree [
- (i: i.map lib.traceVal) # trace all paths. useful for debugging what is being imported.
- (i: i.filter (lib.hasInfix ".mod.")) # filter nix files by some predicate
- (i: i ./modules) # finally, call the configured import-tree with a path
+ (i: i.filter (lib.hasInfix "desktop"))
+ (i: i.filter (lib.hasSuffix "default.nix"))
+ (i: i ./modules)
]
```
-Here is a simpler but less readable equivalent:
+### Using Regular Expressions
+
+Use `.match` for more complex pattern matching:
```nix
-((import-tree.map lib.traceVal).filter (lib.hasInfix ".mod.")) ./modules
+# Import files matching specific patterns
+import-tree.match ".*/desktop@(gnome|kde)\.nix" ./modules
```
-### `import-tree.filter` and `import-tree.filterNot`
-
-`filter` takes a predicate function `path -> bool`. Only paths for which the filter returns `true` are selected:
+### Transforming Files
-> \[!NOTE\]
-> Only files with suffix `.nix` are candidates.
+Use `.map` to transform paths before importing:
```nix
-# import-tree.filter : (path -> bool) -> import-tree
-
-import-tree.filter (lib.hasInfix ".mod.") ./some-dir
+# Add debugging information to each import
+import-tree.map (path: {
+ imports = [ path ];
+ # Track which files were automatically imported
+ _importedPaths = [ (toString path) ];
+}) ./modules
```
-`filter` can be applied multiple times, in which case only the files matching _all_ filters will be selected:
+### Working with File Lists
+
+Get the list of discovered files without importing them:
```nix
-lib.pipe import-tree [
- (i: i.filter (lib.hasInfix ".mod."))
- (i: i.filter (lib.hasSuffix "default.nix"))
- (i: i ./some-dir)
-]
+# Get list of .nix files (remember to use withLib for non-module contexts)
+nixFiles = (import-tree.withLib lib).files ./modules;
+
+# Process non-Nix files
+jsFiles = lib.pipe import-tree [
+ (i: i.initFilter (lib.hasSuffix ".js")) # Look for .js files
+ (i: i.withLib lib) # Required for .files
+ (i: i.files ./src) # Get the file list
+];
```
-Or, in a simpler but less readable way:
+### Pre-configuring Paths
+
+Use `.addPath` to create import-tree instances with predefined search paths:
```nix
-(import-tree.filter (lib.hasInfix ".mod.")).filter (lib.hasSuffix "default.nix") ./some-dir
-```
+# Create a configured tree
+myTree = import-tree.addPath ./modules;
-See also `import-tree.initFilter`.
+# Use it with empty arguments or additional paths
+config = {
+ imports = [
+ myTree.result # Same as: myTree []
+ (myTree ./extra-modules) # Includes both ./modules and ./extra-modules
+ ];
+};
+```
-### `import-tree.match` and `import-tree.matchNot`
+### Custom API Extensions
-`match` takes a regular expression. The regex should match the full path for the path to be selected. Matching is done with `builtins.match`.
+Create domain-specific APIs using `.addAPI`:
```nix
-# import-tree.match : regex -> import-tree
-
-import-tree.match ".*/[a-z]+@(foo|bar)\.nix" ./some-dir
+# Define a custom API for feature management
+featureTree = import-tree.addAPI {
+ # Method to get all available features
+ all = self: self.addPath ./features;
+
+ # Method to enable specific features
+ enable = self: featureName:
+ self.all.filter (lib.hasInfix "+${featureName}");
+
+ # Method for minimal feature set
+ minimal = self: self.enable "minimal";
+
+ # Method for desktop features
+ desktop = self: self.enable "desktop";
+};
+
+# Usage:
+{
+ imports = [
+ featureTree.minimal # Only minimal features
+ featureTree.desktop # Only desktop features
+ ];
+}
```
-`match` can be applied multiple times, in which case only the paths matching _all_ regex patterns will be selected, and can be combined with any number of `filter`, in any order.
+## Why Use import-tree?
-### `import-tree.map`
+### The Dendritic Pattern
-`map` can be used to transform each path by providing a function.
+import-tree was inspired by the [Dendritic Pattern](https://github.com/mightyiam/dendritic), a modular approach where each file represents a focused, composable module. This pattern offers several advantages:
-e.g. to convert the path into a module explicitly:
+- **Better organization**: Each file has a single responsibility
+- **Easier maintenance**: Changes are isolated to specific files
+- **Improved reusability**: Modules can be easily shared across configurations
+- **Cleaner git history**: Changes are more granular and easier to track
-```nix
-# import-tree.map : (path -> any) -> import-tree
+Learn more from:
+- [@mightyiam's original post](https://discourse.nixos.org/t/pattern-each-file-is-a-flake-parts-module/61271)
+- [@drupol's practical experience](https://not-a-number.io/2025/refactoring-my-infrastructure-as-code-configurations/)
+- [@vic's implementation insights](https://discourse.nixos.org/t/how-do-you-structure-your-nixos-configs/65851/8)
-import-tree.map (path: {
- imports = [ path ];
- # assuming such an option is declared
- automaticallyImportedPaths = [ path ];
-})
-```
+### Library Distribution & Community Configurations
+
+The extensible API enables library authors to create domain-specific import-tree configurations, similar to popular editor distributions (like Spacemacs or LazyVim).
-`map` can be applied multiple times, composing the transformations:
+#### Example: Editor Configuration Distribution
```nix
-lib.pipe import-tree [
- (i: i.map (lib.removeSuffix ".nix"))
- (i: i.map builtins.stringLength)
-] ./some-dir
+# editor-distro's flake output
+{ inputs, lib, ... }:
+let
+ # Pre-configured import-tree with editor-specific API
+ modules-tree = lib.pipe inputs.import-tree [
+ (i: i.addPath ./modules)
+ (i: i.addAPI {
+ # Language-specific features
+ ruby = self: self.filter (lib.hasInfix "+ruby");
+ python = self: self.filter (lib.hasInfix "+python");
+
+ # Editor preferences
+ vim = self: self.filter (lib.hasInfix "+vim");
+ emacs = self: self.filter (lib.hasInfix "+emacs");
+
+ # Feature toggles
+ minimal = self: self.filterNot (lib.hasInfix "+bloat");
+ ai-assist = self: self.filter (lib.hasInfix "+copilot");
+ })
+ ];
+in {
+ lib = { inherit modules-tree; };
+}
```
-The above example first removes the `.nix` suffix from all selected paths, then takes their lengths.
-
-Or, in a simpler but less readable way:
+#### Using Distributed Configurations
```nix
-((import-tree.map (lib.removeSuffix ".nix")).map builtins.stringLength) ./some-dir
+# Consumer flake
+{ inputs, ... }:
+let
+ editor = inputs.editor-distro.lib.modules-tree;
+in {
+ imports = [
+ # Compose features declaratively
+ (editor.vim.ruby.minimal)
+
+ # Mix and match as needed
+ (editor.python.ai-assist)
+ ];
+}
```
-`map` can be combined with any number of `filter` and `match` calls, in any order, but the (composed) transformation is applied _after_ the filters, and only to the paths that match all of them.
+This approach enables community-driven configuration sharing while maintaining the flexibility to customize for specific needs.
+
+## API Reference
-### `import-tree.addPath`
+### Core Methods
-`addPath` can be used to prepend paths to be filter as a setup for import-tree.
-This function can be applied multiple times.
+#### `import-tree `
+**Primary function**: Takes paths and returns a module that imports discovered files.
```nix
-# import-tree.addPath : (path_or_list_of_paths) -> import-tree
+# Single path
+import-tree ./modules
-# Both of these result in the same imported files.
-# however, the first adds ./vendor as a *pre-configured* path.
-# and the final user can supply ./modules or [] empty.
-(import-tree.addPath ./vendor) ./modules
-import-tree [./vendor ./modules]
-```
+# Multiple paths
+import-tree [ ./modules ./features ]
-### `import-tree.addAPI`
+# Nested paths (flattened automatically)
+import-tree [ ./modules [ ./features ./plugins ] ]
+```
-`addAPI` extends the current import-tree object with new methods.
-The API is cumulative, meaning that this function can be called multiple times.
+### Configuration Methods
-`addAPI` takes an attribute set of functions taking a single argument:
-`self` which is the current import-tree object.
+#### `.filter `
+**Filter files** by a predicate function `path -> bool`.
```nix
-# import-tree.addAPI : api-attr-set -> import-tree
-
-import-tree.addAPI {
- maximal = self: self.addPath ./modules;
- feature = self: featureName: self.maximal.filter (lib.hasInfix feature);
- minimal = self: self.feature "minimal";
-}
+import-tree.filter (lib.hasInfix "desktop") ./modules
```
-on the previous API, users can call `import-tree.feature "+vim"` or `import-tree.minimal`, etc.
+#### `.filterNot `
+**Exclude files** matching the predicate.
-### `import-tree.withLib`
+```nix
+import-tree.filterNot (lib.hasInfix "experimental") ./modules
+```
-> \[!NOTE\]
-> `withLib` is required prior to invocation of any of `.leafs` or `.pipeTo`.
-> Because with the use of those functions the implementation does not have access to a `lib` that is provided as a module argument.
+#### `.match `
+**Filter by regex** pattern (uses `builtins.match`).
```nix
-# import-tree.withLib : lib -> import-tree
-
-import-tree.withLib pkgs.lib
+import-tree.match ".*/feature-[a-z]+\.nix" ./modules
```
-### `import-tree.pipeTo`
-
-`pipeTo` takes a function that will receive the list of paths.
-When configured with this, `import-tree` will not return a nix module but the result of the function being piped to.
+#### `.map `
+**Transform each path** with a function `path -> any`.
```nix
-# import-tree.pipeTo : ([paths] -> any) -> import-tree
-
-import-tree.pipeTo lib.id # equivalent to `.leafs`
+import-tree.map (path: { imports = [ path ]; source = toString path; })
```
-### `import-tree.leafs`
-
-`leafs` takes no arguments, it is equivalent to calling `import-tree.pipeTo lib.id`. That is, instead of producing a nix module, just return the list of results.
+#### `.addPath `
+**Add paths** to be searched (accumulates with multiple calls).
```nix
-# import-tree.leafs : import-tree
-
-import-tree.leafs
+(import-tree.addPath ./base).addPath ./extras
```
-### `import-tree.new`
-
-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.
+#### `.initFilter `
+**Replace the initial filter** (default: `.nix` files, ignore `/_` paths).
-### `import-tree.initFilter`
+```nix
+# Look for .md files instead
+import-tree.initFilter (lib.hasSuffix ".md")
-*Replaces* the initial filter which defaults to: Include files with `.nix` suffix and not having `/_` infix.
+# Custom ignore pattern
+import-tree.initFilter (p: lib.hasSuffix ".nix" p && !lib.hasInfix "/private/")
+```
-_NOTE_: initFilter is non-accumulating and is the *first* filter to run before those accumulated via `filter`/`match`.
+### Extension Methods
-You can use this to make import-tree scan for other file types or change the ignore convention.
+#### `.addAPI `
+**Extend the API** with custom methods.
```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.addAPI {
+ stable = self: self.filter (lib.hasInfix "stable");
+ beta = self: self.filter (lib.hasInfix "beta");
+}
```
-### `import-tree.files`
-
-A shorthand for `import-tree.leafs.result`. Returns a list of matching files.
+#### `.withLib `
+**Provide lib instance** (required for `.files`, `.leafs`, `.pipeTo` methods).
-This can be used when you don't want to import the tree, but just get a list of files from it.
+```nix
+import-tree.withLib pkgs.lib
+```
-Useful for listing files other than `.nix`, for example, for passing all `.js` files to a minifier:
+### Output Methods
-_TIP_: remember to use `withLib` when *not* using import-tree as a module import.
+#### `.files`
+**Get list of matching files** instead of importing them.
```nix
-# import-tree.files : [ ]
-
-# paths to give to uglify-js
-lib.pipe import-tree [
- (i: i.initFilter (lib.hasSuffix ".js")) # look for .js files. ignore nothing.
- (i: i.addPath ./out) # under the typescript compiler outDir
- (i: i.withLib lib) # set lib since we are not importing modules.
- (i: i.files)
-]
-# => list of all .js files
+(import-tree.withLib lib).files ./modules
+# => [ /path/to/modules/a.nix /path/to/modules/b.nix ]
```
-### `import-tree.result`
+#### `.result`
+**Apply configured tree** with empty path list (useful for pre-configured trees).
-Exactly the same as calling the import-tree object with an empty list `[ ]`.
-This is useful for import-tree objects that already have paths configured via `.addPath`.
+```nix
+(import-tree.addPath ./modules).result
+# Same as: (import-tree.addPath ./modules) []
+```
+
+#### `.pipeTo `
+**Process file list** with a custom function.
```nix
-# import-tree.result :
+import-tree.pipeTo (files: lib.length files) ./modules
+# => number of discovered files
+```
-# these two are exactly the same:
-(import-tree.addPath ./modules).result
-(import-tree.addPath ./modules) [ ]
+#### `.new`
+**Create fresh instance** with empty configuration.
+
+```nix
+import-tree.addPath ./modules).new.addPath ./other
+# Only includes ./other (previous config cleared)
```
-
+## Testing
-## Why
+import-tree uses [checkmate](https://github.com/vic/checkmate) for comprehensive testing.
-Importing a tree of nix modules has some advantages:
+### Running Tests
-### Dendritic Pattern: each file is a flake-parts module
+```bash
+# Run the full test suite
+nix flake check path:checkmate --override-input target path:.
-[That pattern](https://github.com/mightyiam/dendritic) was the original inspiration for this library.
-See [@mightyiam's post](https://discourse.nixos.org/t/pattern-each-file-is-a-flake-parts-module/61271),
-[@drupol's blog post](https://not-a-number.io/2025/refactoring-my-infrastructure-as-code-configurations/) and
-[@vic's reply](https://discourse.nixos.org/t/how-do-you-structure-your-nixos-configs/65851/8)
-to learn about the Dendritic pattern advantages.
+# Format code (if you're contributing)
+nix run github:vic/checkmate#fmt
+```
-### Sharing pre-configured subtrees of modules
+### Test Structure
-
-
+The test suite in [`checkmate.nix`](checkmate.nix) covers:
-Since the import-tree API is _extensible_ and lets you add paths or
-filters at configuration time, configuration-library authors can
-provide custom import-tree instances with an API suited for their
-particular idioms.
+- **Core functionality**: File discovery, filtering, module generation
+- **API methods**: All configuration and extension methods
+- **Edge cases**: Invalid inputs, empty directories, mixed path types
+- **Integration**: Usage within module systems
-@vic is using this on [Dendrix](https://github.com/vic/dendrix) for [community conventions](https://github.com/vic/dendrix/blob/main/dev/modules/community/_pipeline.nix) on tagging files.
+## Contributing
-
+We welcome contributions! Here's how to get started:
-This would allow us to have community-driven *sets* of configurations,
-much like those popular for editors: spacemacs/lazy-vim distributions.
+### Development Setup
-Imagine an editor distribution exposing the following flake output:
+1. **Clone the repository**:
+ ```bash
+ git clone https://github.com/vic/import-tree.git
+ cd import-tree
+ ```
-```nix
-# editor-distro's flakeModule
-{inputs, lib, ...}:
-let
- flake.lib.modules-tree = lib.pipe inputs.import-tree [
- (i: i.addPath ./modules)
- (i: i.addAPI { inherit on off exclusive; })
- (i: i.addAPI { ruby = self: self.on "ruby"; })
- (i: i.addAPI { python = self: self.on "python"; })
- (i: i.addAPI { old-school = self: self.off "copilot"; })
- (i: i.addAPI { vim-btw = self: self.exclusive "vim" "emacs"; })
- ];
+2. **Run tests** to ensure everything works:
+ ```bash
+ nix flake check path:checkmate --override-input target path:.
+ ```
- on = self: flag: self.filter (lib.hasInfix "+${flag}");
- off = self: flag: self.filterNot (lib.hasInfix "+${flag}");
- exclusive = self: onFlag: offFlag: lib.pipe self [
- (self: on self onFlag)
- (self: off self offFlag)
- ];
-in
-{
- inherit flake;
-}
-```
+### Making Changes
-Users of such distribution can do:
+1. **Follow the existing code style** - use the formatter:
+ ```bash
+ nix run github:vic/checkmate#fmt
+ ```
-```nix
-# consumer flakeModule
-{inputs, lib, ...}: let
- ed-tree = inputs.editor-distro.lib.modules-tree;
-in {
- imports = [
- (ed-tree.vim-btw.old-school.on "rust")
- ];
-}
-```
+2. **Add tests** for new functionality in `checkmate.nix`
-
+3. **Update documentation** if you change the API
-## Testing
+4. **Test your changes** thoroughly before submitting
-`import-tree` uses [`checkmate`](https://github.com/vic/checkmate) for testing.
+### Submitting Contributions
-The test suite can be found in [`checkmate.nix`](checkmate.nix). To run it locally:
+1. Create a focused pull request with a clear description
+2. Ensure all tests pass
+3. Include examples for new features
+4. Update documentation as needed
-```sh
-nix flake check path:checkmate --override-input target path:.
-```
+For questions or discussions, feel free to open an issue!
-Run the following to format files:
+## License
-```sh
-nix run github:vic/checkmate#fmt
-```
+Licensed under the [Apache License 2.0](LICENSE).
\ No newline at end of file