Skip to content

Fix auto-imports inserting import in CJS files under node16/node20/nodenext#3328

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/fix-auto-imports-insert-import-statements
Draft

Fix auto-imports inserting import in CJS files under node16/node20/nodenext#3328
Copilot wants to merge 2 commits intomainfrom
copilot/fix-auto-imports-insert-import-statements

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

With module: "node20" (or node16/node18/nodenext) and a tsconfig.json, auto-imports incorrectly emit import statements in CJS files instead of require() calls.

Two problems in computeShouldUseRequire():

  • moduleDetection: "force" (the default for Node module kinds) sets ExternalModuleIndicator on all files unconditionally, so the CJS/ESM content heuristic can't distinguish CJS files from ESM files
  • GetEmitModuleKind() < ModuleKindES2015 returns false for Node module kinds (ModuleKindNode20 = 102) even though the file may be CJS

Both cause the function to return false (use import) before reaching GetImpliedNodeFormatForEmit(), which would have given the correct answer based on file extension and package.json "type".

Fix

Add an early exit for Node module kinds that delegates directly to GetImpliedNodeFormatForEmit():

moduleKind := v.program.Options().GetEmitModuleKind()
if core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext {
    return v.program.GetImpliedNodeFormatForEmit(v.importingFile) != core.ModuleKindESNext
}

For Node module kinds, the runtime format is authoritatively determined by file extension and package.json "type" — file content indicators are unreliable under force detection.

Tests

Two new fourslash tests covering the exact scenario from the issue:

  • CJS file with existing module.exports syntax + module: "node20" + package.json "type": "commonjs"
  • Empty CJS file (same config) — the edge case where ExternalModuleIndicator alone causes the wrong result

… module kinds

When using module: "node16"/"node18"/"node20"/"nodenext", moduleDetection
defaults to "force", which sets ExternalModuleIndicator on ALL files regardless
of actual syntax. This made the file content indicators unreliable for
determining CJS vs ESM, and the simple module kind comparison
(GetEmitModuleKind() < ModuleKindES2015) also failed because Node module kinds
have numeric values >= 100.

The fix adds an early check for Node module kinds that uses
GetImpliedNodeFormatForEmit(), which correctly determines the runtime module
format based on file extension and package.json "type" field.

Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/9405eb29-fef7-4989-a974-aba9130e0d67

Co-authored-by: andrewbranch <3277153+andrewbranch@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix auto-imports inserting import statement into CJS files Fix auto-imports inserting import in CJS files under node16/node20/nodenext Apr 2, 2026
Copilot AI requested a review from andrewbranch April 2, 2026 16:56
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.

Auto-imports insert import statement into CJS files

2 participants