Module system
The kit ships as the full repo; the setup wizard prunes the modules you
don’t keep. What makes that safe is a contract: every prunable module
carries a module.json manifest declaring everything it owns (code,
npm dependencies, env vars, manifest permissions, docs). One declaration
removes all of it together.
Manifests live at the package root for workspace packages
(packages/gate/module.json) or under
apps/extension/modules/<id>/module.json for app-level modules,
validated against tooling/config/module.schema.json.
Anatomy of a manifest
Section titled “Anatomy of a manifest”{ "$schema": "../../tooling/config/module.schema.json", "id": "billing", "title": "Billing & entitlements", "description": "Stripe checkout/portal routes, webhook-written entitlements, useEntitlement('paid').", "files": ["packages/core-billing/**", "backend/functions/src/billing/**"], "dependsOn": ["auth"], "npmDependencies": { "@extensionstart/core-billing": "workspace:*" }, "env": [{ "name": "WXT_API_URL", "description": "deployed Functions base URL" }], "permissions": [], "wiring": ["apps/extension/entrypoints/background/index.ts"], "docs": []}filesglobs are repo-root-relative; a module can own files outside its package.dependsOnis by module ID. The resolver keeps dependencies of any kept module and drops dependents of any dropped one.- Core modules set
"removable": false; the wizard never offers to prune them. - Env vars and permissions listed by several modules are pruned only when no kept module lists them.
permissionsis why the generated manifest shrinks when you prune: each module declares thechrome.*permissions it needs, and a smaller permission surface means a faster store review.
Wiring markers
Section titled “Wiring markers”A module’s code often touches shared files it doesn’t own: the background import order, the messaging protocol, surface roots. Those touchpoints carry marker comments so the pruner can strip them:
- Line marker: a
// module:<id>suffix (or{/* module:<id> */}in JSX) removes that line when<id>is dropped. - Block marker: everything from
module:<id>:startthroughmodule:<id>:end(inclusive) is removed. Blocks of different modules may nest.
Each manifest lists the shared files carrying its markers under wiring.
The pass criterion: any prune combination leaves pnpm typecheck and
pnpm lint green with zero dangling imports.
Backend files stay
Section titled “Backend files stay”Manifests list backend files as documentation of ownership, but the
pruner leaves backend/** in place. The Hono app is one self-contained
function and unused routes are harmless. Delete them manually if you want
a minimal backend.
Add your own module
Section titled “Add your own module”-
Create the manifest (
apps/extension/modules/<id>/module.jsonfor an app-level feature) withid,title,description, andfilesglobs for everything the module owns. -
Tag each touchpoint in shared files with a
// module:<id>line marker or amodule:<id>:start/endblock, and list those files underwiring. -
Declare
npmDependencies,env(names must exist inapps/extension/.env.example),permissions, anddependsOn. -
Keep the manifest in sync: adding a file, dependency, env var, or permission means updating
module.jsonin the same change. -
Prove it prunes cleanly:
Terminal window pnpm create extstart --dry-run --keep none # your module in the plan?Then, on a scratch branch, run a real prune that drops your module and check
pnpm typecheckandpnpm lintstay green.