# 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.
