Fix: Consult referenced project options for synthetic default export eligibility (#63038)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: andrewbranch <3277153+andrewbranch@users.noreply.github.com>
Co-authored-by: Andrew Branch <andrew@wheream.io>
This commit is contained in:
Copilot
2026-01-26 15:07:24 -08:00
committed by GitHub
parent 114327939c
commit 66edca11c9
4 changed files with 126 additions and 0 deletions

View File

@@ -3794,6 +3794,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// 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 && file.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
const redirect = host.getRedirectFromSourceFile(file.path) || host.getRedirectFromOutput(file.path);
if (redirect) {
// This is a declaration file from a project reference, so we can determine
// its module format from the referenced project's options
const targetModuleKind = host.getEmitModuleFormatOfFile(file);
if (usageMode === ModuleKind.ESNext && ModuleKind.ES2015 <= targetModuleKind && targetModuleKind <= ModuleKind.ESNext) {
return false;
}
}
}
}
if (!allowSyntheticDefaultImports) {
return false;

View File

@@ -90,6 +90,42 @@ describe("unittests:: tsc:: projectReferences::", () => {
commandLineArgs: ["--p", "app", "--pretty", "false"],
});
verifyTsc({
scenario: "projectReferences",
subScenario: "referenced project with esnext module disallows synthetic default imports",
sys: () =>
TestServerHost.createWatchedSystem({
"/home/src/workspaces/project/lib/tsconfig.json": jsonToReadableText({
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": jsonToReadableText({
compilerOptions: {
module: "esnext",
moduleResolution: "bundler",
},
references: [
{ path: "../lib" },
],
}),
"/home/src/workspaces/project/app/index.ts": `
import TestSrc from '../lib/src/utils'; // Error
import TestDecl from '../lib/dist/utils'; // Error
console.log(TestSrc.test());
console.log(TestDecl.test());`,
}),
commandLineArgs: ["--p", "app", "--pretty", "false"],
});
verifyTsc({
scenario: "projectReferences",
subScenario: "referencing ambient const enum from referenced project with preserveConstEnums",

View File

@@ -86,6 +86,8 @@ declare const console: { log(msg: any): void; };
Output::
app/src/index.ts(2,28): 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(3,28): 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(4,28): error TS1192: Module '"/home/src/workspaces/project/lib/dist/a"' has no default export.
app/src/index.ts(5,28): error TS1192: Module '"/home/src/workspaces/project/lib/dist/a"' has no default export.
//// [/home/src/workspaces/project/app/dist/local.js]

View File

@@ -0,0 +1,73 @@
currentDirectory:: /home/src/workspaces/project useCaseSensitiveFileNames:: false
Input::
//// [/home/src/workspaces/project/lib/tsconfig.json]
{
"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]
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler"
},
"references": [
{
"path": "../lib"
}
]
}
//// [/home/src/workspaces/project/app/index.ts]
import TestSrc from '../lib/src/utils'; // Error
import TestDecl from '../lib/dist/utils'; // Error
console.log(TestSrc.test());
console.log(TestDecl.test());
//// [/home/src/tslibs/TS/Lib/lib.d.ts]
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
interface ReadonlyArray<T> {}
declare const console: { log(msg: any): void; };
/home/src/tslibs/TS/Lib/tsc.js --p app --pretty false
Output::
app/index.ts(2,28): error TS1192: Module '"/home/src/workspaces/project/lib/dist/utils"' has no default export.
app/index.ts(3,28): error TS1192: Module '"/home/src/workspaces/project/lib/dist/utils"' has no default export.
//// [/home/src/workspaces/project/app/index.js]
import TestSrc from '../lib/src/utils'; // Error
import TestDecl from '../lib/dist/utils'; // Error
console.log(TestSrc.test());
console.log(TestDecl.test());
exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated