module: allow .ts in node_modules when .d.ts is present#63936
Open
GeoffreyBooth wants to merge 1 commit into
Open
module: allow .ts in node_modules when .d.ts is present#63936GeoffreyBooth wants to merge 1 commit into
GeoffreyBooth wants to merge 1 commit into
Conversation
Collaborator
|
Review requested:
|
By default, Node.js refuses to strip types from `.ts`/`.mts`/`.cts` files under `node_modules`, throwing `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`. This protects editor and `tsc` performance: a dependency that ships raw TypeScript without declarations forces consumers to infer types from its source. But the folder-based rule also blocks legitimate cases where trusted first-party TypeScript ends up under `node_modules`, such as monorepo deploys (`pnpm deploy`), packages from a private registry or a Git URL, and globally installed TypeScript CLIs. Add an experimental, opt-in flag `--experimental-strip-types-in-node-modules-with-declarations`. Under it, a TypeScript file under `node_modules` is stripped and executed when a co-located declaration file sits beside it (e.g. `foo.d.ts` next to `foo.ts`), the default layout emitted by `tsc --emitDeclarationOnly`. The declaration's presence signals that the author pre-computed the type boundaries downstream tooling relies on, so editors read declarations instead of inferring from raw source. The check is a single `stat`, and the flag is rejected unless type-stripping is enabled. Refs: nodejs#63853 Refs: nodejs#63869 Signed-off-by: Geoffrey Booth <webadmin@geoffreybooth.com>
e0bbcf0 to
1826070
Compare
anonrig
approved these changes
Jun 16, 2026
Member
|
I think this is a far better solution than any alternatives. Thank you. |
Collaborator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
By default, Node.js refuses to strip types from
.ts/.mts/.ctsfiles undernode_modules, throwingERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING. The restriction protects editor/tscperformance: a dependency that ships raw TypeScript without declarations forces consumers to infer types from its source (“slow types”).But using the folder as the boundary also blocks legitimate cases where trusted first-party TypeScript ends up under
node_modulesand does ship declarations:node_modules(pnpm deploy),Solution
Add an experimental, opt-in flag,
--experimental-strip-types-in-node-modules-with-declarations. Under it, a TypeScript file undernode_modulesis stripped and executed when a co-located declaration sits beside it (foo.d.tsnext tofoo.ts) — the default layout emitted bytsc --emitDeclarationOnly. The gate is a singlestat, and requires type-stripping to be enabled (rejected with--no-strip-types). Otherwise the existing error is thrown.Each of the blocked workflows already runs a build or publish step, so producing declarations is cheap: the same CI that assembles a
pnpm deploy, publishes to a private registry, or packs a global CLI can runtsc --emitDeclarationOnlyto emit.d.tsfiles beside the sources. That one artifact then both satisfies Node’s gate and ships ready-to-consume types, so the raw.tsruns with no separate JavaScript build — which is the thing these users are actually asking for.It also keeps the TypeScript team’s guarantee intact. The restriction exists to stop a dependency’s raw, undeclared source from dragging down a consumer’s editor and
tsc; by tying execution to the presence of a declaration, Node will only run raw.tsfrom a package that already provides one, so the consumer’s tooling always resolves a.d.tsand never falls back to inferring types across a dependency. The runtime permission is bound to the same artifact that keeps editing fast, rather than to where the code happens to live.Notes
exports"types"condition) is not recognized in this initial version; adding that is a small follow-up if the layout proves common..d.tsfiles.private: true(which can’t cover private-registry packages and doesn’t address editor performance).@nodejs/typescript @anonrig @marco-ippolito @RyanCavanaugh