Packages

Zero-dependency bootstrap archive for SvEx projects.

Current section

Files

Jump to
sv_ex_new README.md
Raw

README.md

# sv_ex_new
The `mix sv_ex.new` bootstrap archive — one of
[SvEx](https://github.com/wimwian-org/sv_ex)'s two outputs, and the only one a
developer installs globally.
It generates a stock Elixir or Phoenix project, splices the `sv_ex` dev
dependency into its `mix.exs`, writes `target.exs`, and fetches and compiles
dependencies. Everything after that — applying plugins, recording what was
written — belongs to the hex package, which the generated project now depends
on.
```sh
mix archive.install hex sv_ex_new
mix sv_ex.new NAME --base otp|api|web --github-org ORG [--sv-ex-path PATH]
```
| flag | meaning |
| ---------------- | -------------------------------------------------------------------- |
| `--base` | `otp` (`mix new`), `api` or `web` (both `phx.new`). Required. |
| `--github-org` | Written into `target.exs`. Required — a silent default here would |
| | silently generate the wrong project. |
| `--app` | Override the OTP app name derived from NAME. |
| `--module` | Override the root module derived from NAME. |
| `--sv-ex-path` | Splice a `path:` dep instead of the hex requirement — for developing |
| | SvEx itself against a real generated project. |
`--base api` and `--base web` generate the **same** stock tree
(`phx.new --no-html --no-assets`). That is deliberate: `priv/baselines.exs`
records the `:web` baseline as `derived_from: :api` plus the `:web_layer`
plugin, so the asset pipeline arrives when that plugin is applied, never from
`phx.new`. Generating `:web` with HTML and assets would hand the plugin a tree
it did not derive against.
## Zero dependencies, on purpose
This archive declares **no hex requirements**, and `SvEx.ArchiveTest` in the
root suite asserts it against the built `.ez` and the unpacked hex metadata.
A Mix archive goes on the developer's global code path, where an
archive-resident module permanently shadows the real one a target project
depends on. Every dependency carries `only:` excluding `:prod` *and*
`runtime: false`; `runtime: false` alone is not enough, because a dep without
`only:` still emits a full entry in the package's `requirements`.
That same shadowing is why this archive is a build target of its own rather than
part of the root project — the root README's "One project, two outputs" carries
that half.
## Post-MVP continuation — verified working, deliberately NOT implemented
Once `mix sv_ex.gen` exists in the hex package, `Mix.Tasks.SvEx.New` continues
with this sequence. It is recorded **here** rather than in the task's
`@moduledoc` because `SvEx.BoundaryGuard` substring-scans this perimeter's
`lib/**/*.ex` — documentation and comments included — and two of these four
calls are on its banned list. Prose that names a banned API is
indistinguishable from a call to it, by design: the guard exists because a rule
expressed only in prose rots.
```elixir
File.cd!(target)
Mix.Project.pop()
Code.compile_file("mix.exs", ignore_module_conflict: true)
Mix.Task.run("sv_ex.gen", argv)
```
All four steps are required and were verified at Elixir 1.20.3.
- `Mix.Project.pop/0` is mandatory when a project is on the stack, and a
harmless no-op returning `nil` when none is. Omitting it produces a state that
looks correct and then fails at `deps.compile` with "cannot retrieve
dependencies information".
- The process must stay changed into the target directory for the rest of the
run, because `compile_path/0`, `deps_path/0` and `build_path/0` re-expand
against the current working directory on every call.
- `Mix.Task.get/1` is **not** a valid probe for the delegated task: it returns
`nil` while `Mix.Task.run/2` on the same name succeeds, because `run/2`
triggers loadpaths first.
Running this today buys nothing — `sv_ex.gen` does not exist — and
`deps.compile` prunes every archive off the code path before it could.
## Development
This is a separate Mix project. Its tests are **not** reached by the root
`mix test`:
```sh
cd installer/sv_ex_new
mix test
mix test --include toolchain # the tests that shell out to a real generator
```
`bin/check` at the repository root runs format, credo, coveralls, doctor and
dialyzer here as well as in the root — that is what "both outputs" means.