Skip to content

Port node18 and nodenext changes #1070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
54 changes: 30 additions & 24 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ type Program interface {
FileExists(fileName string) bool
GetSourceFile(fileName string) *ast.SourceFile
GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.ModuleKind
GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, usageLocation *ast.StringLiteralLike) core.ResolutionMode
GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ModuleKind
GetResolvedModule(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule
GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule]
Expand Down Expand Up @@ -5016,7 +5017,10 @@ func (c *Checker) checkImportDeclaration(node *ast.Node) {
}
}
}
if c.isOnlyImportableAsDefault(moduleSpecifier, resolvedModule) && !hasTypeJsonImportAttribute(node) {
if !importClause.IsTypeOnly() &&
core.ModuleKindNode18 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext &&
c.isOnlyImportableAsDefault(moduleSpecifier, resolvedModule) &&
!hasTypeJsonImportAttribute(node) {
c.error(moduleSpecifier, diagnostics.Importing_a_JSON_file_into_an_ECMAScript_module_requires_a_type_Colon_json_import_attribute_when_module_is_set_to_0, c.moduleKind.String())
}
} else if c.compilerOptions.NoUncheckedSideEffectImports.IsTrue() && importClause == nil {
Expand Down Expand Up @@ -5113,29 +5117,32 @@ func (c *Checker) checkImportAttributes(declaration *ast.Node) {
if isTypeOnly && override != core.ResolutionModeNone {
return // Other grammar checks do not apply to type-only imports with resolution mode assertions
}
var mode core.ResolutionMode
if c.moduleKind == core.ModuleKindNodeNext {
if moduleSpecifier := getModuleSpecifierFromNode(declaration); moduleSpecifier != nil {
mode = c.getEmitSyntaxForModuleSpecifierExpression(moduleSpecifier)

if !c.moduleKind.SupportsImportAttributes() {
if isImportAttributes {
c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_node18_nodenext_or_preserve)
} else {
c.grammarErrorOnNode(node, diagnostics.Import_assertions_are_only_supported_when_the_module_option_is_set_to_esnext_node18_nodenext_or_preserve)
}
return
}
if mode != core.ModuleKindESNext && c.moduleKind != core.ModuleKindESNext && c.moduleKind != core.ModuleKindPreserve {
var message *diagnostics.Message
switch {
case isImportAttributes:
if c.moduleKind == core.ModuleKindNodeNext {
message = diagnostics.Import_attributes_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls

if c.moduleKind == core.ModuleKindNodeNext && !isImportAttributes {
c.grammarErrorOnNode(node, diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert)
return
}

if moduleSpecifier := getModuleSpecifierFromNode(declaration); moduleSpecifier != nil {
if c.getEmitSyntaxForModuleSpecifierExpression(moduleSpecifier) == core.ModuleKindCommonJS {
if isImportAttributes {
c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls)
} else {
message = diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_node18_nodenext_or_preserve
c.grammarErrorOnNode(node, diagnostics.Import_assertions_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls)
}
case c.moduleKind == core.ModuleKindNodeNext:
message = diagnostics.Import_assertions_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls
default:
message = diagnostics.Import_assertions_are_only_supported_when_the_module_option_is_set_to_esnext_node18_nodenext_or_preserve
return
}
c.grammarErrorOnNode(node, message)
return
}

if isTypeOnly {
c.grammarErrorOnNode(node, core.IfElse(isImportAttributes,
diagnostics.Import_attributes_cannot_be_used_with_type_only_imports_or_exports,
Expand Down Expand Up @@ -10210,7 +10217,7 @@ func (c *Checker) checkNewTargetMetaProperty(node *ast.Node) *Type {
}

func (c *Checker) checkImportMetaProperty(node *ast.Node) *Type {
if c.moduleKind == core.ModuleKindNode16 || c.moduleKind == core.ModuleKindNodeNext {
if core.ModuleKindNode16 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext {
sourceFileMetaData := c.program.GetSourceFileMetaData(ast.GetSourceFileOfNode(node).Path())
if sourceFileMetaData == nil || sourceFileMetaData.ImpliedNodeFormat != core.ModuleKindESNext {
c.error(node, diagnostics.The_import_meta_meta_property_is_not_allowed_in_files_which_will_build_into_CommonJS_output)
Expand Down Expand Up @@ -14092,10 +14099,9 @@ func (c *Checker) canHaveSyntheticDefault(file *ast.Node, moduleSymbol *ast.Symb
}

func (c *Checker) getEmitSyntaxForModuleSpecifierExpression(usage *ast.Node) core.ResolutionMode {
// !!!
// if isStringLiteralLike(usage) {
// return host.getEmitSyntaxForUsageLocation(ast.GetSourceFileOfNode(usage), usage)
// }
if ast.IsStringLiteralLike(usage) {
return c.program.GetEmitSyntaxForUsageLocation(ast.GetSourceFileOfNode(usage), usage)
}
return core.ModuleKindNone
}

Expand Down Expand Up @@ -14446,7 +14452,7 @@ func (c *Checker) resolveExternalModule(location *ast.Node, moduleReference stri
if resolvedModule.IsExternalLibraryImport && !(tspath.ExtensionIsTs(resolvedModule.Extension) || resolvedModule.Extension == tspath.ExtensionJson) {
c.errorOnImplicitAnyModule(false /*isError*/, errorNode, mode, resolvedModule, moduleReference)
}
if c.moduleResolutionKind == core.ModuleResolutionKindNode16 || c.moduleResolutionKind == core.ModuleResolutionKindNodeNext {
if c.moduleKind == core.ModuleKindNode16 || c.moduleKind == core.ModuleKindNode18 {
isSyncImport := c.program.GetDefaultResolutionModeForFile(importingSourceFile) == core.ModuleKindCommonJS && ast.FindAncestor(location, ast.IsImportCall) == nil ||
ast.FindAncestor(location, ast.IsImportEqualsDeclaration) != nil
overrideHost := ast.FindAncestor(location, ast.IsResolutionModeOverrideHost)
Expand Down
3 changes: 2 additions & 1 deletion internal/checker/grammarchecks.go
Original file line number Diff line number Diff line change
Expand Up @@ -1211,7 +1211,7 @@ func (c *Checker) checkGrammarForInOrForOfStatement(forInOrOfStatement *ast.ForI
c.diagnostics.Add(createDiagnosticForNode(forInOrOfStatement.AwaitModifier, diagnostics.X_for_await_loops_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module))
}
switch c.moduleKind {
case core.ModuleKindNode16, core.ModuleKindNodeNext:
case core.ModuleKindNode16, core.ModuleKindNode18, core.ModuleKindNodeNext:
sourceFileMetaData := c.program.GetSourceFileMetaData(sourceFile.Path())
if sourceFileMetaData != nil && sourceFileMetaData.ImpliedNodeFormat == core.ModuleKindCommonJS {
c.diagnostics.Add(createDiagnosticForNode(forInOrOfStatement.AwaitModifier, diagnostics.The_current_file_is_a_CommonJS_module_and_cannot_use_await_at_the_top_level))
Expand Down Expand Up @@ -1724,6 +1724,7 @@ func (c *Checker) checkGrammarAwaitOrAwaitUsing(node *ast.Node) bool {
}
switch c.moduleKind {
case core.ModuleKindNode16,
core.ModuleKindNode18,
core.ModuleKindNodeNext:
sourceFileMetaData := c.program.GetSourceFileMetaData(sourceFile.Path())
if sourceFileMetaData != nil && sourceFileMetaData.ImpliedNodeFormat == core.ModuleKindCommonJS {
Expand Down
10 changes: 2 additions & 8 deletions internal/compiler/fileloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,17 +512,12 @@ func importSyntaxAffectsModuleResolution(options *core.CompilerOptions) bool {
}

func getEmitSyntaxForUsageLocationWorker(fileName string, meta *ast.SourceFileMetaData, usage *ast.Node, options *core.CompilerOptions) core.ResolutionMode {
if options == nil {
// This should always be provided, but we try to fail somewhat
// gracefully to allow projects like ts-node time to update.
return core.ResolutionModeNone
}

if ast.IsRequireCall(usage.Parent) || ast.IsExternalModuleReference(usage.Parent) && ast.IsImportEqualsDeclaration(usage.Parent.Parent) {
return core.ModuleKindCommonJS
}
fileEmitMode := ast.GetEmitModuleFormatOfFileWorker(fileName, options, meta)
if ast.IsImportCall(ast.WalkUpParenthesizedExpressions(usage.Parent)) {
if ast.ShouldTransformImportCall(fileName, options, ast.GetImpliedNodeFormatForEmitWorker(fileName, options, meta)) {
if ast.ShouldTransformImportCall(fileName, options, fileEmitMode) {
return core.ModuleKindCommonJS
} else {
return core.ModuleKindESNext
Expand All @@ -535,7 +530,6 @@ func getEmitSyntaxForUsageLocationWorker(fileName string, meta *ast.SourceFileMe
// file, until/unless declaration emit can indicate a true ESM import. On the
// other hand, writing CJS syntax in a definitely-ESM file is fine, since declaration
// emit preserves the CJS syntax.
fileEmitMode := ast.GetEmitModuleFormatOfFileWorker(fileName, options, meta)
if fileEmitMode == core.ModuleKindCommonJS {
return core.ModuleKindCommonJS
} else {
Expand Down
4 changes: 4 additions & 0 deletions internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,10 @@ func (p *Program) GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.Mod
return ast.GetEmitModuleFormatOfFileWorker(sourceFile.FileName(), p.Options(), p.GetSourceFileMetaData(sourceFile.Path()))
}

func (p *Program) GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, location *ast.StringLiteralLike) core.ResolutionMode {
return getEmitSyntaxForUsageLocationWorker(sourceFile.FileName(), p.sourceFileMetaDatas[sourceFile.Path()], location, p.Options())
}

func (p *Program) GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ResolutionMode {
return ast.GetImpliedNodeFormatForEmitWorker(sourceFile.FileName(), p.Options(), p.GetSourceFileMetaData(sourceFile.Path()))
}
Expand Down
10 changes: 8 additions & 2 deletions internal/core/compileroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func (options *CompilerOptions) GetEmitScriptTarget() ScriptTarget {
return options.Target
}
switch options.GetEmitModuleKind() {
case ModuleKindNode16:
case ModuleKindNode16, ModuleKindNode18:
return ScriptTargetES2022
case ModuleKindNodeNext:
return ScriptTargetESNext
Expand All @@ -173,7 +173,7 @@ func (options *CompilerOptions) GetModuleResolutionKind() ModuleResolutionKind {
return options.ModuleResolution
}
switch options.GetEmitModuleKind() {
case ModuleKindNode16:
case ModuleKindNode16, ModuleKindNode18:
return ModuleResolutionKindNode16
case ModuleKindNodeNext:
return ModuleResolutionKindNodeNext
Expand Down Expand Up @@ -357,6 +357,12 @@ func (moduleKind ModuleKind) IsNonNodeESM() bool {
return moduleKind >= ModuleKindES2015 && moduleKind <= ModuleKindESNext
}

func (moduleKind ModuleKind) SupportsImportAttributes() bool {
return ModuleKindNode18 <= moduleKind && moduleKind <= ModuleKindNodeNext ||
moduleKind == ModuleKindPreserve ||
moduleKind == ModuleKindESNext
}

type ResolutionMode = ModuleKind // ModuleKindNone | ModuleKindCommonJS | ModuleKindESNext

const (
Expand Down
5 changes: 5 additions & 0 deletions internal/transformers/importelision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ type fakeProgram struct {
getSourceFile func(FileName string) *ast.SourceFile
}

// GetEmitSyntaxForUsageLocation implements checker.Program.
func (p *fakeProgram) GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, usageLocation *ast.StringLiteralLike) core.ResolutionMode {
panic("unimplemented")
}

// CommonSourceDirectory implements checker.Program.
func (p *fakeProgram) CommonSourceDirectory() string {
panic("unimplemented")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ export default _default;
declare const _default: string;
export default _default;
//// [index.d.ts]
export declare const mod: typeof import("./case0.js") | typeof import("./case1.js") | typeof import("./caseFallback.js");
export declare const mod: {
default: typeof import("./case0.js");
} | {
default: typeof import("./case1.js");
} | {
default: typeof import("./caseFallback.js");
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,4 @@
+declare const _default: string;
export default _default;
//// [index.d.ts]
-export declare const mod: {
- default: typeof import("./case0.js");
-} | {
- default: typeof import("./case1.js");
-} | {
- default: typeof import("./caseFallback.js");
-};
+export declare const mod: typeof import("./case0.js") | typeof import("./case1.js") | typeof import("./caseFallback.js");
export declare const mod: {
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ export default 'fallback';

=== /index.ts ===
export const mod = await (async () => {
>mod : typeof import("./case0.js") | typeof import("./case1.js") | typeof import("./caseFallback.js")
>await (async () => { const x: number = 0; switch (x) { case 0: return await import("./case0.js"); case 1: return await import("./case1.js"); default: return await import("./caseFallback.js"); }})() : typeof import("./case0.js") | typeof import("./case1.js") | typeof import("./caseFallback.js")
>(async () => { const x: number = 0; switch (x) { case 0: return await import("./case0.js"); case 1: return await import("./case1.js"); default: return await import("./caseFallback.js"); }})() : Promise<typeof import("./case0.js") | typeof import("./case1.js") | typeof import("./caseFallback.js")>
>(async () => { const x: number = 0; switch (x) { case 0: return await import("./case0.js"); case 1: return await import("./case1.js"); default: return await import("./caseFallback.js"); }}) : () => Promise<typeof import("./case0.js") | typeof import("./case1.js") | typeof import("./caseFallback.js")>
>async () => { const x: number = 0; switch (x) { case 0: return await import("./case0.js"); case 1: return await import("./case1.js"); default: return await import("./caseFallback.js"); }} : () => Promise<typeof import("./case0.js") | typeof import("./case1.js") | typeof import("./caseFallback.js")>
>mod : { default: typeof import("./case0.js"); } | { default: typeof import("./case1.js"); } | { default: typeof import("./caseFallback.js"); }
>await (async () => { const x: number = 0; switch (x) { case 0: return await import("./case0.js"); case 1: return await import("./case1.js"); default: return await import("./caseFallback.js"); }})() : { default: typeof import("./case0.js"); } | { default: typeof import("./case1.js"); } | { default: typeof import("./caseFallback.js"); }
>(async () => { const x: number = 0; switch (x) { case 0: return await import("./case0.js"); case 1: return await import("./case1.js"); default: return await import("./caseFallback.js"); }})() : Promise<{ default: typeof import("./case0.js"); } | { default: typeof import("./case1.js"); } | { default: typeof import("./caseFallback.js"); }>
>(async () => { const x: number = 0; switch (x) { case 0: return await import("./case0.js"); case 1: return await import("./case1.js"); default: return await import("./caseFallback.js"); }}) : () => Promise<{ default: typeof import("./case0.js"); } | { default: typeof import("./case1.js"); } | { default: typeof import("./caseFallback.js"); }>
>async () => { const x: number = 0; switch (x) { case 0: return await import("./case0.js"); case 1: return await import("./case1.js"); default: return await import("./caseFallback.js"); }} : () => Promise<{ default: typeof import("./case0.js"); } | { default: typeof import("./case1.js"); } | { default: typeof import("./caseFallback.js"); }>

const x: number = 0;
>x : number
Expand All @@ -31,22 +31,22 @@ export const mod = await (async () => {
>0 : 0

return await import("./case0.js");
>await import("./case0.js") : typeof import("./case0.js")
>import("./case0.js") : Promise<typeof import("./case0.js")>
>await import("./case0.js") : { default: typeof import("./case0.js"); }
>import("./case0.js") : Promise<{ default: typeof import("./case0.js"); }>
>"./case0.js" : "./case0.js"

case 1:
>1 : 1

return await import("./case1.js");
>await import("./case1.js") : typeof import("./case1.js")
>import("./case1.js") : Promise<typeof import("./case1.js")>
>await import("./case1.js") : { default: typeof import("./case1.js"); }
>import("./case1.js") : Promise<{ default: typeof import("./case1.js"); }>
>"./case1.js" : "./case1.js"

default:
return await import("./caseFallback.js");
>await import("./caseFallback.js") : typeof import("./caseFallback.js")
>import("./caseFallback.js") : Promise<typeof import("./caseFallback.js")>
>await import("./caseFallback.js") : { default: typeof import("./caseFallback.js"); }
>import("./caseFallback.js") : Promise<{ default: typeof import("./caseFallback.js"); }>
>"./caseFallback.js" : "./caseFallback.js"
}
})();
Loading
Loading