Skip to content

Commit 985bb98

Browse files
Copilotjakebailey
andauthored
Port #63038: Consult referenced project options for synthetic default export eligibility (#2752)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
1 parent 80401b2 commit 985bb98

File tree

5 files changed

+160
-17
lines changed

5 files changed

+160
-17
lines changed

internal/checker/checker.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14466,6 +14466,20 @@ func (c *Checker) canHaveSyntheticDefault(file *ast.Node, moduleSymbol *ast.Symb
1446614466
// are ESM, there cannot be a synthetic default.
1446714467
return false
1446814468
}
14469+
// For other files (not node16/nodenext with impliedNodeFormat), check if we can determine
14470+
// the module format from project references
14471+
if targetMode == core.ModuleKindNone && file.AsSourceFile().IsDeclarationFile {
14472+
// Try to get the project reference - try both source file mapping and output file mapping
14473+
// since declaration files can be mapped either way depending on how they're resolved
14474+
if c.program.GetRedirectForResolution(file.AsSourceFile()) != nil || c.program.GetProjectReferenceFromOutputDts(file.AsSourceFile().Path()) != nil {
14475+
// This is a declaration file from a project reference, so we can determine
14476+
// its module format from the referenced project's options
14477+
targetModuleKind := c.program.GetEmitModuleFormatOfFile(file.AsSourceFile())
14478+
if usageMode == core.ModuleKindESNext && core.ModuleKindES2015 <= targetModuleKind && targetModuleKind <= core.ModuleKindESNext {
14479+
return false
14480+
}
14481+
}
14482+
}
1446914483
}
1447014484
if !c.allowSyntheticDefaultImports {
1447114485
return false

internal/execute/tsctests/tsc_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3939,6 +3939,41 @@ func TestTscProjectReferences(t *testing.T) {
39393939
},
39403940
commandLineArgs: []string{"--p", "app", "--pretty", "false"},
39413941
},
3942+
{
3943+
subScenario: "referenced project with esnext module disallows synthetic default imports",
3944+
files: FileMap{
3945+
"/home/src/workspaces/project/lib/tsconfig.json": stringtestutil.Dedent(`
3946+
{
3947+
"compilerOptions": {
3948+
"composite": true,
3949+
"declaration": true,
3950+
"module": "esnext",
3951+
"moduleResolution": "bundler",
3952+
"rootDir": "src",
3953+
"outDir": "dist"
3954+
},
3955+
"include": ["src"]
3956+
}`),
3957+
"/home/src/workspaces/project/lib/src/utils.ts": "export const test = () => 'test';",
3958+
"/home/src/workspaces/project/lib/dist/utils.d.ts": "export declare const test: () => string;",
3959+
"/home/src/workspaces/project/app/tsconfig.json": stringtestutil.Dedent(`
3960+
{
3961+
"compilerOptions": {
3962+
"module": "esnext",
3963+
"moduleResolution": "bundler"
3964+
},
3965+
"references": [
3966+
{ "path": "../lib" }
3967+
]
3968+
}`),
3969+
"/home/src/workspaces/project/app/index.ts": stringtestutil.Dedent(`
3970+
import TestSrc from '../lib/src/utils'; // Error
3971+
import TestDecl from '../lib/dist/utils'; // Error
3972+
console.log(TestSrc.test());
3973+
console.log(TestDecl.test());`),
3974+
},
3975+
commandLineArgs: []string{"--p", "app", "--pretty", "false"},
3976+
},
39423977
{
39433978
subScenario: "referencing ambient const enum from referenced project with preserveConstEnums",
39443979
files: FileMap{

testdata/baselines/reference/tsbuild/javascriptProjectEmit/loads-js-based-projects-with-non-moved-json-files-and-emits-them-correctly.js

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,17 @@ Output::
9191
2 export = x;
9292
  ~~~~~~~~~~~
9393

94+
sub-project/index.js:1:8 - error TS1192: Module '"/home/src/workspaces/solution/common/index"' has no default export.
9495

95-
Found 2 errors in the same file, starting at: common/index.ts:1
96+
1 import mod from '../common';
97+
   ~~~
98+
99+
100+
Found 3 errors in 2 files.
101+
102+
Errors Files
103+
2 common/index.ts:1
104+
1 sub-project/index.js:1
96105

97106
//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
98107
/// <reference no-default-lib="true"/>
@@ -119,9 +128,7 @@ interface Symbol {
119128
declare const console: { log(msg: any): void; };
120129
//// [/home/src/workspaces/out/sub-project-2/index.d.ts] *new*
121130
export declare function getVar(): {
122-
key: {
123-
val: number;
124-
};
131+
key: any;
125132
};
126133

127134
//// [/home/src/workspaces/out/sub-project-2/index.js] *new*
@@ -134,7 +141,7 @@ export function getVar() {
134141
}
135142

136143
//// [/home/src/workspaces/out/sub-project-2/tsconfig.tsbuildinfo] *new*
137-
{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","../sub-project/index.d.ts","../../solution/sub-project-2/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\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"}
144+
{"version":"FakeTSVersion","root":[3],"fileNames":["lib.es2025.full.d.ts","../sub-project/index.d.ts","../../solution/sub-project-2/index.js"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\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"}
138145
//// [/home/src/workspaces/out/sub-project-2/tsconfig.tsbuildinfo.readable.baseline.txt] *new*
139146
{
140147
"version": "FakeTSVersion",
@@ -166,18 +173,18 @@ export function getVar() {
166173
},
167174
{
168175
"fileName": "../sub-project/index.d.ts",
169-
"version": "b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",
170-
"signature": "b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",
176+
"version": "ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",
177+
"signature": "ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",
171178
"impliedNodeFormat": "CommonJS"
172179
},
173180
{
174181
"fileName": "../../solution/sub-project-2/index.js",
175182
"version": "56ecb5738c72a131a1514873df723721-import { m } from '../sub-project/index';\n\nconst variable = {\n key: m,\n};\n\nexport function getVar() {\n return variable;\n}",
176-
"signature": "f6a0b1edad82fddabb5c98ad5da1660d-export declare function getVar(): {\n key: {\n val: number;\n };\n};\n",
183+
"signature": "cb28999e3dc9364c7b77f755d2449d70-export declare function getVar(): {\n key: any;\n};\n",
177184
"impliedNodeFormat": "CommonJS",
178185
"original": {
179186
"version": "56ecb5738c72a131a1514873df723721-import { m } from '../sub-project/index';\n\nconst variable = {\n key: m,\n};\n\nexport function getVar() {\n return variable;\n}",
180-
"signature": "f6a0b1edad82fddabb5c98ad5da1660d-export declare function getVar(): {\n key: {\n val: number;\n };\n};\n",
187+
"signature": "cb28999e3dc9364c7b77f755d2449d70-export declare function getVar(): {\n key: any;\n};\n",
181188
"impliedNodeFormat": 1
182189
}
183190
}
@@ -203,19 +210,17 @@ export function getVar() {
203210
]
204211
},
205212
"latestChangedDtsFile": "./index.d.ts",
206-
"size": 1592
213+
"size": 1546
207214
}
208215
//// [/home/src/workspaces/out/sub-project/index.d.ts] *new*
209-
export declare const m: {
210-
val: number;
211-
};
216+
export declare const m: any;
212217

213218
//// [/home/src/workspaces/out/sub-project/index.js] *new*
214219
import mod from '../common';
215220
export const m = mod;
216221

217222
//// [/home/src/workspaces/out/sub-project/tsconfig.tsbuildinfo] *new*
218-
{"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-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\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"}
223+
{"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-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\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"}
219224
//// [/home/src/workspaces/out/sub-project/tsconfig.tsbuildinfo.readable.baseline.txt] *new*
220225
{
221226
"version": "FakeTSVersion",
@@ -264,11 +269,11 @@ export const m = mod;
264269
{
265270
"fileName": "../../solution/sub-project/index.js",
266271
"version": "4c69d0c670e9dc788b5e107f277aa8ab-import mod from '../common';\n\nexport const m = mod;",
267-
"signature": "b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",
272+
"signature": "ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",
268273
"impliedNodeFormat": "CommonJS",
269274
"original": {
270275
"version": "4c69d0c670e9dc788b5e107f277aa8ab-import mod from '../common';\n\nexport const m = mod;",
271-
"signature": "b13b16e08eb0717669fa55818828b2cb-export declare const m: {\n val: number;\n};\n",
276+
"signature": "ce0233db1f3aabecabdb072a4f4c8d1e-export declare const m: any;\n",
272277
"impliedNodeFormat": 1
273278
}
274279
}
@@ -299,8 +304,25 @@ export const m = mod;
299304
"../../solution/common/index.d.ts"
300305
]
301306
},
307+
"semanticDiagnosticsPerFile": [
308+
[
309+
"../../solution/sub-project/index.js",
310+
[
311+
{
312+
"pos": 7,
313+
"end": 10,
314+
"code": 1192,
315+
"category": 1,
316+
"messageKey": "Module_0_has_no_default_export_1192",
317+
"messageArgs": [
318+
"\"/home/src/workspaces/solution/common/index\""
319+
]
320+
}
321+
]
322+
]
323+
],
302324
"latestChangedDtsFile": "./index.d.ts",
303-
"size": 1595
325+
"size": 1773
304326
}
305327
//// [/home/src/workspaces/solution/common/index.d.ts] *new*
306328
import x = require("./obj.json");

testdata/baselines/reference/tsc/projectReferences/default-import-interop-uses-referenced-project-settings.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ ExitStatus:: DiagnosticsPresent_OutputsGenerated
5757
Output::
5858
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?
5959
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?
60+
app/src/index.ts(3,8): error TS1192: Module '"/home/src/workspaces/project/lib/dist/a"' has no default export.
61+
app/src/index.ts(4,8): error TS1192: Module '"/home/src/workspaces/project/lib/dist/a"' has no default export.
6062
//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
6163
/// <reference no-default-lib="true"/>
6264
interface Boolean {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
currentDirectory::/home/src/workspaces/project
2+
useCaseSensitiveFileNames::true
3+
Input::
4+
//// [/home/src/workspaces/project/app/index.ts] *new*
5+
import TestSrc from '../lib/src/utils'; // Error
6+
import TestDecl from '../lib/dist/utils'; // Error
7+
console.log(TestSrc.test());
8+
console.log(TestDecl.test());
9+
//// [/home/src/workspaces/project/app/tsconfig.json] *new*
10+
{
11+
"compilerOptions": {
12+
"module": "esnext",
13+
"moduleResolution": "bundler"
14+
},
15+
"references": [
16+
{ "path": "../lib" }
17+
]
18+
}
19+
//// [/home/src/workspaces/project/lib/dist/utils.d.ts] *new*
20+
export declare const test: () => string;
21+
//// [/home/src/workspaces/project/lib/src/utils.ts] *new*
22+
export const test = () => 'test';
23+
//// [/home/src/workspaces/project/lib/tsconfig.json] *new*
24+
{
25+
"compilerOptions": {
26+
"composite": true,
27+
"declaration": true,
28+
"module": "esnext",
29+
"moduleResolution": "bundler",
30+
"rootDir": "src",
31+
"outDir": "dist"
32+
},
33+
"include": ["src"]
34+
}
35+
36+
tsgo --p app --pretty false
37+
ExitStatus:: DiagnosticsPresent_OutputsGenerated
38+
Output::
39+
app/index.ts(1,8): error TS1192: Module '"/home/src/workspaces/project/lib/dist/utils"' has no default export.
40+
app/index.ts(2,8): error TS1192: Module '"/home/src/workspaces/project/lib/dist/utils"' has no default export.
41+
//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
42+
/// <reference no-default-lib="true"/>
43+
interface Boolean {}
44+
interface Function {}
45+
interface CallableFunction {}
46+
interface NewableFunction {}
47+
interface IArguments {}
48+
interface Number { toExponential: any; }
49+
interface Object {}
50+
interface RegExp {}
51+
interface String { charAt: any; }
52+
interface Array<T> { length: number; [n: number]: T; }
53+
interface ReadonlyArray<T> {}
54+
interface SymbolConstructor {
55+
(desc?: string | number): symbol;
56+
for(name: string): symbol;
57+
readonly toStringTag: symbol;
58+
}
59+
declare var Symbol: SymbolConstructor;
60+
interface Symbol {
61+
readonly [Symbol.toStringTag]: string;
62+
}
63+
declare const console: { log(msg: any): void; };
64+
//// [/home/src/workspaces/project/app/index.js] *new*
65+
import TestSrc from '../lib/src/utils'; // Error
66+
import TestDecl from '../lib/dist/utils'; // Error
67+
console.log(TestSrc.test());
68+
console.log(TestDecl.test());
69+
70+

0 commit comments

Comments
 (0)