MishkaChelekom.Generators.Core (Mishka Chelekom v0.0.9-beta.3)

Copy Markdown View Source

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.

Summary

Functions

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

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

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

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

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

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

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.

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

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

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

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

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

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

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

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

Resolves the component list to generate for a plural task.

The catalog template directory for a layer.

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

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

Functions

all_component_names(igniter, atom)

@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(args, flag, value)

@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(color, subtitle \\ nil)

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

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

check_dependencies(igniter)

@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(component_str)

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

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

convert_options(value)

@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(igniter, component_module, config, opts \\ [])

@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(igniter)

@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(igniter, task, list, extra_args)

@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(igniter, component, layer)

@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(sub)

@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()

@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(igniter, options)

@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(field)

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

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

module_atom(field, atom)

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

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

resolve_components(igniter, requested_csv, layer, user_config, cli_exclude)

@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(atom)

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

The catalog template directory for a layer.

  • :styledpriv/components
  • :headlesspriv/headless

track_generated_file(igniter, location)

@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(config)

@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}.