# `MishkaChelekom.Generators.Core`
[🔗](https://github.com/mishka-group/mishka_chelekom/blob/v0.0.9/lib/mishka_chelekom/generators/core.ex#L1)

Shared generator plumbing used by the styled (`mix mishka.ui.gen.component(s)`) and
headless (`mix mishka.ui.gen.headless(.components)`) tasks.

This is the reusable foundation the mix tasks build on: library `priv/` resolution,
catalog discovery/validation, component naming, the EEx assign skeleton, generated-file
tracking and prefix persistence. Each mix task stays a thin front-end over these helpers.

Larger, more specialized concerns live in sibling modules of the same namespace:
`MishkaChelekom.Generators.Preflight`, `.Assets`, `.Multi` and `.ImportMacro`.

# `all_component_names`

```elixir
@spec all_component_names(Igniter.t(), :styled | :headless) :: [String.t()]
```

Every available component name for a layer (sorted, unique basenames of the `.eex` files).

`:styled` also discovers host-app custom templates under
`priv/mishka_chelekom/{components,templates,presets}`.

# `append_arg`

```elixir
@spec append_arg([String.t()], String.t(), String.t() | nil) :: [String.t()]
```

Appends `[flag, value]` to `args` when `value` is present (not `nil`/empty).

# `banner`

```elixir
@spec banner(String.t(), String.t() | nil) :: :ok
```

Prints the Mishka owl banner in `color`, optionally with a `subtitle` line beneath it.

# `check_dependencies`

```elixir
@spec check_dependencies(Igniter.t()) :: Igniter.t()
```

Verifies Phoenix >= 1.8.0 and Tailwind >= 4.0.0, adding an issue per
problem found. Returns the (possibly annotated) igniter.

# `component_atom`

```elixir
@spec component_atom(String.t()) :: atom()
```

Turns a component name string into an atom (used to index a catalog `.exs`).

# `convert_options`

```elixir
@spec convert_options(nil | String.t() | [String.t()]) :: nil | [String.t()]
```

Converts a CSV string or list of values into a trimmed list of strings.

# `eex_assigns`

```elixir
@spec eex_assigns(Igniter.t(), module(), keyword(), keyword()) :: keyword()
```

Builds the EEx assigns shared by both layers: nil-fills every declared catalog arg, then
merges `:module`, `:web_module`, `:component_prefix` and `:module_prefix_camel`.

`opts`:

  * `:values` - already-validated arg values to seed (styled); defaults to `[]` (headless)
  * `:component_prefix` - resolved public-function prefix (or `nil`)
  * `:module_prefix` - resolved module-name prefix used to derive `module_prefix_camel`

# `ensure_user_config`

```elixir
@spec ensure_user_config(Igniter.t()) :: Igniter.t()
```

Creates the sample `priv/mishka_chelekom/config.exs` if it does not exist yet (idempotent).

Shared by the styled and headless setup so both layers handle the config file the same way.

# `fan_out`

```elixir
@spec fan_out(Igniter.t(), String.t(), [String.t()], [String.t()]) :: Igniter.t()
```

Composes `task` once per component in `list`, prepending each name to `extra_args`.

# `fetch_catalog`

```elixir
@spec fetch_catalog(Igniter.t(), String.t(), :styled | :headless) ::
  {:ok, %{component: String.t(), path: String.t(), config: keyword()}}
  | {:error, {:not_found, String.t()} | {:bad_catalog, String.t(), String.t()}}
```

Resolves a component's `.eex` template and sibling `.exs` catalog, reads and validates it.

Returns `{:ok, %{component, path, config}}` (the underscored name, the template path and the
validated catalog) or a structured error the caller turns into a user-facing message:

  * `{:error, {:not_found, path}}`
  * `{:error, {:bad_catalog, reason, config_path}}`

For `:styled`, names prefixed `component_`/`preset_`/`template_` resolve against the host
app's `priv/mishka_chelekom/{components,presets,templates}`; everything else (and all
`:headless` names) resolves against the library `priv/`.

# `lib_priv`

```elixir
@spec lib_priv(String.t()) :: String.t()
```

Joins a sub-path onto the library `priv/` dir.

    iex> MishkaChelekom.Generators.Core.lib_priv("components/button.eex")

# `lib_priv_dir`

```elixir
@spec lib_priv_dir() :: String.t()
```

Returns the absolute path to the library's `priv/` directory.

Prefers `:code.priv_dir(:mishka_chelekom)` (works for hex deps, path deps, and umbrellas),
falling back to the legacy `"deps/mishka_chelekom/priv"` string only if the app priv dir
is unavailable.

# `maybe_save_prefixes`

```elixir
@spec maybe_save_prefixes(
  Igniter.t(),
  keyword()
) :: Igniter.t()
```

Persists `--component-prefix`/`--module-prefix` to the user config unless this is a sub
generation (`--sub`) or saving is disabled (`--no-save`).

# `module_atom`

```elixir
@spec module_atom(String.t()) :: atom()
```

Turns a dotted, lower-case string into a module atom (`"a_b.c" -> :"AB.C"`).

# `module_atom`

```elixir
@spec module_atom(String.t(), :last) :: atom()
```

Like `module_atom/1` but keeps only the last segment (`"a.b.c" -> :C`).

# `resolve_components`

```elixir
@spec resolve_components(
  Igniter.t(),
  String.t() | nil,
  :styled | :headless,
  map(),
  [String.t()] | nil
) :: [String.t()]
```

Resolves the component list to generate for a plural task.

An empty request (or one containing `"all"`) expands to every component of `layer`; otherwise
the requested names are used. Names in the user config `:exclude_components` or `cli_exclude`
are removed.

# `template_dir`

```elixir
@spec template_dir(:styled | :headless) :: String.t()
```

The catalog template directory for a layer.

  * `:styled`   → `priv/components`
  * `:headless` → `priv/headless`

# `track_generated_file`

```elixir
@spec track_generated_file(Igniter.t(), String.t()) :: Igniter.t()
```

Records a generated file in `igniter_exs[:dont_move_files]` so Igniter does not relocate it.

# `validate_catalog`

```elixir
@spec validate_catalog(term()) :: {:ok, keyword()} | {:error, String.t()}
```

Validates that a loaded catalog config has the minimal expected shape, returning
`{:ok, config}` or `{:error, reason}`.

---

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