Skip to content

ref(typescript): Default to node16 module/moduleResolution#21520

Open
mydea wants to merge 12 commits into
developfrom
ref/tsconfig-node16-default
Open

ref(typescript): Default to node16 module/moduleResolution#21520
mydea wants to merge 12 commits into
developfrom
ref/tsconfig-node16-default

Conversation

@mydea

@mydea mydea commented Jun 15, 2026

Copy link
Copy Markdown
Member

Summary

Make module: "node16" + moduleResolution: "node16" the default in the base config (packages/typescript/tsconfig.json, previously moduleResolution: "node" with an implicit module: "es6"), then clean up the per-package overrides accordingly.

This is a dev-time-only change. In this repo tsc never emits the shipped JavaScript — Rollup does (every package's exports point at /build/...). tsc is used only to (a) type-check our source and (b) emit .d.ts. So module/moduleResolution affect only how strictly our own source is type-checked; they do not change any shipped artifact.

Verification: built .d.ts (and, for Angular, the full ng-packagr output) old-vs-new for representatives of each category and diffed — byte-identical. JS bundles are Rollup-emitted and provably independent of these options. All build/types configs type-check with no config-validity errors.

node16/node16 vs esnext/bundler — and why each, where

The two coherent pairings model different consumers (mixing them, e.g. esnext+node16, is rejected by tsc):

  • node16/node16 models Node's real resolver: honors exports/imports conditional resolution and the CJS/ESM file-mode split. Stricter, "how Node actually sees it." Best fit for packages meant to run in Node.
  • esnext/bundler models how a bundler (Rollup/webpack/Vite/esbuild) resolves: reads exports conditions but skips the Node CJS/ESM mode split and extension rules. Best fit for packages that are always run through a bundler (browser / framework / edge).

Since everything here is bundled and consumers only ever see built /build files + hand-authored exports maps, the choice is about type-checking fidelity, not output. We default to node16 because the majority of packages are Node/server packages — they get stricter, Node-realistic checking for free — and we keep bundler where it was already deliberately configured for bundled targets.

What changed

Base (packages/typescript/tsconfig.json): module: node16 + moduleResolution: node16.

Removed now-redundant overrides (these inherit the base):

  • Node16 overrides dropped from node, node-core, tanstackstart.
  • Redundant lib: ["es2020"] dropped from node, tanstackstart, types, server-utils, node-native, profiling-node (base already sets lib: ["es2020"]; node-core keeps its extra ES2021.WeakRef).

Added explicit overrides where a package previously set only one of the pair and leaned on the old base default (otherwise they'd hit the invalid node16+bundler / esnext+node16 combos):

  • module: "esnext" added to keep their bundler: browser, astro, integration-shims, react-router.
  • moduleResolution: "bundler" added to keep their esnext: cloudflare, hono, deno, feedback, node-native, profiling-node, replay-canvas, replay-internal, replay-worker.

Per-package exceptions surfaced by node16

node16 honors exports maps and ESM/CJS file modes that the old node (node10) resolver ignored. Three packages needed targeted handling:

  • svelteTS2307: imports deep internal subpaths (e.g. svelte/types/compiler/preprocess) that Svelte's exports map doesn't declare. Still needed for Svelte 3 support, so pinned to the legacy node resolver (filesystem lookup). .d.ts unchanged.
  • solidTS1479/TS1541: a CommonJS package importing ESM-only deps (solid-js). Bundled, so set esnext/bundler. .d.ts unchanged.
  • angular — built with ng-packagr, which pins TypeScript 4.6.4. node16 didn't exist until TS 4.7, so TS 4.6 rejects the literal anywhere in the inherited config chain (even when overridden). Resolved by not extending the shared base for this package — its tsconfig now inlines the effective base options and uses esnext/node (TS-4.6-compatible). It is the only package that doesn't extend the base; a comment documents why. Full ng-packagr build output verified byte-identical.

Net effect: the default now matches the Node-package majority, the bundled packages explicitly declare they're bundler-targeted instead of silently relying on a default, and nothing we ship changes.

🤖 Generated with Claude Code

@mydea mydea marked this pull request as ready for review June 15, 2026 08:27
@mydea mydea requested review from a team as code owners June 15, 2026 08:27
@mydea mydea requested review from JPeer264, Lms24, andreiborza, chargome, logaretm and nicohrubec and removed request for a team June 15, 2026 08:27
Comment thread packages/node-native/tsconfig.json Outdated
mydea and others added 11 commits June 15, 2026 14:04
… imports

Svelte's `exports` map does not declare deep subpaths like
`svelte/types/compiler/preprocess`, which our source imports (still needed
for Svelte 3 support). `node16` resolution honors the `exports` map and
rejects these, so pin the svelte package back to the legacy `node` resolver,
which resolves them via filesystem lookup. Emitted `.d.ts` is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`@sentry/angular` is `"type": "module"`, so under the new `node16` base
resolution its relative imports would require explicit `.js` extensions
(TS2835), breaking the `ng build`. Like our other framework packages
(react, vue) it's compiled via a bundler (ng-packagr), so set
`module: esnext` + `moduleResolution: bundler` explicitly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`@sentry/solid` imports ESM-only deps (`solid-js`, `solid-js/web`). Under the
new `node16` base resolution, a CommonJS file can't `require` an ESM module
(TS1479/TS1541). Like our other framework packages it's bundled, so set
`module: esnext` + `moduleResolution: bundler` explicitly. Emitted `.d.ts` is
unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`@sentry/angular` is built with ng-packagr, which pins TypeScript 4.6 — that
version predates `node16` and rejects the literal anywhere in the inherited
config chain, even when the package overrides module/moduleResolution. So
stop extending the shared base entirely and inline the effective base options
here, using `esnext`/`node` (which TS 4.6 supports). Build output verified
byte-identical to before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ests

The TS 3.8 compatibility job patches the base tsconfig before running, but
only stripped `noUncheckedIndexedAccess`. With the base now defaulting to
`node16` module/moduleResolution (added in TS 4.7), TS 3.8 fails on the
unrecognized literal. Downgrade both to TS 3.8-compatible `esnext`/`node` in
the `use-ts-3_8.js` setup scripts for node and node-core integration tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous attempt set `module: esnext` when patching the base tsconfig for
TS 3.8. But the integration-test scenarios run via `node -r ts-node/register`,
so ts-node compiled them to ESM and they crashed on load as CommonJS — every
scenario timed out. Restore the pre-`node16` base shape instead: drop `module`
(ts-node falls back to CommonJS, as before) and keep `moduleResolution: node`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea mydea force-pushed the ref/tsconfig-node16-default branch from 6f65ac6 to 6229192 Compare June 15, 2026 12:04

@logaretm logaretm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in #19435 we change the resolution to bundler for ts 6.0/7.0 compatibility, should we go for that instead?

Note: The TS upgrade guide recommends either bundler or nodenext with bundler being a more suitable option for us.

@mydea

mydea commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

in #19435 we change the resolution to bundler for ts 6.0/7.0 compatibility, should we go for that instead?

Note: The TS upgrade guide recommends either bundler or nodenext with bundler being a more suitable option for us.

ah, I see. I guess then we can go ahead and just move it to bundler/esnext everywhere already, should be fine I think!

@logaretm

logaretm commented Jun 15, 2026

Copy link
Copy Markdown
Member

ah, I see. I guess then we can go ahead and just move it to bundler/esnext everywhere already, should be fine I think!

Cool, bundler isn't safe till we upgrade to TS 6.0 tho (from what i understood from ts docs, maybe i'm wrong), so they go hand in hand. Otherwise nodenext seems like the other value we can upgrade to now. Maybe we just do 6.0 & bundler at v11 instead of changing anything now?

Everything we ship is bundled (Rollup emits the JS; tsc only emits .d.ts, which
is byte-identical either way), so the base config now defaults to esnext/bundler
— a uniform, modern default that the bundled majority of packages already used.
This also avoids the node16 literal, which older toolchains (Angular's pinned
TS 4.6, the TS 3.8 compat job) can't parse.

Removes all now-redundant module/moduleResolution overrides across packages,
leaving only genuine exceptions:
- svelte: `node` resolution (Svelte 3 imports deep paths not in its exports map)
- angular: inlined config with esnext/node (ng-packagr pins TS 4.6)
- nextjs/remix tsconfig.test.json: Node16 (deliberately exercise node resolution)
- astro tsconfig.dev.json: unchanged dev config

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants