SvEx. Source. MixExs
(SvEx v0.4.2)
Locates and patches constructs in a mix.exs, structurally (SDD R5, R6).
The one module that knows what a mix.exs looks like. Reading and writing
live together deliberately: they were separate and they disagreed — the
writer matched one of the four shapes mix accepts, the reader matched two,
and each reported success on the shapes it could not handle.
Every function takes and returns SOURCE TEXT and touches no filesystem, so a caller supplies bytes from wherever it has them and this module is coverable without generating a project.
Placement is Sourceror.get_range/1 followed by Sourceror.patch_string/2,
which splices at a range and copies every other byte through unchanged.
Sourceror.to_string/1 is never called here: reprinting a file the project
owns is a first-order R6 violation, and mix.exs's own comment records the
measurement that rejected it.
Summary
Functions
Adds dep to source's dependency list.
The aliases/0 keyword list in source, as {name, [command source]} pairs.
Appends element to the literal list at list_range, as source text.
The dependency declarations in source, as the strings the author wrote.
The project/0 keyword list in source, as {key, source string} pairs.
Sets name to commands in source's aliases/0.
Sets key to value in source's project/0.
Types
@type error() :: {:no_deps_list, :not_found | :not_a_literal} | {:no_project_function, :not_found} | {:no_aliases, :not_found} | {:unparseable, term()}
Functions
Adds dep to source's dependency list.
Idempotent, decided by comparing PARSED declarations rather than text: a
reflowed declaration is not an absent one, and duplicating it makes mix
reject the file.
The dependency lands at the TOP of the list, which priv/meta/cache_component
records and the round trip compares byte for byte. A single-line list stays
single-line and a multi-line one stays multi-line — patch_string/2 indents
continuation lines relative to the range start, so the change carries no
leading whitespace of its own.
The aliases/0 keyword list in source, as {name, [command source]} pairs.
{:error, {:no_aliases, :not_found}} when the project declares none, which
every mix new project does.
Appends element to the literal list at list_range, as source text.
Shared with SvEx.Source.ApplicationEx, which appends a supervision child by
exactly the same mechanics: patch at the LAST element's range so the author's
formatting survives, or — for an empty list — at a zero-width point just
inside the brackets, so that mix new's commented-out examples are not
deleted along with the list they sit in.
add_dep/2 does NOT use this: a dependency goes at the top of the list, a
position priv/meta/cache_component records and the round trip compares.
The dependency declarations in source, as the strings the author wrote.
Source strings rather than terms, so the manifest records only: and
runtime: exactly as written (D10).
The project/0 keyword list in source, as {key, source string} pairs.
Values are strings rather than terms for the same reason deps/1's are: what
the author wrote is what the manifest must record.
Sets name to commands in source's aliases/0.
Three cases, all of which the Svelte layer needs: the key is absent and is
added; the key exists and its command list is REPLACED; aliases/0 does not
exist and is created, with aliases: aliases() wired into project/0 — the
ordinary case on a mix new project, where a function nothing calls would be
inert, and silently so.
Replacement is why idempotency is structural here: after one apply the old commands are gone, so a textual presence test cannot tell "already applied" from "never applied".
Sets key to value in source's project/0.
value is SOURCE TEXT, not a term — "aliases()" is a call and ":renamed"
an atom, and a plugin records what it wants written rather than a term
this module would have to render back.