Skip to content

fix(plugin-metadata): emit .js extensions for dynamic imports under nodenext (#3364)#3404

Open
maruthang wants to merge 1 commit intonestjs:masterfrom
maruthang:fix/issue-3364-nodenext-ext-a754525b
Open

fix(plugin-metadata): emit .js extensions for dynamic imports under nodenext (#3364)#3404
maruthang wants to merge 1 commit intonestjs:masterfrom
maruthang:fix/issue-3364-nodenext-ext-a754525b

Conversation

@maruthang
Copy link
Copy Markdown
Contributor

PR Checklist

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

Issue Number: #3364

PluginMetadataGenerator printed visitor-supplied dynamic import("./hello.dto") ts.CallExpressions and the typeImports strings verbatim. Under moduleResolution: 'node16' | 'nodenext', ESM specifiers must include explicit file extensions — so the generated metadata.ts failed type-check and threw ERR_MODULE_NOT_FOUND at runtime.

Two prior attempts (#3378, #3365) tried to suppress the error with @ts-nocheck; both were closed because the maintainer wanted the underlying generation fixed instead.

What is the new behavior?

The generator now reads programRef.getCompilerOptions().moduleResolution. When it's Node16 or NodeNext, it walks the metadata tree:

  • For each import() CallExpression whose specifier is a relative path without an extension, rewrite the specifier to append .js.
  • For the typeImports map (raw strings of the shape await import(...)), regex-rewrite the inner import(\"...\") specifier the same way.

Bare specifiers, absolute paths, and already-extended specifiers (.js, .mjs, .cjs, .json, .ts, .tsx, etc.) are skipped, so classic node10 and bundler resolution are unaffected.

Five small pure helpers are exported alongside the rewrite logic, covered by 18 new unit tests in test/lib/compiler/plugins/plugin-metadata-generator.spec.ts. Full local suite: 21 suites, 166 passed, 3 pre-existing skips, 0 failures.

Does this PR introduce a breaking change?

  • Yes
  • No

The rewrite only activates when moduleResolution is Node16 or NodeNext. Default node resolution behavior is identical to before.

Other information

The same fix likely applies to v12.0.0. Happy to open a backport PR if desired.

Closes #3364

…odenext resolution (nestjs#3364)

Under `moduleResolution: 'node16'` or `'nodenext'`, ESM-style imports
require explicit file extensions. The PluginMetadataGenerator previously
emitted dynamic `import("./hello.dto")` calls without an extension,
causing TypeScript diagnostics in the generated metadata file and
`ERR_MODULE_NOT_FOUND` at runtime when the metadata is executed.

Detect the consuming project's `moduleResolution` from the supplied
`ts.Program` and, when it is Node16 or NodeNext, walk both the
collected metadata AST and the visitor `typeImports` map to append
`.js` to relative specifiers that lack an extension. Bare specifiers,
absolute paths, and already-extended specifiers (`.js`, `.mjs`, `.cjs`,
`.json`, ...) are left untouched, so projects using classic / node10 /
bundler resolution are unaffected.

Closes nestjs#3364
@kamilmysliwiec
Copy link
Copy Markdown
Member

could you please target v12.0.0

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.

PluginMetadataGenerator generates dynamic imports without file extension with moduleResolution nodenext

2 participants