COMPILER
amo eject
The exit is part of the design. One command compiles your app a final time, copies the runtime into your project as readable source files, and rewrites every @amojs.dev/core import to a relative path. What remains is a vanilla JavaScript project you own completely — the framework you can uninstall.
Run it
$ npx amo eject src dist
amo eject — 2 compiled, 10 copied, 9 runtime files → dist (runtime: amo-runtime/)
runtime taken from node_modules/@amojs.dev/core/src
no bare "@amojs.dev/core" imports remain — deleting amo changes nothing.
The last line is a guarantee the repository's test suite enforces: it executes ejected output and asserts zero bare-specifier imports, so module resolution never touches node_modules again.
What changes in your code
Only the import lines. Templates are already compiled to direct DOM code — the same output amo build produces — so the rest of the file is untouched.
import { signal, html, mount } from '@amojs.dev/core';
import { signal, mount } from '../amo-runtime/runtime.js';
import { tpl as _$t, bindChild as _$child, bindEvent as _$event } from "../amo-runtime/compiled.js";
The runtime you receive
Core's own source files, verbatim — a few hundred lines of plain JavaScript with JSDoc: the signal graph, the bindings, mount, each, and the parser only if something still uses html`` at runtime. Readable is the point; it is the code you would have written, so you can keep writing it. The runtime is resolved from the ejected project's own installed @amojs.dev/core when there is one, so the output keeps the exact version the code was written against; the CLI prints where it came from.
The router comes along
If the app imports @amojs.dev/router, eject copies it into amo-runtime/router/ and rewrites those imports too. An ejected app keeps routing with both packages deleted.
Why it can exist
Most frameworks cannot offer this because their compiled output calls into a runtime that is itself the framework — a virtual DOM, a scheduler, a component system. AmoJS compiles to the DOM operations a person would write by hand, within 10% of hand-written bytes, so handing over the small runtime beneath them is the whole job. Every design decision that keeps that true — no component API, no Proxy, no file format, no global delegation — is also what keeps eject honest.