Skip to content

Conversation

jakebailey
Copy link
Member

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR ports TypeScript PR 61805 to address changes related to the module=node20 configuration, focusing on updates to module resolution behavior and module format handling in Node.js 20 environments.

Key changes include:

  • Removal of legacy module resolution validation errors
  • Updated module output formats for ESM vs CJS contexts
  • Improved import assertion to import attributes error messaging
  • Corrected module resolution strategy selection

Reviewed Changes

Copilot reviewed 300 out of 645 changed files in this pull request and generated no comments.

File Description
Multiple baseline error files Removed TS5109 errors requiring Node16 module resolution with Node20 module setting
Multiple baseline JavaScript output files Updated module compilation output to use proper ESM syntax instead of CommonJS when appropriate
Import assertion baseline files Changed error messages from import assertions to import attributes deprecation warnings
Module resolution trace files Updated to show Node16 resolution strategy instead of Bundler

@jakebailey
Copy link
Member Author

Hm, data race...

@jakebailey
Copy link
Member Author

I can't figure out how to deal with the circularity errors when compiling concurrently. These are the kinds of errors that we have been silencing by skipping the tests (concurrentSkippedErrorBaselines), which would defeat the purpose of these checks. I am not sure if this means the tests are "bad" themselves for introducing those circularities, or if there's a bug in my (or someone else's) ported code that's causing the issue.

@jakebailey
Copy link
Member Author

The symbol baselines doing something like:

image

Implies that we are somehow choosing the wrong import when compiling concurrently, so I hope it's just that kind of bug, but the race detector does not detect anything.

@jakebailey jakebailey force-pushed the jabaile/node20 branch 2 times, most recently from 53306a6 to 38d667c Compare October 7, 2025 16:56
Comment on lines +15001 to +15032
targetFile := core.Find(moduleSymbol.Declarations, ast.IsSourceFile)
usageMode := c.getEmitSyntaxForModuleSpecifierExpression(reference)
var exportModuleDotExportsSymbol *ast.Symbol
if namespaceImport != nil && targetFile != nil &&
core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext &&
usageMode == core.ModuleKindCommonJS &&
c.program.GetImpliedNodeFormatForEmit(targetFile.AsSourceFile()) == core.ModuleKindESNext {
exportModuleDotExportsSymbol = c.getExportOfModule(symbol, ast.InternalSymbolNameModuleExports, namespaceImport, dontResolveAlias)
}
if exportModuleDotExportsSymbol != nil {
if !suppressInteropError && symbol.Flags&(ast.SymbolFlagsModule|ast.SymbolFlagsVariable) == 0 {
c.error(referencingLocation, diagnostics.This_module_can_only_be_referenced_with_ECMAScript_imports_Slashexports_by_turning_on_the_0_flag_and_referencing_its_default_export, "esModuleInterop")
}
if c.compilerOptions.GetESModuleInterop() && c.hasSignatures(typ) {
return c.cloneTypeAsModuleType(exportModuleDotExportsSymbol, typ, referenceParent)
}
return exportModuleDotExportsSymbol
}

isEsmCjsRef := targetFile != nil && isESMFormatImportImportingCommonjsFormatFile(usageMode, c.program.GetImpliedNodeFormatForEmit(targetFile.AsSourceFile()))
if c.compilerOptions.GetESModuleInterop() || isEsmCjsRef {
if c.hasSignatures(typ) || c.getPropertyOfTypeEx(typ, ast.InternalSymbolNameDefault, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) != nil || isEsmCjsRef {
var moduleType *Type
if typ.Flags()&TypeFlagsStructuredType != 0 {
moduleType = c.getTypeWithSyntheticDefaultImportType(typ, symbol, moduleSymbol, reference)
} else {
moduleType = c.createDefaultPropertyWrapperForModule(symbol, symbol.Parent, nil)
}
return c.cloneTypeAsModuleType(symbol, moduleType, referenceParent)
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bit is the code that's causing the circularity. Perhaps that's why it was commented out originally?

@andrewbranch
Copy link
Member

The circularities are real. I believe what’s going on here is this: suppose you have a big circle of imports: a.ts → b.ts → c.ts → d.ts → e.ts → a.ts. In single-threaded mode, we always start with a.ts because of file order / source order, and we go off down the chain:

  • resolve import of b.ts
    • resolve import of c.ts
      • resolve import of d.ts
        • resolve import of e.ts
          • resolve import of a.ts - already in progress! set import of a.ts to any
          • return any
        • return any
      • return any
    • return any
  • return any
  • import of b.ts got set to any while resolving import of c.ts, so a circularity occurred
  • report an error on the import of b.ts

With this algorithm, the circle can be arbitrarily long, but we only ever report an error on the place where we start, which is semi-arbitrary but deterministic by file order, source order, etc. But if we divvy up all the files between multiple independent checkers, there’s a strong chance multiple of them will start resolving the chain in different places, resulting in the same error being reported in a different file.

I’m not sure what we want to actually do about this.

  • It would be easy to issue the circularity error on every file instead of an arbitrary point, but that would probably be annoying
  • Otherwise, we would have to save the resolution stack in order to apply some arbitrary but deterministic way of picking where to issue the error regardless of where we started. (We don’t currently maintain that stack at all; we just notice a mutation happened and know a circularity occurred, but we have no breadcrumbs for it. It shouldn’t be hard to add, though.)

@jakebailey
Copy link
Member Author

jakebailey commented Oct 8, 2025

That's what I thought, unfortunately.

As a non-fix, should the tests be fixed to not use a circular reference? It seems like those which break aren't intentionally testing that behavior.

@andrewbranch
Copy link
Member

Yeah, I think that would be fine for now.

@jakebailey
Copy link
Member Author

microsoft/TypeScript#62568 does that, and it indeed makes this PR pass tests when applied to the submodule.

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.

2 participants