From 7b02db847c19e0a899d5b7d83b3405f14bead791 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Feb 2026 00:11:15 +0000
Subject: [PATCH 1/2] Initial plan
From f25b21993f330881a5ba5b2b38bdebf5677a42cc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Feb 2026 00:50:04 +0000
Subject: [PATCH 2/2] Port TypeScript PR #63038: Consult referenced project
options for synthetic default export eligibility
In canHaveSyntheticDefault, when GetImpliedNodeFormatForEmit returns no
result for a declaration file, check if the file comes from a project
reference. If so, use GetEmitModuleFormatOfFile to determine the module
format from the referenced project's compiler options. When the usage is
ESM and the target module kind is in the ES2015-ESNext range, deny
synthetic defaults.
Also adds a new test case "referenced project with esnext module
disallows synthetic default imports" and updates affected baselines.
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
---
internal/checker/checker.go | 14 ++++
internal/execute/tsctests/tsc_test.go | 35 ++++++++++
...ved-json-files-and-emits-them-correctly.js | 56 ++++++++++-----
...nterop-uses-referenced-project-settings.js | 2 +
...ule-disallows-synthetic-default-imports.js | 70 +++++++++++++++++++
5 files changed, 160 insertions(+), 17 deletions(-)
create mode 100644 testdata/baselines/reference/tsc/projectReferences/referenced-project-with-esnext-module-disallows-synthetic-default-imports.js
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index 9dac7a0507b..47e7511e49f 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -14465,6 +14465,20 @@ func (c *Checker) canHaveSyntheticDefault(file *ast.Node, moduleSymbol *ast.Symb
// are ESM, there cannot be a synthetic default.
return false
}
+ // For other files (not node16/nodenext with impliedNodeFormat), check if we can determine
+ // the module format from project references
+ if targetMode == core.ModuleKindNone && file.AsSourceFile().IsDeclarationFile {
+ // Try to get the project reference - try both source file mapping and output file mapping
+ // since declaration files can be mapped either way depending on how they're resolved
+ if c.program.GetRedirectForResolution(file.AsSourceFile()) != nil || c.program.GetProjectReferenceFromOutputDts(file.AsSourceFile().Path()) != nil {
+ // This is a declaration file from a project reference, so we can determine
+ // its module format from the referenced project's options
+ targetModuleKind := c.program.GetEmitModuleFormatOfFile(file.AsSourceFile())
+ if usageMode == core.ModuleKindESNext && core.ModuleKindES2015 <= targetModuleKind && targetModuleKind <= core.ModuleKindESNext {
+ return false
+ }
+ }
+ }
}
if !c.allowSyntheticDefaultImports {
return false
diff --git a/internal/execute/tsctests/tsc_test.go b/internal/execute/tsctests/tsc_test.go
index ad71e344f74..e5db89897bc 100644
--- a/internal/execute/tsctests/tsc_test.go
+++ b/internal/execute/tsctests/tsc_test.go
@@ -3939,6 +3939,41 @@ func TestTscProjectReferences(t *testing.T) {
},
commandLineArgs: []string{"--p", "app", "--pretty", "false"},
},
+ {
+ subScenario: "referenced project with esnext module disallows synthetic default imports",
+ files: FileMap{
+ "/home/src/workspaces/project/lib/tsconfig.json": stringtestutil.Dedent(`
+ {
+ "compilerOptions": {
+ "composite": true,
+ "declaration": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "rootDir": "src",
+ "outDir": "dist"
+ },
+ "include": ["src"]
+ }`),
+ "/home/src/workspaces/project/lib/src/utils.ts": "export const test = () => 'test';",
+ "/home/src/workspaces/project/lib/dist/utils.d.ts": "export declare const test: () => string;",
+ "/home/src/workspaces/project/app/tsconfig.json": stringtestutil.Dedent(`
+ {
+ "compilerOptions": {
+ "module": "esnext",
+ "moduleResolution": "bundler"
+ },
+ "references": [
+ { "path": "../lib" }
+ ]
+ }`),
+ "/home/src/workspaces/project/app/index.ts": stringtestutil.Dedent(`
+ import TestSrc from '../lib/src/utils'; // Error
+ import TestDecl from '../lib/dist/utils'; // Error
+ console.log(TestSrc.test());
+ console.log(TestDecl.test());`),
+ },
+ commandLineArgs: []string{"--p", "app", "--pretty", "false"},
+ },
{
subScenario: "referencing ambient const enum from referenced project with preserveConstEnums",
files: FileMap{
diff --git a/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-with-non-moved-json-files-and-emits-them-correctly.js b/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-with-non-moved-json-files-and-emits-them-correctly.js
index f0454940016..6cc2746e15f 100644
--- a/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-with-non-moved-json-files-and-emits-them-correctly.js
+++ b/testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-with-non-moved-json-files-and-emits-them-correctly.js
@@ -91,8 +91,17 @@ Output::
[7m2[0m export = x;
[7m [0m [91m~~~~~~~~~~~[0m
+[96msub-project/index.js[0m:[93m1[0m:[93m8[0m - [91merror[0m[90m TS1192: [0mModule '"/home/src/workspaces/solution/common/index"' has no default export.
-Found 2 errors in the same file, starting at: common/index.ts[90m:1[0m
+[7m1[0m import mod from '../common';
+[7m [0m [91m ~~~[0m
+
+
+Found 3 errors in 2 files.
+
+Errors Files
+ 2 common/index.ts[90m:1[0m
+ 1 sub-project/index.js[90m:1[0m
//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
///
@@ -119,9 +128,7 @@ interface Symbol {
declare const console: { log(msg: any): void; };
//// [/home/src/workspaces/out/sub-project-2/index.d.ts] *new*
export declare function getVar(): {
- key: {
- val: number;
- };
+ key: any;
};
//// [/home/src/workspaces/out/sub-project-2/index.js] *new*
@@ -134,7 +141,7 @@ export function getVar() {
}
//// [/home/src/workspaces/out/sub-project-2/tsconfig.tsbuildinfo] *new*
-{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","../sub-project/index.d.ts","../../solution/sub-project-2/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",{"version":"56ecb5738c72a131a1514873df723721-import { m } from '../sub-project/index';\n\nconst variable = {\n key: m,\n};\n\nexport function getVar() {\n return variable;\n}","signature":"f6a0b1edad82fddabb5c98ad5da1660d-export declare function getVar(): {\n key: {\n val: number;\n };\n};\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"esModuleInterop":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1]],"latestChangedDtsFile":"./index.d.ts"}
+{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","../sub-project/index.d.ts","../../solution/sub-project-2/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",{"version":"56ecb5738c72a131a1514873df723721-import { m } from '../sub-project/index';\n\nconst variable = {\n key: m,\n};\n\nexport function getVar() {\n return variable;\n}","signature":"cb28999e3dc9364c7b77f755d2449d70-export declare function getVar(): {\n key: any;\n};\n","impliedNodeFormat":1}],"fileIdsList":[[2]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"esModuleInterop":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1]],"latestChangedDtsFile":"./index.d.ts"}
//// [/home/src/workspaces/out/sub-project-2/tsconfig.tsbuildinfo.readable.baseline.txt] *new*
{
"version": "FakeTSVersion",
@@ -166,18 +173,18 @@ export function getVar() {
},
{
"fileName": "../sub-project/index.d.ts",
- "version": "b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",
- "signature": "b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",
+ "version": "ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",
+ "signature": "ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",
"impliedNodeFormat": "CommonJS"
},
{
"fileName": "../../solution/sub-project-2/index.js",
"version": "56ecb5738c72a131a1514873df723721-import { m } from '../sub-project/index';\n\nconst variable = {\n key: m,\n};\n\nexport function getVar() {\n return variable;\n}",
- "signature": "f6a0b1edad82fddabb5c98ad5da1660d-export declare function getVar(): {\n key: {\n val: number;\n };\n};\n",
+ "signature": "cb28999e3dc9364c7b77f755d2449d70-export declare function getVar(): {\n key: any;\n};\n",
"impliedNodeFormat": "CommonJS",
"original": {
"version": "56ecb5738c72a131a1514873df723721-import { m } from '../sub-project/index';\n\nconst variable = {\n key: m,\n};\n\nexport function getVar() {\n return variable;\n}",
- "signature": "f6a0b1edad82fddabb5c98ad5da1660d-export declare function getVar(): {\n key: {\n val: number;\n };\n};\n",
+ "signature": "cb28999e3dc9364c7b77f755d2449d70-export declare function getVar(): {\n key: any;\n};\n",
"impliedNodeFormat": 1
}
}
@@ -203,19 +210,17 @@ export function getVar() {
]
},
"latestChangedDtsFile": "./index.d.ts",
- "size": 1592
+ "size": 1546
}
//// [/home/src/workspaces/out/sub-project/index.d.ts] *new*
-export declare const m: {
- val: number;
-};
+export declare const m: any;
//// [/home/src/workspaces/out/sub-project/index.js] *new*
import mod from '../common';
export const m = mod;
//// [/home/src/workspaces/out/sub-project/tsconfig.tsbuildinfo] *new*
-{"version":"FakeTSVersion","root":[4],"fileNames":["lib.es2025.full.d.ts","../../solution/common/obj.json","../../solution/common/index.d.ts","../../solution/sub-project/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"d47747c9a3b20f363d6de91e2bd8ed62-{\n \"val\": 42,\n}"},"641f5162aeaa035322008b19df89c663-import x = require(\"./obj.json\");\nexport = x;\n",{"version":"4c69d0c670e9dc788b5e107f277aa8ab-import mod from '../common';\n\nexport const m = mod;","signature":"b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n","impliedNodeFormat":1}],"fileIdsList":[[2],[3]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"esModuleInterop":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1],[4,2]],"latestChangedDtsFile":"./index.d.ts"}
+{"version":"FakeTSVersion","root":[4],"fileNames":["lib.es2025.full.d.ts","../../solution/common/obj.json","../../solution/common/index.d.ts","../../solution/sub-project/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"d47747c9a3b20f363d6de91e2bd8ed62-{\n \"val\": 42,\n}"},"641f5162aeaa035322008b19df89c663-import x = require(\"./obj.json\");\nexport = x;\n",{"version":"4c69d0c670e9dc788b5e107f277aa8ab-import mod from '../common';\n\nexport const m = mod;","signature":"ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n","impliedNodeFormat":1}],"fileIdsList":[[2],[3]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declaration":true,"esModuleInterop":true,"outDir":"..","rootDir":"../../solution","skipLibCheck":true},"referencedMap":[[3,1],[4,2]],"semanticDiagnosticsPerFile":[[4,[{"pos":7,"end":10,"code":1192,"category":1,"messageKey":"Module_0_has_no_default_export_1192","messageArgs":["\"/home/src/workspaces/solution/common/index\""]}]]],"latestChangedDtsFile":"./index.d.ts"}
//// [/home/src/workspaces/out/sub-project/tsconfig.tsbuildinfo.readable.baseline.txt] *new*
{
"version": "FakeTSVersion",
@@ -264,11 +269,11 @@ export const m = mod;
{
"fileName": "../../solution/sub-project/index.js",
"version": "4c69d0c670e9dc788b5e107f277aa8ab-import mod from '../common';\n\nexport const m = mod;",
- "signature": "b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",
+ "signature": "ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",
"impliedNodeFormat": "CommonJS",
"original": {
"version": "4c69d0c670e9dc788b5e107f277aa8ab-import mod from '../common';\n\nexport const m = mod;",
- "signature": "b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",
+ "signature": "ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",
"impliedNodeFormat": 1
}
}
@@ -299,8 +304,25 @@ export const m = mod;
"../../solution/common/index.d.ts"
]
},
+ "semanticDiagnosticsPerFile": [
+ [
+ "../../solution/sub-project/index.js",
+ [
+ {
+ "pos": 7,
+ "end": 10,
+ "code": 1192,
+ "category": 1,
+ "messageKey": "Module_0_has_no_default_export_1192",
+ "messageArgs": [
+ "\"/home/src/workspaces/solution/common/index\""
+ ]
+ }
+ ]
+ ]
+ ],
"latestChangedDtsFile": "./index.d.ts",
- "size": 1595
+ "size": 1773
}
//// [/home/src/workspaces/solution/common/index.d.ts] *new*
import x = require("./obj.json");
diff --git a/testdata/baselines/reference/tsc/projectReferences/default-import-interop-uses-referenced-project-settings.js b/testdata/baselines/reference/tsc/projectReferences/default-import-interop-uses-referenced-project-settings.js
index 4d2bce478b1..d1431013b4a 100644
--- a/testdata/baselines/reference/tsc/projectReferences/default-import-interop-uses-referenced-project-settings.js
+++ b/testdata/baselines/reference/tsc/projectReferences/default-import-interop-uses-referenced-project-settings.js
@@ -57,6 +57,8 @@ ExitStatus:: DiagnosticsPresent_OutputsGenerated
Output::
app/src/index.ts(1,8): error TS2613: Module '"/home/src/workspaces/project/app/src/local"' has no default export. Did you mean to use 'import { local } from "/home/src/workspaces/project/app/src/local"' instead?
app/src/index.ts(2,8): error TS2613: Module '"/home/src/workspaces/project/node_modules/esm-package/index"' has no default export. Did you mean to use 'import { esm } from "/home/src/workspaces/project/node_modules/esm-package/index"' instead?
+app/src/index.ts(3,8): error TS1192: Module '"/home/src/workspaces/project/lib/dist/a"' has no default export.
+app/src/index.ts(4,8): error TS1192: Module '"/home/src/workspaces/project/lib/dist/a"' has no default export.
//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
///
interface Boolean {}
diff --git a/testdata/baselines/reference/tsc/projectReferences/referenced-project-with-esnext-module-disallows-synthetic-default-imports.js b/testdata/baselines/reference/tsc/projectReferences/referenced-project-with-esnext-module-disallows-synthetic-default-imports.js
new file mode 100644
index 00000000000..cbed14ee8f7
--- /dev/null
+++ b/testdata/baselines/reference/tsc/projectReferences/referenced-project-with-esnext-module-disallows-synthetic-default-imports.js
@@ -0,0 +1,70 @@
+currentDirectory::/home/src/workspaces/project
+useCaseSensitiveFileNames::true
+Input::
+//// [/home/src/workspaces/project/app/index.ts] *new*
+import TestSrc from '../lib/src/utils'; // Error
+import TestDecl from '../lib/dist/utils'; // Error
+console.log(TestSrc.test());
+console.log(TestDecl.test());
+//// [/home/src/workspaces/project/app/tsconfig.json] *new*
+{
+ "compilerOptions": {
+ "module": "esnext",
+ "moduleResolution": "bundler"
+ },
+ "references": [
+ { "path": "../lib" }
+ ]
+}
+//// [/home/src/workspaces/project/lib/dist/utils.d.ts] *new*
+export declare const test: () => string;
+//// [/home/src/workspaces/project/lib/src/utils.ts] *new*
+export const test = () => 'test';
+//// [/home/src/workspaces/project/lib/tsconfig.json] *new*
+{
+ "compilerOptions": {
+ "composite": true,
+ "declaration": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "rootDir": "src",
+ "outDir": "dist"
+ },
+ "include": ["src"]
+}
+
+tsgo --p app --pretty false
+ExitStatus:: DiagnosticsPresent_OutputsGenerated
+Output::
+app/index.ts(1,8): error TS1192: Module '"/home/src/workspaces/project/lib/dist/utils"' has no default export.
+app/index.ts(2,8): error TS1192: Module '"/home/src/workspaces/project/lib/dist/utils"' has no default export.
+//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+interface SymbolConstructor {
+ (desc?: string | number): symbol;
+ for(name: string): symbol;
+ readonly toStringTag: symbol;
+}
+declare var Symbol: SymbolConstructor;
+interface Symbol {
+ readonly [Symbol.toStringTag]: string;
+}
+declare const console: { log(msg: any): void; };
+//// [/home/src/workspaces/project/app/index.js] *new*
+import TestSrc from '../lib/src/utils'; // Error
+import TestDecl from '../lib/dist/utils'; // Error
+console.log(TestSrc.test());
+console.log(TestDecl.test());
+
+