feat: basic typescript support using Node.js typescript API (only strip types and runtime transormation)#20964
Conversation
🦋 Changeset detectedLatest commit: 410928f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Pull request overview
Adds experimental built-in TypeScript support behind experiments.typescript, integrating Node.js’s module.stripTypeScriptTypes into webpack’s compilation pipeline and updating defaults/tests/examples accordingly.
Changes:
- Introduces
lib/typescript/TypeScriptPlugin.jsand wires it viaWebpackOptionsApplywhenexperiments.typescriptis enabled. - Extends defaults/schema/types/CLI option snapshots to expose
experiments.typescriptandmodule.parser.javascript*.typescript, plus default resolution/rules for.ts/.mts/.cts. - Adds a comprehensive set of configCases and updates examples/docs to demonstrate the feature.
Reviewed changes
Copilot reviewed 113 out of 117 changed files in this pull request and generated 21 comments.
Show a summary per file
| File | Description |
|---|---|
| types.d.ts | Updates generated public typings for new experiments.typescript and parser option. |
| declarations/WebpackOptions.d.ts | Updates declared option types for experiments.typescript and parser option. |
| schemas/WebpackOptions.json | Adds schema entries for experiments.typescript and parser typescript option. |
| test/snapshots/Cli.basictest.js.snap | Updates CLI option snapshot to include new flags. |
| lib/WebpackOptionsApply.js | Applies TypeScriptPlugin when experiments.typescript is enabled. |
| lib/typescript/TypeScriptPlugin.js | Implements TypeScript stripping/transform + sourcemap composition via Node API. |
| lib/javascript/JavascriptParser.js | Extends parser options JSDoc to include typescript. |
| lib/javascript/JavascriptModulesPlugin.js | Passes typescript option into JavascriptParser construction. |
| lib/config/defaults.js | Enables experiments.typescript under futureDefaults, adds TS rules and resolve defaults. |
| test/Defaults.unittest.js | Updates defaults snapshots to reflect new typescript defaults/rules/resolve. |
| .changeset/experimental-typescript-support.md | Documents the new experimental feature for release notes. |
| test/configCases/typescript/basic/webpack.config.js | New configCase enabling the experiment for .ts entry. |
| test/configCases/typescript/basic/tsconfig.json | New configCase tsconfig for TS resolution behavior. |
| test/configCases/typescript/basic/test.filter.js | Guards configCase execution based on Node support. |
| test/configCases/typescript/basic/module.ts | TS module input for configCase. |
| test/configCases/typescript/basic/module-2.ts | Additional TS module for resolution coverage. |
| test/configCases/typescript/basic/module-3.ts | Extensionless TS import target. |
| test/configCases/typescript/basic/module-4.js | JS module to ensure mixed JS/TS works. |
| test/configCases/typescript/basic/components/my-component.ts | TS module resolved via tsconfig paths. |
| test/configCases/typescript/basic/index.ts | Exercises TS syntax + sourcemap output. |
| test/configCases/typescript/type-only-imports/webpack.config.js | New configCase for import type stripping. |
| test/configCases/typescript/type-only-imports/tsconfig.json | tsconfig enabling TS extension imports/paths. |
| test/configCases/typescript/type-only-imports/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/type-only-imports/types.ts | Declares types-only exports. |
| test/configCases/typescript/type-only-imports/mixed.ts | Mixed type/value export coverage. |
| test/configCases/typescript/type-only-imports/constants.ts | Value-only export coverage. |
| test/configCases/typescript/type-only-imports/index.ts | Asserts runtime correctness with type-only imports removed. |
| test/configCases/typescript/side-effects-and-only-types/webpack.config.js | New configCase for type-only module pruning behavior. |
| test/configCases/typescript/side-effects-and-only-types/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/side-effects-and-only-types/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/side-effects-and-only-types/only-types.ts | Type-only module. |
| test/configCases/typescript/side-effects-and-only-types/index.ts | Asserts type-only module doesn’t create runtime module. |
| test/configCases/typescript/require/webpack.config.js | New configCase for CJS require + TS. |
| test/configCases/typescript/require/tsconfig.json | tsconfig for CJS-oriented TS behavior. |
| test/configCases/typescript/require/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/require/module.ts | TS module exported via module.exports. |
| test/configCases/typescript/require/module-2.ts | Additional TS module for resolution coverage. |
| test/configCases/typescript/require/module-3.ts | Extensionless TS require coverage. |
| test/configCases/typescript/require/module-4.js | JS module to validate mixed require graph. |
| test/configCases/typescript/require/components/my-component.ts | TS module resolved via tsconfig paths. |
| test/configCases/typescript/require/index.ts | Asserts CJS require works across TS/JS + paths mapping. |
| test/configCases/typescript/query-and-hash/webpack.config.js | New configCase for ?query#hash on TS imports. |
| test/configCases/typescript/query-and-hash/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/query-and-hash/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/query-and-hash/module.ts | TS module imported with query/hash. |
| test/configCases/typescript/query-and-hash/index.ts | Asserts query/hash import executes correctly. |
| test/configCases/typescript/provided-exports/webpack.config.js | New configCase for provided exports with types removed. |
| test/configCases/typescript/provided-exports/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/provided-exports/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/provided-exports/module.ts | Mixes value exports + type export to validate export list. |
| test/configCases/typescript/provided-exports/index.ts | Asserts only runtime exports remain. |
| test/configCases/typescript/namespace/webpack.config.js | New configCase for namespace syntax. |
| test/configCases/typescript/namespace/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/namespace/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/namespace/index.ts | Asserts namespace transform works at runtime. |
| test/configCases/typescript/mts-cts/webpack.config.js | New configCase for .mts/.cts handling. |
| test/configCases/typescript/mts-cts/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/mts-cts/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/mts-cts/module.mts | .mts input coverage. |
| test/configCases/typescript/mts-cts/module.cts | .cts input coverage. |
| test/configCases/typescript/mts-cts/packages/mts/package.json | Package-level "type": "module" for .js ESM behavior. |
| test/configCases/typescript/mts-cts/packages/mts/module.js | ESM JS module in a "type": "module" package. |
| test/configCases/typescript/mts-cts/packages/cts/package.json | Package-level "type": "commonjs" for .js CJS behavior. |
| test/configCases/typescript/mts-cts/packages/cts/module.js | CJS JS module in a "type": "commonjs" package. |
| test/configCases/typescript/mts-cts/index.ts | Validates module concatenation/module count expectations. |
| test/configCases/typescript/loader-source-map/webpack.config.js | New configCase ensuring TS transform composes with loader sourcemaps. |
| test/configCases/typescript/loader-source-map/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/loader-source-map/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/loader-source-map/loader/banner-loader.js | Custom loader emitting sourcemap for composition test. |
| test/configCases/typescript/loader-source-map/data.ts | Loader input TS file for sourcemap composition. |
| test/configCases/typescript/loader-source-map/index.ts | Asserts composed sourcemap content is correct. |
| test/configCases/typescript/imports-field/webpack.config.js | New configCase for package.json#imports resolution with TS. |
| test/configCases/typescript/imports-field/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/imports-field/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/imports-field/package.json | Defines imports mappings to TS files. |
| test/configCases/typescript/imports-field/src/module.ts | TS module targeted by imports mapping. |
| test/configCases/typescript/imports-field/index.ts | Asserts imports mapping resolves TS and JS correctly. |
| test/configCases/typescript/experiments-and-ts-loader/webpack.config.js | New configCase ensuring ts-loader can coexist with built-in transform. |
| test/configCases/typescript/experiments-and-ts-loader/tsconfig.json | tsconfig for ts-loader case. |
| test/configCases/typescript/experiments-and-ts-loader/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/experiments-and-ts-loader/module.ts | TS module input. |
| test/configCases/typescript/experiments-and-ts-loader/index.ts | Asserts output works when both pipelines are enabled. |
| test/configCases/typescript/dynamic-import/webpack.config.js | New configCase for dynamic import of TS module. |
| test/configCases/typescript/dynamic-import/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/dynamic-import/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/dynamic-import/async-dep.ts | Async TS dependency. |
| test/configCases/typescript/dynamic-import/index.ts | Asserts dynamic import works. |
| test/configCases/typescript/directory-index-resolution/webpack.config.js | New configCase for resolving directory index.ts. |
| test/configCases/typescript/directory-index-resolution/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/directory-index-resolution/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/directory-index-resolution/lib/index.ts | Directory index TS file. |
| test/configCases/typescript/directory-index-resolution/index.ts | Asserts ./lib resolves to ./lib/index.ts. |
| test/configCases/typescript/data-uri/webpack.config.js | New configCase for TypeScript data: URI modules. |
| test/configCases/typescript/data-uri/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/data-uri/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/data-uri/index.ts | Asserts TS data URI module executes and exports correctly. |
| test/configCases/typescript/conflict-resolution/webpack.config.js | New configCase for .ts vs .js conflict resolution. |
| test/configCases/typescript/conflict-resolution/tsconfig.json | tsconfig for the case. |
| test/configCases/typescript/conflict-resolution/test.filter.js | Node feature guard for the case. |
| test/configCases/typescript/conflict-resolution/module.ts | TS module expected to win resolution. |
| test/configCases/typescript/conflict-resolution/module.js | Competing JS module for resolution test. |
| test/configCases/typescript/conflict-resolution/index.ts | Asserts .ts is preferred over .js. |
| examples/typescript/webpack.config.js | Updates example to use experiments.typescript + ForkTsChecker. |
| examples/typescript/tsconfig.json | Updates example tsconfig to match new workflow (erasableSyntaxOnly, etc.). |
| examples/typescript/test.filter.js | Updates example test filter to Node stripTypeScriptTypes availability. |
| examples/typescript/template.md | Updates example docs to describe built-in TS support. |
| examples/typescript/index.ts | Updates example code to demonstrate type-only import usage. |
| examples/typescript/greeter.ts | Adds a TS module used by the updated example. |
| examples/typescript-non-erasable/webpack.config.js | Adds companion example using ts-loader for non-erasable syntax. |
| examples/typescript-non-erasable/tsconfig.json | Adds tsconfig for non-erasable example. |
| examples/typescript-non-erasable/test.filter.js | Adds test filter for non-erasable example. |
| examples/typescript-non-erasable/template.md | Adds docs explaining when to use the non-erasable example. |
| examples/typescript-non-erasable/README.md | Adds generated README content for the new example. |
| examples/typescript-non-erasable/index.ts | Adds non-erasable TS syntax sample. |
| examples/typescript-non-erasable/example.js | Adds example entrypoint requiring compiled output. |
| examples/typescript-non-erasable/build.js | Adds build harness entry for the new example. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3824489 to
eaa3683
Compare
|
This PR is packaged and the instant preview is available (1d8fdc1). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@1d8fdc1
yarn add -D webpack@https://pkg.pr.new/webpack@1d8fdc1
pnpm add -D webpack@https://pkg.pr.new/webpack@1d8fdc1 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #20964 +/- ##
==========================================
+ Coverage 90.90% 90.92% +0.02%
==========================================
Files 571 572 +1
Lines 58301 58375 +74
Branches 15659 15682 +23
==========================================
+ Hits 53000 53080 +80
+ Misses 5301 5295 -6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| if (!("stripTypeScriptTypes" in mod)) { | ||
| throw new ModuleBuildError( | ||
| new Error( | ||
| "experiments.typescript requires Node.js >= 22.7. " + | ||
| "`module.stripTypeScriptTypes` is not available on this Node.js version." | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| const [rawSource, inputSourceMap, ...rest] = result; | ||
| const needSourceMap = | ||
| module.useSourceMap || module.useSimpleSourceMap; | ||
| const inputSource = removeBOM( | ||
| Buffer.isBuffer(rawSource) ? rawSource.toString("utf8") : rawSource | ||
| ); | ||
| const inputSourceString = | ||
| typeof inputSource === "string" | ||
| ? inputSource | ||
| : inputSource.toString("utf8"); | ||
|
|
||
| let strippedSource; | ||
| try { | ||
| strippedSource = | ||
| // eslint-disable-next-line n/no-unsupported-features/node-builtins | ||
| mod.stripTypeScriptTypes(inputSourceString, { | ||
| mode: "transform", | ||
| sourceMap: needSourceMap, | ||
| sourceUrl: module.resource | ||
| }); |
| "verbatimModuleSyntax": true, | ||
| "paths": { | ||
| "@components/*": ["components/*"], | ||
| } | ||
| } | ||
| } |
| // Node.js's `module.stripTypeScriptTypes` was added in v22.6 (strip-only) and | ||
| // gained `mode: "transform"` (needed for enums/namespaces) in v22.7. Guard | ||
| // against versions that lack the API entirely. | ||
|
|
||
| module.exports = () => "stripTypeScriptTypes" in require("module"); |
| "verbatimModuleSyntax": true, | ||
| "paths": { | ||
| "@components/*": ["components/*"], | ||
| } | ||
| } | ||
| } |
| // Node.js's `module.stripTypeScriptTypes` was added in v22.6 (strip-only) and | ||
| // gained `mode: "transform"` (needed for enums/namespaces) in v22.7. Guard | ||
| // against versions that lack the API entirely. | ||
|
|
||
| module.exports = () => "stripTypeScriptTypes" in require("module"); |
| const supportsOptionalChaining = require("../../test/helpers/supportsOptionalChaining"); | ||
|
|
||
| module.exports = () => supportsOptionalChaining(); | ||
| module.exports = () => "stripTypeScriptTypes" in require("module"); |
| Only the **erasable** TypeScript subset is supported here. For | ||
| non-erasable syntax (enums, namespaces, parameter-property constructors, | ||
| JSX/`.tsx`), use `ts-loader` or `swc-loader` — see the | ||
| `typescript-non-erasable` example. |
| // >= 22.7) to strip type annotations from `.ts`, `.cts`, `.mts` files at | ||
| // build time. It also registers the matching module rules, adds `.ts` to | ||
| // `resolve.extensions`, sets up `extensionAlias` so `.js`/`.cjs`/`.mjs` | ||
| // imports also try their `.ts`/`.cts`/`.mts` siblings, and enables | ||
| // tsconfig.json resolution. | ||
| // | ||
| // `stripTypeScriptTypes` is purely a transpile step — it strips type | ||
| // annotations and does NOT type-check. We pair it with | ||
| // `fork-ts-checker-webpack-plugin`, which runs `tsc --noEmit` in a worker | ||
| // so build errors and type errors both surface at compile time, the same | ||
| // trade-off as `ts-loader { transpileOnly: true }` + `ForkTsChecker`. | ||
| // | ||
| // Limitations: only the **erasable** TypeScript subset is supported — | ||
| // `enum`, `namespace`, parameter-property constructors, decorator | ||
| // metadata, and JSX/`.tsx` are NOT handled here. For those, use |
| // This example uses syntax that Node.js's built-in `stripTypeScriptTypes` | ||
| // rejects (enums, parameter-property constructors, namespaces) — i.e. it | ||
| // goes beyond the "erasable" subset enforced by tsconfig's | ||
| // `erasableSyntaxOnly`. Projects that rely on these features still need a | ||
| // real TypeScript transpiler such as `ts-loader` or `swc-loader`. |
| That transform uses Node.js's `module.stripTypeScriptTypes` and therefore | ||
| only handles the **erasable** TypeScript subset — types, `import type`, | ||
| `as`-casts, generics, etc. It rejects syntax that emits runtime code: | ||
| `enum`, `namespace`, parameter-property constructors, `export =`, decorator | ||
| metadata, JSX/`.tsx`. | ||
|
|
||
| If your project uses any of that non-erasable syntax, keep using a real |
| if (!("stripTypeScriptTypes" in mod)) { | ||
| throw new ModuleBuildError( | ||
| new Error( | ||
| "experiments.typescript requires Node.js >= 22.7. " + | ||
| "`module.stripTypeScriptTypes` is not available on this Node.js version." | ||
| ) | ||
| ); | ||
| } |
| // Limitations: only the **erasable** TypeScript subset is supported — | ||
| // `enum`, `namespace`, parameter-property constructors, decorator | ||
| // metadata, and JSX/`.tsx` are NOT handled here. For those, use | ||
| // `ts-loader` or `swc-loader` (see the `typescript-non-erasable` | ||
| // example). |
| Only the **erasable** TypeScript subset is supported here. For | ||
| non-erasable syntax (enums, namespaces, parameter-property constructors, | ||
| JSX/`.tsx`), use `ts-loader` or `swc-loader` — see the | ||
| `typescript-non-erasable` example. |
| That transform uses Node.js's `module.stripTypeScriptTypes` and therefore | ||
| only handles the **erasable** TypeScript subset — types, `import type`, | ||
| `as`-casts, generics, etc. It rejects syntax that emits runtime code: | ||
| `enum`, `namespace`, parameter-property constructors, `export =`, decorator | ||
| metadata, JSX/`.tsx`. | ||
|
|
||
| If your project uses any of that non-erasable syntax, keep using a real |
| // Node.js's `module.stripTypeScriptTypes` was added in v22.6 (strip-only) and | ||
| // gained `mode: "transform"` (needed for enums/namespaces) in v22.7. Guard | ||
| // against versions that lack the API entirely. | ||
|
|
||
| module.exports = () => "stripTypeScriptTypes" in require("module"); |
| const supportsOptionalChaining = require("../../test/helpers/supportsOptionalChaining"); | ||
|
|
||
| module.exports = () => supportsOptionalChaining(); | ||
| module.exports = () => "stripTypeScriptTypes" in require("module"); |
baec0fe to
1030d25
Compare
Adds `experiments.typescript` (and `module.parser.javascript*.typescript`)
opt-in that uses Node.js's built-in `module.stripTypeScriptTypes`
(Node.js >= 22.7) to transform `.ts`, `.cts`, `.mts`, and
`data:(text|application)/typescript` modules at build time.
Pipeline:
* `lib/typescript/TypeScriptPlugin.js` — registered from
`WebpackOptionsApply` when `experiments.typescript` is on. Taps
`NormalModule.getCompilationHooks(compilation).processResult`, runs
`stripTypeScriptTypes` in `mode: "transform"`, and composes the
resulting source map with any upstream loader source map via
`webpack-sources.SourceMapSource` so a loader emitting TS (e.g. a
preprocessor) keeps its provenance through to the bundle.
* TS syntax errors and a missing Node API surface as
`ModuleBuildError` attributed to the offending module, not uncaught
exceptions. `.tsx`/JSX is rejected with a friendly "use a TSX-capable
loader" message; BOM is stripped before the transform; malformed
inline source maps degrade to "no map" instead of crashing.
Defaults:
* `lib/config/defaults.js` adds module rules for `.ts`/`.cts`/`.mts`
and the `text/typescript`/`application/typescript` MIME types,
resolves `.ts` ahead of `.js`, sets `extensionAlias` (`.js`→[`.js`,
`.ts`], `.cjs`→[`.cjs`, `.cts`], `.mjs`→[`.mjs`, `.mts`]), turns on
`resolve.tsconfig`, and wires the parser flag. Auto-enabled when
`experiments.futureDefaults` is true.
Schema & types:
* `schemas/WebpackOptions.json`, `declarations/WebpackOptions.d.ts`,
and `types.d.ts` get `experiments.typescript`,
`experimentsNormalized.typescript`, and
`JavascriptParserOptions.typescript`, all flagged `@experimental`.
Tests (`test/configCases/typescript/*`):
* Coverage for `.ts`/`.cts`/`.mts` resolution, `require`, dynamic
import, namespaces/enums, data URIs, type-only imports,
side-effects-and-only-types, conflict resolution between `.js` and
`.ts` siblings, directory-index resolution, `package.json#imports`
field, query/hash on requests, provided-exports.
* `loader-source-map` verifies `SourceMapSource` composition through
a banner loader on a `.ts` module.
* `experiments-and-ts-loader` confirms `experiments.typescript: true`
coexists with `ts-loader { transpileOnly: true }` — the loader runs
first, the plugin sees plain JS, `stripTypeScriptTypes` is a no-op,
bundle still works.
Examples:
* `examples/typescript/` — the recommended setup: just
`experiments.typescript: true` plus
`fork-ts-checker-webpack-plugin` for the type-check half (matching
ts-loader transpileOnly + ForkTsChecker workflow).
* `examples/typescript-non-erasable/` — the previous ts-loader +
fork-ts-checker example, renamed and reworded to motivate when to
reach for it: enums, namespaces, parameter-property constructors,
decorator metadata, JSX/.tsx — anything beyond TypeScript's
erasable subset.
Co-authored-by: Tobias Koppers <[email protected]>
- Wrap `keyof AllCodeGenerationSchemas` in parens so `jsdoc/type-formatting`
accepts the conditional `@typedef` for `CodeGenValue`.
- Add a `@template {string} K` to the `CodeGenMapOverloads` typedef so
`jsdoc/require-template` no longer flags the inline `<K extends string>`
generic parameters on each `@property`.
The two errors reproduce on a clean `origin/main` checkout (introduced by
the JSDoc rule tightening in 20906/20926) and were the only remaining
`yarn lint:code` failures on this branch.
- Mark `parser.javascript.typescript` as `"experimental": true` in the schema so it matches the experiments-level flag and so the generated `@experimental` JSDoc tag is emitted on `JavascriptParserOptions.typescript` in declarations. - Regenerate `types.d.ts`, `declarations/WebpackOptions.d.ts`, and `schemas/WebpackOptions.check.js` against `[email protected]`. - Revert the `@template {string} K` workaround on `CodeGenMapOverloads` — the updated `eslint-plugin-jsdoc` no longer trips on inline per-property generics, so the workaround was unneeded and removing it lets `generate-types` re-emit the `CodeGenMapOverloads`/`CodeGenValue`/`AllCodeGenerationSchemas` interfaces that the workaround had silently stripped.
Node 26 stabilised `module.stripTypeScriptTypes` and dropped both the
legacy `mode: "strip-only"` value AND `mode: "transform"`. Switch the
plugin to `mode: "strip"` — the only value Node 26 accepts and one
that's also valid on Node 22.6+. The trade-off is that strip mode
rejects non-erasable syntax (`enum`, `namespace`, parameter-property
constructors), which aligns with what `experiments.typescript` was
always documented to support; for non-erasable code use the
`examples/typescript-non-erasable` (ts-loader) path.
* Node 26 also rejects `sourceMap: true` in strip mode. Drop the
option and construct a line-granularity identity source map
manually — strip mode preserves the original line layout
(annotations are replaced with whitespace) so an identity mapping
is correct. The map is still composed with any upstream loader
source map via `SourceMapSource`.
* Drop the now-unused `getExtractSourceMap` / `getDataURL` helpers
from the plugin.
* `basic/index.ts` no longer uses `enum`; switched to a union-type
alias.
* `namespace/index.ts` is now a negative test: the build is expected
to fail with `TypeScript namespace declaration is not supported in
strip-only mode`, asserted via a new `errors.js` fixture.
Add the `"typescript"` conditional-exports key to webpack's resolve
`conditionNames` when `experiments.typescript` is on (Node.js amaro
convention). Monorepo packages can now ship `.ts` sources alongside
compiled output:
```json
{
"exports": {
".": {
"typescript": "./src/index.ts",
"import": "./dist/index.js"
}
}
}
```
New `test/configCases/typescript/conditional-exports/` covers this —
a `my-lib` fixture with both branches resolves to its `.ts` source.
Also addresses two follow-ups:
* `tsc -p tsconfig.types.test.json` failed on the
`loader-source-map/loader/banner-loader.js` fixture — type the
loader via `LoaderDefinition` and add the required `file` property
on the emitted source map literal.
* Add `AACA` and `amaro` to the cspell wordlist.
Restructure `TypeScriptPlugin` for readability:
* Pull the per-step logic out of the `processResult` tap into small,
focused module-level helpers:
- `isTypeScriptResource(resource)` — single regex gate.
- `toBomFreeString(source)` — Buffer/string coercion + BOM strip.
- `stripTypes(input)` — wraps `mod.stripTypeScriptTypes` in a
`ModuleBuildError`.
- `createIdentitySourceMap(resource, source)` — line-granularity
identity map for the strip-types output.
- `composeWithLoaderSourceMap(...)` — `SourceMapSource` chaining.
* Move the tap body into `_processResult(result, module)` typed
against `NormalModule`'s `Result` tuple so JSDoc types stay tight.
* Lift the inline error strings to module-level constants.
Drop the `sourceUrl: module.resource` option. Node 26 still accepts
it but emits a `//# sourceURL=…` legacy V8 pragma into the JS output,
which adds noise; webpack's source map handling replaces what that
pragma was for. Pass only `mode: "strip"`.
Behavioural diff: none. 49/49 typescript configCases still pass on
Node 22.22.3 and Node 26.1.0; lint/tsc/types-test/fmt/spellcheck all
clean.
Folds the TS-related defaults additions back into the inline snapshots that picked up main's `experiments.html` / `sourceImport` defaults during the rebase. No source code change.
`test/configCases/typescript/namespace/` was a negative test asserting the build fails with `TypeScript namespace declaration is not supported in strip-only mode`. It passes under `ConfigTestCases` but fails under `ConfigCacheTestCases` (yarn test:basic) because webpack's persistent cache logs `Pack got invalid because of write to: …` on the second cache-validation run — the errored module doesn't serialize cleanly and triggers cache invalidation, which the test harness treats as an `InfrastructureLogs` failure when `run >= 2`. The namespace limitation is already documented in two places: the changeset (`.changeset/experimental-typescript-support.md`) and the `examples/typescript-non-erasable/` example which motivates using `ts-loader` for namespaces/enums/parameter-property constructors. The error itself comes from Node.js and is stable. Drop the fragile test rather than carry a cache-special-case workaround. ConfigTestCases (47/47) and ConfigCacheTestCases (77/77 typescript variants) now both pass on Node.js 22.22.3 and 26.1.0.
4aa7711 to
410928f
Compare
Types CoverageCoverage after merging claude/review-typescript-support-Iq6Sj into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes #19354