Helpers for steps that write files from templates.
A step's templates live beside it, in the directory named after its file
with a leading underscore (Composer.Step.template_dir/1):
lib/composer/core/steps/sidebar_layout.ex
lib/composer/core/steps/_sidebar_layout/app_fn.ex.eex
lib/composer/core/steps/_sidebar_layout/files/...Inside a step, template/1 (imported by use Composer.Step) refers to a
file or directory there, and is checked at compile time:
def igniter(igniter) do
igniter
|> Composer.Templates.require_present({:dep, :heroicons}, "the sidebar layout uses heroicons")
|> Composer.Templates.copy_dir(template("files"), ".")
|> Composer.Templates.replace_function(
Module.concat([Composer.Templates.assigns(igniter).web_module, "Layouts"]),
:app,
1,
template("app_fn.ex.eex")
)
endA reference is {cookbook, step_id, path}. Code outside a step, such as a
helper module shared by a cookbook's steps, builds one with ref/2.
Rendering
Only files ending in .eex are rendered, with EEx, and written without
the .eex extension. Every other file is copied verbatim, so templates
that contain their own <%= %> tags (a HEEx or Astral template) are safe.
Elixir source must be stored as .ex.eex: a bare .ex file in a step's
directory is a child step, compiled as part of the package, so
use Composer.Cookbook rejects one that is not a step.
.eex templates see the assigns as @name. assigns/1 builds the
standard ones, and every helper merges caller-given assigns over them.
Path variables
A path segment such as __web_dir__ is replaced with the matching assign,
so lib/__web_dir__/components/layouts.ex.eex is written to
lib/my_app_web/components/layouts.ex. A variable with no assign raises,
naming the variable and the template; nothing is written with a literal
__var__ in its path.
Existing files
A target file that exists is overwritten by default; the change is still
shown in the Igniter diff before anything is written. Pass
on_exists: :skip to leave existing files alone.
Summary
Types
A template: {cookbook, step_id, path}, path relative to the step's template directory.
Functions
Returns the standard assigns for the target app
Writes every file under a template directory into target_dir, keeping
the relative layout. .gitkeep files are skipped. Options are as for
copy_file/4.
Writes one template to target, rendering it when it ends in .eex.
Replaces __var__ path variables in path with assigns, raising with the
variable and template when one has no assign.
Adds content to the file at path
Returns the absolute path of a template, raising when its cookbook or the file does not exist.
Returns a reference to path in the template directory of step, a
step module.
Renders a template file: EEx when it ends in .eex, verbatim otherwise.
Replaces function name/arity of module with the rendered template, a
complete def.
Adds a warning when a prerequisite is missing: {:dep, name} for a
dependency in mix.exs, or {:file, path}.
Types
Functions
Returns the standard assigns for the target app:
%{app_name: "my_app", app_module: "MyApp", web_module: "MyAppWeb", web_dir: "my_app_web"}
@spec copy_dir(Igniter.t(), template_ref(), Path.t(), keyword()) :: Igniter.t()
Writes every file under a template directory into target_dir, keeping
the relative layout. .gitkeep files are skipped. Options are as for
copy_file/4.
@spec copy_file(Igniter.t(), template_ref(), Path.t(), keyword()) :: Igniter.t()
Writes one template to target, rendering it when it ends in .eex.
target may contain path variables, and loses a trailing .eex.
Options
:assigns- merged overassigns/1:on_exists-:overwrite(default) or:skip
Replaces __var__ path variables in path with assigns, raising with the
variable and template when one has no assign.
Adds content to the file at path:
at: {:after, anchor}- right after the first occurrence ofanchorat: {:before, anchor}- right before itat: :append- at the end of the file
Content already in the file is not added again. A missing file or anchor
is a loud warning rather than a silent no-op, so drift in the target
surfaces. Pass warning: to word it.
@spec path(template_ref()) :: Path.t()
Returns the absolute path of a template, raising when its cookbook or the file does not exist.
@spec ref(module(), Path.t()) :: template_ref()
Returns a reference to path in the template directory of step, a
step module.
This is how code outside a step refers to a step's templates:
Composer.Templates.ref(Composer.ComposerLab.Steps.Astral.DaisyComponents, "components/button.astral")
#=> {"composer_lab", "astral/daisy_components", "components/button.astral"}Inside a step, use template/1, which is also checked at compile time.
Raises when step is not a step module.
Renders a template file: EEx when it ends in .eex, verbatim otherwise.
@spec replace_function( Igniter.t(), module(), atom(), arity(), template_ref(), map() | keyword(), keyword() ) :: Igniter.t()
Replaces function name/arity of module with the rendered template, a
complete def.
A missing function or module is a warning, not a crash, since the target
app may have been customized. Pass warning: in opts to word it.
Adds a warning when a prerequisite is missing: {:dep, name} for a
dependency in mix.exs, or {:file, path}.