import-tree

repository·main·Indexed 43 days ago

https://github.com/denful/import-tree

A utility for recursively importing Nix modules from a directory structure. It provides a functional API to filter, match, and map files, supporting the Dendritic Pattern across NixOS, nix-darwin, home-manager, flake-parts, and NixVim. It can be used as a dependency-free Flake or a Nix library, featuring chaining methods and extensibility via .addAPI for domain-specific instances.

Tokens
11K
Snippets
51
Records
67
Agent score
55%

What's inside import-tree

  1. What is import-tree?

    main

    import-tree is a Nix library designed to automate the discovery of Nix modules within a directory tree. Instead of manually maintaining an imports = [ ... ] list, you can point import-tree at a directory, and it will recursively generate a list of Nix module imports for you.

    Key characteristics:

    • Dependency-free: It does not require external Nix dependencies.
    • Single-file: It is distributed as a single file, making it easy to integrate.
    • Flexible usage: It can be used as a Nix Flake or as a standard Nix import.
  2. Overview of import-tree

    main

    import-tree is a tool for recursively importing Nix modules from a directory using a simple and extensible API. It is designed to work across various Nix ecosystems including NixOS, nix-darwin, home-manager, flake-parts, and NixVim. It can be used as a dependency-free Flake or as a Nix library. It supports the Dendritic Pattern on both stable and unstable Nix.

    Key features include:

    • Sensible defaults with configurable behavior.
    • Chaining methods like .filter, .match, and .map for precise file selection.
    • Extensibility via .addAPI to create domain-specific instances.
    • Default behavior: paths containing /_ are ignored.
  3. Key features of import-tree

    main

    import-tree provides several capabilities for managing Nix module discovery:

    • Universal Compatibility: Works with NixOS, nix-darwin, home-manager, flake-parts, NixVim, and any other Nix module system.
    • Composable Filters: Use .filter, .filterNot, .match, and .matchNot to refine which files are included.
    • Transformations: Use .map to transform discovered paths (e.g., wrapping them in modules or reading their contents).
    • Extensible API: Use .addAPI to create domain-specific presets, feature flags, or module sets.
    • Non-Module Usage: Use .leaves and .files to obtain raw file lists for purposes outside of standard Nix module evaluation.
    • Dendritic Pattern Support: Specifically designed to support the Dendritic pattern where every file is treated as a self-contained module.
  4. What is the Dendritic Pattern?

    main

    The Dendritic pattern is a Nix configuration convention where every file in a directory tree is a self-contained Nix module. Instead of maintaining a single monolithic configuration file with a long list of imports, you organize your system into a directory structure where each file represents a specific concern (e.g., networking, users, or desktop components). import-tree automates the process of importing these files, allowing you to manage your configuration by simply adding or removing files from the tree.

    modules/
      networking.nix       # network config
      openssh.nix          # SSH server
      desktop/
        sway.nix           # window manager
        waybar.nix         # status bar
        notifications.nix  # notification daemon
      users/
        alice.nix           # user account
  5. Use late binding in API methods

    main

    API methods in import-tree are late-bound. This means a method can reference other methods that have not yet been defined at the time of the .addAPI call. The reference is resolved only when the method is actually invoked. This is useful for building APIs incrementally across different modules where the order of definition might vary.

    let
      # 'result' refers to 'late', which doesn't exist yet
      first = import-tree.addAPI { result = self: self.late; };
      
      # Now we define 'late'
      extended = first.addAPI { late = _self: "hello"; };
    in
      extended.result  # => "hello"
  6. Chain multiple .addAPI calls

    main

    Calling .addAPI is cumulative. When you call .addAPI on an object that has already been extended, the previous extensions are preserved. This allows you to build complex APIs incrementally through multiple stages or modules.

    let
      first = import-tree.addAPI { foo = self: self.addPath ./foo; };
      second = first.addAPI { bar = self: self.addPath ./bar; };
    in
      second.foo.files  # 'foo' is still available on the 'second' object
  7. Customize module discovery with the Builder API

    main

    The import-tree library provides a builder-style API to customize how Nix files are discovered and processed. You can chain methods to refine the set of imported modules.

    Available customization methods include:

    • .filter: Filter the discovered files.
    • .match: Match files based on specific criteria.
    • .map: Transform the discovered files.
    • .addPath: Add additional paths to the discovery tree.

    Additionally, the API is extensible via .addAPI, allowing you to create domain-specific import-tree instances by adding your own custom methods.

  8. How import-tree discovers modules

    main

    By default, import-tree recursively scans a directory and includes all .nix files in its output. However, it follows a convention where directories containing an underscore (e.g., /_/) are ignored. This allows you to keep helper files or private utilities in the same tree without them being automatically imported as modules.

    Example Directory Structure:

    modules/
      networking.nix
      desktop/
        sway.nix
        waybar.nix
      _helpers/
        utils.nix

    Result of calling import-tree ./modules:

    • ./modules/networking.nix
    • ./modules/desktop/sway.nix
    • ./modules/desktop/waybar.nix

    Note: ./modules/_helpers/utils.nix is skipped.

  9. When to use Combinators vs addAPI

    main

    Choosing between combinators and .addAPI depends on the intended reuse of the configuration:

    • Use Combinators when:

      • You are performing one-off file discovery in scripts.
      • You need to build a configuration inline without intermediate variables.
      • You are chaining a few small, temporary transformations.
    • Use .addAPI when:

      • You are building complex, reusable patterns.
      • You want to provide a clean, named interface for domain-specific logic that will be used in multiple places.
  10. How the Dendritic pattern works with import-tree

    main

    The Dendritic pattern treats a Nix configuration as a file tree where each file is a self-contained module. import-tree acts as the glue that connects these modules by turning a directory structure into a list of imports.

    This approach allows you to organize concerns into a directory hierarchy (e.g., ./modules/desktop/, ./modules/services/) without writing the manual boilerplate required to link every individual file into your main configuration.