# `Igniter`
[🔗](https://github.com/ash-project/igniter/blob/v0.8.2/lib/igniter.ex#L5)

Tools for generating and patching code into an Elixir project.

## Assigns

Assigns are a way to store arbitrary data on an Igniter struct that can be used to
pass information between tasks, configure behavior, or maintain state throughout
the execution pipeline. They work similarly to assigns in Phoenix LiveView or Plug.

You can set assigns using `assign/3` or `assign/2`, and access them via the
`assigns` field on the Igniter struct.

### Special Assigns

The following assigns have special meaning and can be set to control Igniter's behavior:

* `:prompt_on_git_changes?` - Controls whether Igniter should warn users about
  uncommitted git changes before applying modifications. Defaults to `true`. When
  enabled, Igniter will check git status and display a warning if there are
  uncommitted changes, giving users a chance to save their work before proceeding.

* `:quiet_on_no_changes?` - Controls whether Igniter should display a message when
  no changes are proposed. Defaults to `false`. When set to `true`, Igniter will
  suppress the "No proposed content changes!" message that normally appears when
  running operations that don't result in any file modifications.

# `t`

```elixir
@type t() :: %Igniter{
  args: Igniter.Mix.Task.Args.t(),
  assigns: map(),
  issues: [String.t()],
  mkdirs: [String.t()],
  moves: %{optional(String.t()) =&gt; String.t()},
  notices: [String.t()],
  parent: term(),
  rewrite: Rewrite.t(),
  rms: [String.t()],
  task: term(),
  tasks: [
    String.t()
    | {String.t(), [String.t()]}
    | {String.t(), [String.t()], :delayed}
  ],
  warnings: [String.t()]
}
```

# `zipper_updater`

```elixir
@type zipper_updater() :: (Sourceror.Zipper.t() -&gt;
                       {:ok, Sourceror.Zipper.t()}
                       | {:error, String.t() | [String.t()]}
                       | {:warning, String.t() | [String.t()]})
```

# `add_issue`

```elixir
@spec add_issue(t(), term() | [term()]) :: t()
```

Adds an issue to the issues list. Any issues will prevent writing and be displayed to the user.

# `add_notice`

```elixir
@spec add_notice(t(), String.t()) :: t()
```

Adds a notice to the notices list. Notices are displayed to the user once the igniter finishes running.

# `add_task`

Adds a task to the tasks list. Tasks will be run after all changes have been committed

# `add_warning`

```elixir
@spec add_warning(t(), term() | [term()]) :: t()
```

Adds a warning to the warnings list. Warnings will not prevent writing, but will be displayed to the user.

# `apply_and_fetch_dependencies`

Applies the current changes to the `mix.exs` in the Igniter and fetches dependencies.

Returns the remaining changes in the Igniter if successful.

## Options

* `:error_on_abort?` - If `true`, raises an error if the user aborts the operation. Returns the original igniter if not.
* `:yes` - If `true`, automatically applies the changes without prompting the user.

# `assign`

# `assign`

```elixir
@spec assign(t(), atom(), term()) :: t()
```

Stores the key/value pair in `igniter.assigns`

# `changed?`

```elixir
@spec changed?(t() | Rewrite.Source.t()) :: boolean()
```

Returns true if the igniter or source provided has changed

# `changed?`

```elixir
@spec changed?(t(), String.t() | [String.t()]) :: boolean()
```

Returns true if any of the files specified in `paths` have changed.

# `compose_task`

```elixir
@spec compose_task(
  t(),
  task :: String.t() | module(),
  argv :: [String.t()] | nil,
  fallback :: (t() -&gt; t()) | (t(), [String.t()] -&gt; t()) | nil
) :: t()
```

Finds the `Igniter.Mix.Task` task by name and composes it with `igniter`.

If the task doesn't exist, a `fallback` function may be provided. This
function should accept and return the `igniter`.

## Argument handling

This function calls the task's `igniter/1` (or `igniter/2`) callback, setting
`igniter.args` using the current `igniter.args.argv_flags`. This prevents
composed tasks from accidentally consuming positional arguments. If you
wish the composed task to access additional arguments, you must explicitly
pass an `argv` list.

Additionally, you must declare other tasks you are composing with in your
task's `Igniter.Mix.Task.Info` struct using the `:composes` key. Without
this, you'll see unexpected argument errors if a flag that a composed task
uses is passed without you explicitly declaring it in your `:schema`.

## Example

    def info(_argv, _parent) do
      %Igniter.Mix.Task.Info{
        ...,
        composes: [
          "other.task1",
          "other.task2"
        ]
      }

    def igniter(igniter) do
      igniter
      # other.task1 will see igniter.args.argv_flags as its args
      |> Igniter.compose_task("other.task1")
      # other.task2 will see an additional arg and flag
      |> Igniter.compose_task("other.task2", ["arg", "--flag"] ++ igniter.argv.argv_flags)
    end

# `copy_template`

```elixir
@spec copy_template(
  igniter :: t(),
  source :: Path.t(),
  target :: Path.t(),
  assigns :: Keyword.t(),
  opts :: Keyword.t()
) :: t()
```

Copies an EEx template file from  the source path to the target path.

Accepts the same options as `create_new_file/4`.

# `create_new_elixir_file`

> This function is deprecated. Use `create_new_file/4`.

# `create_new_file`

Creates a new file in the project with the provided string contents. Adds an error if it already exists.

## Options

- `:on_exists` - The action to take if the file already exists. Can be
  - `:error` (default) - Adds an error that prevents any eventual write.
  - `:warning` - Warns when writing but continues (without overwriting)
  - `:skip` - Skips writing the file without a warning
  - `:overwrite` - Warns when writing and overwrites the content with the new content

# `create_or_update_elixir_file`

```elixir
@spec create_or_update_elixir_file(t(), Path.t(), String.t(), zipper_updater()) :: t()
```

Creates the given file in the project with the provided string contents, or updates it with a function of type `zipper_updater()` if it already exists.

# `create_or_update_file`

Creates the given file in the project with the provided string contents, or updates it with a function as in `update_file/3` (or with `zipper_updater()` for elixir files) if it already exists.

# `delay_task`

Adds a delayed task to the tasks list. Delayed tasks will be run after all other composed tasks have been added.

# `do_or_dry_run`

Executes or dry-runs a given Igniter.

# `exists?`

```elixir
@spec exists?(t(), Path.t()) :: boolean()
```

Checks if a file exists on the file system or in the igniter.

# `has_changes?`

Returns whether the current Igniter has pending changes.

# `include_all_elixir_files`

This function stores in the igniter if its been run before, so it is only run once, which is expensive.

# `include_existing_elixir_file`

> This function is deprecated. Use `include_existing_file/3` instead.

```elixir
@spec include_existing_elixir_file(t(), Path.t(), opts :: Keyword.t()) :: t()
```

# `include_existing_file`

```elixir
@spec include_existing_file(t(), Path.t(), opts :: Keyword.t()) :: t()
```

Includes the given file in the project, expecting it to exist. Does nothing if its already been added.

## Options

- `:required?` - Tracks an issue for the file missing. Defaults to `false`.

# `include_glob`

```elixir
@spec include_glob(t(), Path.t() | GlobEx.t()) :: t()
```

Includes all files matching the given glob, expecting them all (for now) to be elixir files.

# `include_or_create_elixir_file`

> This function is deprecated. Use `include_or_create_file/3` instead.

```elixir
@spec include_or_create_elixir_file(t(), Path.t(), contents :: String.t()) :: t()
```

# `include_or_create_file`

```elixir
@spec include_or_create_file(t(), Path.t(), contents :: String.t()) :: t()
```

Includes or creates the given file in the project with the provided contents. Does nothing if its already been added.

# `install`

Installs a package as if calling `mix igniter.install`

See `mix igniter.install` for information on the package format.

## Options

- `append?` - If `true`, appends the package to the existing list of packages instead of prepending. Defaults to `false`.

## Examples

  Igniter.install(igniter, "ash")

  Igniter.install(igniter, "ash_authentication@2.0", ["--authentication-strategies", "password,magic_link"])

# `mkdir`

```elixir
@spec mkdir(t(), Path.t()) :: t()
```

Creates a folder in the project.

# `move_file`

# `new`

```elixir
@spec new() :: t()
```

Returns a new igniter

# `rm`

Deletes a file when the igniter is applied

# `subdirectory?`

# `update_all_elixir_files`

Runs an update over all elixir files

# `update_assign`

# `update_elixir_file`

```elixir
@spec update_elixir_file(t(), Path.t(), zipper_updater(), keyword()) :: t()
```

Updates the source code of the given elixir file

## Options

- `:required?` - Tracks an issue for the file missing. Defaults to `true`.

# `update_file`

Updates a given file's `Rewrite.Source`

# `update_glob`

```elixir
@spec update_glob(
  t(),
  Path.t() | GlobEx.t(),
  zipper_updater()
) :: t()
```

Updates all files matching the given glob with the given zipper function.

Adds any new files matching that glob to the igniter first.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
