Coexistence with TypeScript
Nudo and tsc can share a repo. Nudo targets JavaScript (and stripped .ts sources); it does not replace the TS compiler for .ts-first packages.
Recipe 1: JS packages use Nudo, TS packages use tsc
apps/
web/ # TypeScript → tsc / ts-node
packages/
legacy-js/ # plain .js → nudo check + Nudo LSP
packages/legacy-js/package.json:
{
"nudo": {
"interface": { "autoBind": true },
"analysis": { "mode": "exports", "diagnostics": "default" }
}
}
CI for that package:
npx nudojs check packages/legacy-js/src
Do not run nudo check over apps/web/**/*.ts unless you intentionally strip types.
Recipe 2: Only src/**/*.js under Nudo
Recommended package.json#nudo.analysis for mixed JS/TS monorepos — this is the default coexistence recipe (same table as LSP Client Matrix):
{
"nudo": {
"analysis": {
"include": ["src/**/*.js"],
"exclude": [
"**/node_modules/**",
"**/dist/**",
"**/coverage/**",
"**/*.ts",
"**/*.tsx",
"**/*.d.ts"
],
"mode": "exports",
"diagnostics": "default"
}
}
}
Why these keys:
| Key | Role in a mixed repo |
|---|---|
include | Path whitelist relative to the project root. Empty (default) = every target path is eligible; in mixed repos always scope so tsserver owns .ts alone. |
exclude | Always keep node_modules / dist / coverage. Add **/*.ts / **/*.tsx / **/*.d.ts so opening a TS buffer does not schedule Nudo analysis. |
mode | exports (shipped default) analyzes export-bearing / sidecar / directive JS. See “When to use directives vs exports” below. |
diagnostics | Display tier for the IDE: default = error + warning minus noisy codes; errors = errors only; off silences the IDE display path (CLI nudo check still gates). |
.ts files stay with tsc. Nudo LSP provides hover/inlays for opened .js files that match include only when analysis.mode is exports or all.
Default note:
nudo.analysis.modedefaults to"exports"— files withexport/sidecar/directives are analyzed by the IDE. Set"all"for every target path, or"directives"for the conservative gate. Named-path CLI commands (nudo check src/lib.js) still analyze that file regardless of mode.
When to use mode=directives vs mode=exports
| Situation | Recommended mode | Why |
|---|---|---|
| Day-to-day monorepo IDE; JS packages are Nudo-owned | "exports" (default) | Export-bearing modules enter analysis; plain helper scripts without exports stay quiet. |
| First week onboarding a large JS tree; you only want files that already speak Nudo | "directives" | Only files with @nudo:* (or a sidecar via autoBind path in check) produce IDE diagnostics; lowest noise while inventory grows. |
| CI / scripts that must cover every JS target path | "all" (CLI/watch more than IDE) | Every .js/.mjs/.ts target path is analyzed; pair with tight include. |
| You need hover on non-export internals but zero diagnostics | keep "exports" + diagnostics: "off" for a subtree, or use named-path CLI | Display off does not disable nudo check. |
Recipe 3: Gradual contracts
- Infer first — no directives required.
- When a function needs a CI gate, add
fn.nudo.jsnext to it. nudo checkenforces only handwritten sidecars;@generatedsegments are facts + drift, not new obligations.
Recipe: mixed JS/TS monorepo (no double error storm)
Step-by-step for “I opened my monorepo and now both tsserver and Nudo shout”:
- Confirm ownership split. TypeScript owns
.ts/.tsx; Nudo owns Nudo-target JS (.js/.mjs, and stripped.tsonly if you intentionally analyze it). - Scope the JS package with Recipe 2
include/excludein the package that contains the JS you care about (nearestpackage.jsonwith anudokey wins when the engine walks up). - Start conservative if the tree is large. Temporarily set
"mode": "directives"so only annotated files light up; switch back to"exports"once include/exclude look right. - Reload the window (VS Code: Developer: Reload Window) so the language server re-reads
package.json#nudo.analysis. File-watchers also pick uppackage.jsonchanges, but a reload is the reliable step. - Open one known-good JS file (has
exportunderexportsmode, or a@nudo:*directive). You should see only Nudo diagnostics for that file, not a second copy of every tsserver error on.tssiblings. - Open a
.tsfile next to it. tsserver reports; Nudo should stay silent if exclude covers**/*.ts(or the file simply is not an include match). If Nudo still analyzes TS, yourexclude/includedid not land — re-check the package that actually holds anudokey. - If you still see duplicate messages on the same JS line, they are usually different tools (tsserver
checkJsvs Nudo). Either turn offcheckJsfor that package or keep Nudo onmode=directives/diagnostics=errorsso Nudo stays the narrow contract channel rather than a second checker. - Optional mute:
"diagnostics": "errors"or"off"on noisy packages; re-enable per package when contracts are ready.
Client matrix Known gaps row that points here: lsp-clients.md — “Secondary-server diagnostics may compete with tsserver noise”.
What not to do
- Do not expect Nudo to understand TypeScript type syntax (conditional types,
infer, etc.). - Do not point both tools at the same
.tssources with conflicting severity without splitting paths. - Do not treat
.d.tsprojection (nudo emit) as the source of truth — Abs is;.d.tsis a one-way compatibility channel. - Do not run the IDE on
mode: "all"across a whole mixed monorepo withoutinclude— that is how double storms start.
IDE
Install the Nudo VS Code extension alongside the built-in TS server. They coexist: TS handles .ts, Nudo analyzes .js according to nudo.analysis.mode. Shipped default is "exports" — files with export / sidecar / directives are analyzed in the IDE; set "all" for every target path, or "directives" to restore the conservative gate.
Maintainer packaging / release notes checklist: packages/vscode/RELEASE_CHECKLIST.md. Public LSP surface: packages/lsp/PUBLIC_API.md.
See also
- LSP Client Matrix — non-VS Code recipes + Known gaps tracking
- VS Code Extension
- Versioning & Releases — default flips that invent diagnostics are major on 1.x