Skip to content

Comments

Fix ESM resolution by adding file extensions to imports#5656

Open
iclanton wants to merge 4 commits intomicrosoft:mainfrom
iclanton:include-file-extensions-in-imports
Open

Fix ESM resolution by adding file extensions to imports#5656
iclanton wants to merge 4 commits intomicrosoft:mainfrom
iclanton:include-file-extensions-in-imports

Conversation

@iclanton
Copy link
Member

Summary

This PR migrates all relative import/export specifiers across the repo to include explicit .ts/.tsx file extensions, enabling TypeScript's rewriteRelativeImportExtensions compiler option. This ensures that compiled .js output includes proper file extensions in import specifiers, fixing Node.js ESM resolution when consuming Rush Stack packages via their ESM entry points.

Further work toward resolving #5644

Details

Approach:

  1. Reverted the earlier addition of "node" entries from exports in package.json files (Temporarily add "node" entries to exports in package.json files #5650), restoring ESM entry points for packages that had them.

  2. Enabled rewriteRelativeImportExtensions in the local rig tsconfig.json configurations and added the import/extensions ESLint rule to local-eslint-config to enforce .ts extensions in source imports going forward.

  3. Added .ts extensions to all relative imports (~1,251 files changed) across the repo so TypeScript can rewrite them to .js in compiled output.

  4. Updated @rushstack/heft-typescript-plugin to:

    • Inject rewriteRelativeImportExtensions and allowImportingTsExtensions in tsconfigLoader
    • Enhance wrapWriteFile to rewrite .js specifiers when jsExtensionOverride is set
    • Remove the old addJsExtensionToImports approach (now handled natively by the TypeScript compiler)
  5. require() calls use .js extensions directly (TypeScript does not rewrite these).

  6. ESLint rule disabled for ts-loader and composite-test projects where it doesn't apply.

  7. New esm-node-import-test build-test added to validate that ESM imports resolve correctly at runtime under Node.js.

Backwards compatibility: The addJsExtensionToImports option in @rushstack/heft-typescript-plugin is removed (breaking for anyone using it), replaced by the standard TypeScript rewriteRelativeImportExtensions mechanism. This is reflected as a minor version bump.

How it was tested

  • Verified rush build completes successfully across the repo with the new import extensions and rewriteRelativeImportExtensions enabled.
  • The new esm-node-import-test build-test validates that ESM imports with .js extensions resolve correctly at runtime under Node.js (uses --no-warnings to suppress expected MODULE_TYPELESS_PACKAGE_JSON warnings on Node 22+).
  • Confirmed ESLint import/extensions rule catches missing .ts extensions in new code.

Impacted documentation

None.

…mportExtensions

Migrates all relative import/export specifiers across the repo to include
explicit .ts/.tsx file extensions. This enables TypeScript's
rewriteRelativeImportExtensions compiler option to produce correct .js
extensions in compiled output, fixing ESM resolution under Node.js.

Also includes:
- heft-typescript-plugin: inject rewriteRelativeImportExtensions and
  allowImportingTsExtensions in tsconfigLoader; enhance wrapWriteFile
  to rewrite .js specifiers when jsExtensionOverride is set; remove
  the old addJsExtensionToImports approach
- ESLint import/extensions rule added to local-eslint-config
- ESLint rule disabled for ts-loader and composite-test projects
- require() calls use .js extensions (not rewritten by TS)
- esm-node-import-test: new build-test validating ESM imports work
- rush.json: register esm-node-import-test project

Fixes microsoft#5644
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Needs triage

Development

Successfully merging this pull request may close these issues.

2 participants