Igniter Documentation

repository·main·Indexed 19 days ago

https://github.com/ash-project/igniter

A code generation and project patching framework for Elixir. Igniter provides library authors with a toolkit to create semantic generators and codemods, while allowing end-users to easily install dependencies, scaffold projects via `mix igniter.new`, and perform automated refactoring and dependency upgrades using `mix igniter.upgrade`.

Tokens
14.8K
Snippets
54
Records
79
Agent score
64%

What's inside Igniter

  1. Available Igniter modules for project manipulation

    main

    Igniter provides a suite of modules categorized by their scope of influence. Use the Igniter.Project.* modules to perform high-level operations on your Elixir project structure and configuration, and Igniter.Code.* modules for fine-grained AST manipulation within specific files.

    ### Project-Level Modules (`Igniter.Project.*`)
    
    - **`Igniter.Project.Application`** - Working with Application modules and application configuration
    - **`Igniter.Project.Config`** - Modifying Elixir config files (config.exs, runtime.exs, etc.)
    - **`Igniter.Project.Deps`** - Managing dependencies declared in mix.exs
    - **`Igniter.Project.Formatter`** - Interacting with .formatter.exs files
    - **`Igniter.Project.IgniterConfig`** - Managing .igniter.exs configuration files
    - **`Igniter.Project.MixProject`** - Updating project configuration in mix.exs
    - **`Igniter.Project.Module`** - Creating and managing modules with proper file placement
    - **`Igniter.Project.TaskAliases`** - Managing task aliases in mix.exs
    - **`Igniter.Project.Test`** - Working with test and test support files
    
    ### Code-Level Modules (`Igniter.Code.*`)
    
    - **`Igniter.Code.Common`** - General purpose utilities for working with Sourceror.Zipper
    - **`Igniter.Code.Function`** - Working with function definitions and calls
    - **`Igniter.Code.Keyword`** - Manipulating keyword lists
    - **`Igniter.Code.List`** - Working with lists in AST
    - **`Igniter.Code.Map`** - Manipulating maps
    - **`Igniter.Code.Module`** - Working with module definitions and usage
    - **`Igniter.Code.String`** - Utilities for string literals
    - **`Igniter.Code.Tuple`** - Working with tuples
  2. What is Igniter and how does it work?

    main
    Igniter is a code generation and project patching framework designed for the semantic manipulation of Elixir codebases. Unlike simple text replacement, Igniter performs precise, context-aware changes by working directly with the Abstract Syntax Tree (AST) via Sourceror.Zipper. This allows you to build intelligent generators that can safely create new files and modify existing ones while maintaining code integrity.
  3. Use Task Groups to prevent flag ambiguity

    main

    When composing multiple tasks, users may encounter errors if different tasks define the same flag (e.g., --option). To resolve this, Igniter requires users to prefix the flag with the task name (e.g., --package1.option).

    To prevent this and provide a better user experience, you should assign a group to your tasks in the %Igniter.Mix.Task.Info{} struct.

    Setting a group provides two benefits:

    1. Flag De-duplication: Tasks sharing the same group name are assumed to treat the same flag as having the same meaning. Users can then use a single flag (e.g., --option) for all tasks in that group.
    2. Semantic Naming: It allows for shorter, more readable flag names. Instead of a long name like --ash-authentication-phoenix.install.domain, a group named :ash allows the user to simply use --ash.domain.
  4. Limitations of mix igniter.upgrade

    main

    While mix igniter.upgrade is powerful, it has the following limitations:

    Compile Compatibility

    The new version of a package must be "compile compatible" with your existing code. If a major version introduces breaking changes that prevent compilation, the upgrade will fail.

    • Best Practice for Authors: Provide a version prior to breaking changes that includes an upgrader to the new compatible code. Users can then run mix igniter.upgrade package@that.version before moving to the latest version.

    Path Dependencies

    Igniter cannot currently determine the old version for path dependencies. Consequently, path dependencies cannot be upgraded using mix igniter.upgrade at this time.

  5. How generators work in Igniter

    main

    In Igniter, generators are implemented as wrappers around Mix.Task. They are designed to be called individually via the CLI or composed as part of a larger task group.

    To create a generator, define a module that uses Igniter.Mix.Task and implements the igniter/1 callback. This callback receives an igniter struct, which you can use to perform project modifications like creating files or updating configurations. When run, Igniter automatically presents the user with a diff of the proposed changes before applying them.

    defmodule Mix.Tasks.YourLib.Gen.YourThing do
      use Igniter.Mix.Task
    
      @impl Igniter.Mix.Task
      def igniter(igniter) do
        # Implementation logic goes here
      end
    end
  6. How Igniter.Scribe works

    main

    Igniter.Scribe allows you to create documentation that evolves with your code. Instead of writing static markdown, you write your documentation logic directly within your Igniter tasks.

    When running with --scribe:

    1. A test project environment is initialized.
    2. The task's logic is executed.
    3. All file changes (modifications, creations, deletions) are captured.
    4. A markdown document is generated showing the process and code diffs.
    5. The document is saved to the path provided via the --scribe flag.
  7. How Igniter tasks and generators work

    main

    Igniter is designed around the concept of composable tasks. Mix tasks built with Igniter can be called individually or composed together.

    Installers

    When a user runs mix igniter.install <your_package>, Igniter looks for a task named <your_package>.install. You can generate this task using: mix igniter.gen.task <your_package>.install.

    Generators and Patchers

    Generators can wrap existing tasks and add or modify files. You can create a custom generator using: mix igniter.gen.task <your_package>.task.name.

    Example of a custom generator composing an existing task (ash.gen.resource) and adding a new module:

    # in lib/mix/tasks/my_app.gen.resource.ex
    defmodule Mix.Tasks.MyApp.Gen.Resource do
      use Igniter.Mix.Task
    
      @impl Igniter.Mix.Task
      def igniter(igniter) do
        [resource | _] = igniter.args.argv
    
        resource = Igniter.Code.Module.parse(resource)
        my_special_thing = Module.concat([resource, SpecialThing])
        location = Igniter.Code.Module.proper_location(my_special_thing)
    
        igniter
        |> Igniter.compose_task("ash.gen.resource", igniter.args.argv)
        |> Igniter.Project.Module.create_module(my_special_thing, """
          # this is the special thing for #{inspect()}
        """)
      end
    end
  8. Write an upgrader task for your package

    main

    To allow users to automatically upgrade your package, you should provide an Igniter task named your_package.upgrade.

    This task must accept two positional arguments:

    1. from: The old version of the package.
    2. to: The new version of the package.

    It is recommended to use the Igniter generator to scaffold the task following the correct patterns:

    mix igniter.gen.task your_package.upgrade --upgrade
  9. Install Igniter globally via archive

    main

    If you only want to use mix igniter.install to add dependencies to projects without adding Igniter itself as a dependency, you can install it as a global Mix archive.

    Use this method to access mix igniter.new for generating new projects.

    mix archive.install hex igniter_new
  10. Install Igniter as a library author

    main

    When building a library that uses Igniter to provide installers or generators, add it to your mix.exs with optional: true. This ensures that end-users can install your library normally without Igniter being included in their production builds.

    {:igniter, "~> 0.6", optional: true}
  11. Upgrade dependencies with mix igniter.upgrade

    main

    You can use mix igniter.upgrade as a drop-in replacement for mix deps.update.

    When you run this task, packages that do not use Igniter will be updated normally. However, for packages that have associated Igniter upgrade tasks, those tasks will be automatically executed during the update process. This allows for automated code transformations and configuration updates as part of the dependency upgrade lifecycle.

    mix igniter.upgrade
  12. Create a new Mix project with Igniter

    main

    You can use mix igniter.new to scaffold a new Elixir project. This command allows you to automatically install specific libraries (like ash or ecto) during the generation process.

    Basic usage with installations

    mix igniter.new app_name --install ash,ecto

    Combining with existing generators

    You can compose Igniter with existing project generators (like mix phx.new) using the --with flag. To pass arguments to the underlying generator, use the --with-args flag.

    mix igniter.new app_name --install ash --with phx.new --with-args="--no-ecto --no-html"