Preserves user-authored code across regenerations.
Generators emit a language-appropriate CUSTOM marker before the
closing of every regenerable file. Anything the developer writes
below that marker is preserved verbatim when the generator runs
again.
Caravela also stamps a checksum header at the top of every generated file:
# caravela-gen: generator=context version=0.8.0 above_sha256=<hex>On regeneration the checksum is re-computed from the file on disk.
If the content above the marker was edited by hand, the hashes no
longer match and verify_existing!/2 aborts via Mix.raise/1 — the
user's edits would otherwise be silently overwritten. Pass
force: true (from the mix task's --force flag) to bypass.
Three comment styles are supported via the :style opt:
:elixir(default) —# …:ts—// …(TypeScript):svelte—<!-- … -->(HTML/Svelte top-level)
Summary
Functions
Extract all named CUSTOM blocks from an Elixir source. Returns
%{name => body} where name is a binary and body is the raw text
between the CUSTOM :name and END :name markers.
Marker string for the given style (default :elixir).
Reusable marker block appended by Elixir generators. Terminates the
module with end once custom content is merged in. Only meaningful
for the :elixir style; Svelte and TS templates embed their own
trailing marker inline.
Merge custom content from an existing source into a newly-generated source. If the existing source does not contain the marker, the new source is returned unchanged.
Merge named CUSTOM blocks from existing_source into new_source.
Merge custom content from disk, after verifying the on-disk file has not been tampered with above the marker.
Empty named CUSTOM block, rendered at its extension point by
generator templates. The indent opt controls leading whitespace.
Parse a header line into {:ok, %{generator, version, hash}} or
:error. Exposed for tests and tooling.
Stamp a fresh caravela-gen: header onto source, replacing any
existing header line on the first line.
Inspect source contents. Returns :ok, :no_header, or
{:mismatch, stored_hash, current_hash}.
Verify path's on-disk content against the checksum stored in its
caravela-gen: header. Returns :ok when
Types
Functions
Extract all named CUSTOM blocks from an Elixir source. Returns
%{name => body} where name is a binary and body is the raw text
between the CUSTOM :name and END :name markers.
Only parses Elixir-style markers (# --- CUSTOM :name ---). The
other styles don't support named blocks in this release.
Marker string for the given style (default :elixir).
@spec marker_block() :: String.t()
Reusable marker block appended by Elixir generators. Terminates the
module with end once custom content is merged in. Only meaningful
for the :elixir style; Svelte and TS templates embed their own
trailing marker inline.
Merge custom content from an existing source into a newly-generated source. If the existing source does not contain the marker, the new source is returned unchanged.
Splits on the last occurrence of the marker in both sources.
This is deliberate: the marker string commonly appears inside
@moduledoc / <!-- --> prose where it is being named rather
than delimiting user code. The real marker is always the last one
(emitted at the tail of the template). Using first-occurrence
would cut the file at the docstring and produce garbage.
Merge named CUSTOM blocks from existing_source into new_source.
For every named block in new_source, if existing_source has a
block with the same name, the new source's body is replaced with
the existing body. Orphan blocks (present in existing but absent
in new) are reported via Mix.shell/0 and discarded.
Only applies to Elixir-style markers. Returns new_source
unchanged for any other style.
Merge custom content from disk, after verifying the on-disk file has not been tampered with above the marker.
opts:
:style(default:elixir) —:elixir | :ts | :svelte.:force(default false) — skip the verification step.
Raises via Mix.raise/1 when the file's stored checksum does not
match its current above-marker body and force: true was not
passed. Returns the merged source otherwise.
Empty named CUSTOM block, rendered at its extension point by
generator templates. The indent opt controls leading whitespace.
Example
iex> Caravela.Gen.Custom.named_empty(:list_books, indent: " ")
" # --- CUSTOM :list_books ---\n # --- END :list_books ---"
@spec parse_header_line(String.t(), style()) :: {:ok, %{hash: String.t(), generator: String.t() | nil, version: String.t() | nil}} | :error
Parse a header line into {:ok, %{generator, version, hash}} or
:error. Exposed for tests and tooling.
Stamp a fresh caravela-gen: header onto source, replacing any
existing header line on the first line.
opts:
:generator(required) — atom identifying the generator.:style(default:elixir) — comment style for the header.:version— override the default Caravela version string.
The checksum covers every byte above the CUSTOM marker, excluding the header line itself. Call after any formatting step so the hash reflects the bytes actually written to disk.
@spec verify_contents(String.t(), style()) :: :ok | :no_header | {:mismatch, String.t(), String.t()}
Inspect source contents. Returns :ok, :no_header, or
{:mismatch, stored_hash, current_hash}.
Pure function. Useful from tests and mix caravela.gen --check.
Verify path's on-disk content against the checksum stored in its
caravela-gen: header. Returns :ok when:
- the file does not exist;
- the file has no Caravela header (first-time adoption);
- the file has a header and the stored hash matches the current above-marker body.
Raises via Mix.raise/1 when the stored hash does not match, unless
opts[:force] is true.