[heft-typescript-plugin] Add emitModulePackageJson option for ESM output folders#5665
Open
iclanton wants to merge 3 commits intomicrosoft:mainfrom
Open
[heft-typescript-plugin] Add emitModulePackageJson option for ESM output folders#5665iclanton wants to merge 3 commits intomicrosoft:mainfrom
iclanton wants to merge 3 commits intomicrosoft:mainfrom
Conversation
…put folders
Add a new 'emitModulePackageJson' option for 'additionalModuleKindsToEmit'
entries in typescript.json. When enabled, the TypeScript plugin writes a
package.json with the appropriate "type" field ("module" for ESNext/ES2015,
"commonjs" for CommonJS) to the output folder after compilation.
This ensures Node.js correctly interprets .js files in ESM output folders
like lib-esm/, fixing named import failures on Node 18 where .js files
without a nearest "type": "module" package.json are treated as CommonJS.
Enabled by default in local-node-rig and decoupled-local-node-rig for
their lib-esm output.
| } | ||
|
|
||
| const isEsm: boolean = | ||
| moduleKindToEmit.moduleKind === ts.ModuleKind.ES2015 || |
Contributor
There was a problem hiding this comment.
There are a lot more module kinds that are ESM, e.g. ES2020. Probably should be doing a range comparison.
Or, more likely, select module types that aren't ESM, and make the rest be ESM.
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.
Summary
When a project uses
additionalModuleKindsToEmitto emit ESM output to alib-esm/folder, Node.js 18 fails to load the.jsfiles as ES modules because there is no"type": "module"in the nearest package.json. This adds a newemitModulePackageJsonoption that automatically writes a package.json with the correct"type"field into the output folder after compilation.Fixes the
esm-node-import-testbuild failure on Node 18.Details
On Node 18, when a package's
exportsmap points"import"to./lib-esm/index.jsbut the package lacks"type": "module", Node treats.jsfiles as CommonJS. This causesSyntaxError: Named export 'Path' not founderrors. Node 20.17+ handles this via automatic module detection, but Node 18 (which is still in the supportednodeSupportedVersionRange) does not.The fix adds
emitModulePackageJsonas an optional boolean onadditionalModuleKindsToEmitentries in typescript.json. When set totrue, after TypeScript compilation completes, the plugin writes a package.json containing{ "type": "module" }(for ESNext/ES2015 module kinds) or{ "type": "commonjs" }(for CommonJS) into the output folder. This is emitted in all three code paths: normal build, solution build, and watch mode.An alternative considered was using
.mjsfile extensions viaemitMjsExtensionForESModule, but that requires updating allexportsmaps and import specifiers. Another alternative was a per-projectcopy-files-plugintask to copy a marker file, but that doesn't scale across the monorepo.This change is fully backwards compatible —
emitModulePackageJsondefaults tofalse.The option is enabled in
local-node-riganddecoupled-local-node-rigfor theirlib-esmoutput.How it was tested
@rushstack/heft-typescript-pluginand verified the new option is compiled into the schema and builder@rushstack/node-core-libraryand confirmed package.json with{ "type": "module" }is emittedesm-node-import-test(heft test --clean) on Node 18 (v18.20.8), Node 20 (v20.20.0), and Node 24 (v24.13.1) — all pass@rushstack/heft-typescript-plugin's own build succeeds with no errors@rushstack/node-core-library's 241 tests pass with no regressions