Migrating existing JS
Nudo does not require annotations up front. The migration path is code-first: keep the implementation, generate reviewable contracts, tighten by hand, then lock CI.
existing JS → --draft → human review → *.nudo.js → check / doctor / IDE
0. Prerequisites
pnpm add -D @nudojs/cli @nudojs/lsp # or npx @nudojs/cli
# optional project config in package.json
{
"nudo": {
"analysis": {
"mode": "exports",
"diagnostics": "default",
"evalMissingSlot": "off"
},
"interface": { "autoBind": true }
}
}
See Coexistence with TypeScript if the repo already has tsc.
1. Inventory
nudo interface src/
Prints every top-level export with its tier:
| Tier | Meaning | Migration action |
|---|---|---|
handwritten | Already contracted (sidecar / @nudo:refine) | Leave; enforce with check |
generated | Call-site domains frozen into @generated | Refresh with --emit when usage changes |
implicit | Inference only — display | Draft candidates |
2. Draft contracts from code
nudo interface --draft src/lib.js
nudo interface --draft --write src/lib.js --fn greet --fn double
# or IDE: CodeLens ⚡ draft interface / VS Code “Nudo: Draft Interface”
Evidence in the draft module (never invents check obligations):
| Evidence | Source | Use |
|---|---|---|
callsite / directive | Observed arguments | Best starting point |
body | Fields the implementation reads | Suggestions only — fill types by hand |
symbolic | generalize return shape | Return slot when no cases |
| omitted slots | No evidence | TODO comments |
Output lands in src/lib.nudo.draft.js — not ambient-loaded. Copy reviewed lines into src/lib.nudo.js.
Worked sample: docs/examples/interface-draft/.
Runnable demo (temp dir: inventory → draft → accept → check):
pnpm run migrate-demo
# from the nudo monorepo — scripts/migrate-demo.sh
3. Review checklist
For each draft export:
- Params — are call-site shapes too narrow for future callers? Widen (
number()vslit(21)). - Body-read fields — promote
shape({ name })only if every caller must provide them (that is a real obligation). - Returns — match the contract you want enforced, not every historical result.
- Handwritten clash — if
*.nudo.jsalready binds the name, keep handwritten (draft never overwrites).
Example accept:
// src/lib.nudo.js
import { fn, number, shape, string } from "@nudojs/core";
export const double = fn({ x: number() }, number());
export const greet = fn({ user: shape({ name: string() }) }, string());
4. Gate with check
nudo check src/
nudo check src/lib.js --json # CI
- Violations on handwritten contracts fail the build.
- generated segments report drift as warnings (facts + refresh), not as new obligations.
- implicit display never invents errors by itself.
Optional evaluation hints (default off):
{ "nudo": { "analysis": { "evalMissingSlot": "warning" } } }
Surfaces nudo:missing-slot when evaluation hits a closed object shape without a field — a hint to tighten drafts, not an automatic contract.
5. Freeze call-site domains (optional)
nudo interface --emit src/lib.js --fn double --callsites test/
nudo interface --emit src/lib.js --dry-run --exit-on-diff # CI drift gate
--emit writes @generated segments from observed arguments. Use it for usage sites you trust; keep handwritten contracts for API surface you want enforced.
6. IDE / agents
| Surface | Entry |
|---|---|
| Hover tier | ● interface / handwritten|generated|implicit |
| CodeLens | persist / update / draft |
| VS Code | Output channel commands |
| Agent | nudo.interface, nudo.interface.draft, nudo.check |
| CLI | nudo interface, --draft, --emit, nudo check, nudo doctor |
7. Ongoing health
nudo doctor src/ # uncovered fns, drift, analysis errors
nudo infer src/lib.js --callsites test/ --emit-cases=update
Pin package versions per Versioning & Releases (0.x minors may break; 1.x core/service/cli follow SemVer).
What not to do
- Do not expect body
if (p.foo)reads to become check obligations (C0 model). - Do not commit
*.nudo.draft.jsas if it were a live contract — copy into*.nudo.jsfirst. - Do not treat
generatedsnapshots as the full API you intend to enforce — promote to handwritten when it matters.