Skip to content

Commit 7e3a80a

Browse files
committed
Modify synthetic default generation code for dual-mode module resolution
1 parent cadd115 commit 7e3a80a

14 files changed

+236
-81
lines changed

src/compiler/checker.ts

+23-7
Original file line numberDiff line numberDiff line change
@@ -2629,7 +2629,23 @@ namespace ts {
26292629
return ((isExportAssignment(node) && !node.isExportEquals) || hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node));
26302630
}
26312631

2632-
function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean) {
2632+
function getUsageModeForExpression(usage: Expression) {
2633+
return isStringLiteralLike(usage) ? getModeForUsageLocation(getSourceFileOfNode(usage), usage) : undefined;
2634+
}
2635+
2636+
function isESMFormatImportImportingCommonjsFormatFile(usageMode: SourceFile["impliedNodeFormat"], targetMode: SourceFile["impliedNodeFormat"]) {
2637+
return usageMode === ModuleKind.ESNext && targetMode === ModuleKind.CommonJS;
2638+
}
2639+
2640+
function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean, usage: Expression) {
2641+
const usageMode = file && getUsageModeForExpression(usage);
2642+
if (file && usageMode !== undefined) {
2643+
const result = isESMFormatImportImportingCommonjsFormatFile(usageMode, file.impliedNodeFormat);
2644+
if (usageMode === ModuleKind.ESNext || result) {
2645+
return result;
2646+
}
2647+
// fallthrough on cjs usages so we imply defaults for interop'd imports, too
2648+
}
26332649
if (!allowSyntheticDefaultImports) {
26342650
return false;
26352651
}
@@ -2672,7 +2688,7 @@ namespace ts {
26722688
}
26732689

26742690
const file = moduleSymbol.declarations?.find(isSourceFile);
2675-
const hasSyntheticDefault = canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias);
2691+
const hasSyntheticDefault = canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, node.parent.moduleSpecifier);
26762692
if (!exportDefaultSymbol && !hasSyntheticDefault) {
26772693
if (hasExportAssignmentSymbol(moduleSymbol)) {
26782694
const compilerOptionName = moduleKind >= ModuleKind.ES2015 ? "allowSyntheticDefaultImports" : "esModuleInterop";
@@ -2824,7 +2840,7 @@ namespace ts {
28242840
let symbolFromModule = getExportOfModule(targetSymbol, name, specifier, dontResolveAlias);
28252841
if (symbolFromModule === undefined && name.escapedText === InternalSymbolName.Default) {
28262842
const file = moduleSymbol.declarations?.find(isSourceFile);
2827-
if (canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias)) {
2843+
if (canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, moduleSpecifier)) {
28282844
symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
28292845
}
28302846
}
@@ -3587,7 +3603,7 @@ namespace ts {
35873603
sigs = getSignaturesOfStructuredType(type, SignatureKind.Construct);
35883604
}
35893605
if (sigs && sigs.length) {
3590-
const moduleType = getTypeWithSyntheticDefaultImportType(type, symbol, moduleSymbol!);
3606+
const moduleType = getTypeWithSyntheticDefaultImportType(type, symbol, moduleSymbol!, isImportCall(referenceParent) ? referenceParent.arguments[0] : referenceParent.moduleSpecifier);
35913607
// Create a new symbol which has the module's type less the call and construct signatures
35923608
const result = createSymbol(symbol.flags, symbol.escapedName);
35933609
result.declarations = symbol.declarations ? symbol.declarations.slice() : [];
@@ -30945,18 +30961,18 @@ namespace ts {
3094530961
if (moduleSymbol) {
3094630962
const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, specifier, /*dontRecursivelyResolve*/ true, /*suppressUsageError*/ false);
3094730963
if (esModuleSymbol) {
30948-
return createPromiseReturnType(node, getTypeWithSyntheticDefaultImportType(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol));
30964+
return createPromiseReturnType(node, getTypeWithSyntheticDefaultImportType(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier));
3094930965
}
3095030966
}
3095130967
return createPromiseReturnType(node, anyType);
3095230968
}
3095330969

30954-
function getTypeWithSyntheticDefaultImportType(type: Type, symbol: Symbol, originalSymbol: Symbol): Type {
30970+
function getTypeWithSyntheticDefaultImportType(type: Type, symbol: Symbol, originalSymbol: Symbol, moduleSpecifier: Expression): Type {
3095530971
if (allowSyntheticDefaultImports && type && !isErrorType(type)) {
3095630972
const synthType = type as SyntheticDefaultModuleType;
3095730973
if (!synthType.syntheticType) {
3095830974
const file = originalSymbol.declarations?.find(isSourceFile);
30959-
const hasSyntheticDefault = canHaveSyntheticDefault(file, originalSymbol, /*dontResolveAlias*/ false);
30975+
const hasSyntheticDefault = canHaveSyntheticDefault(file, originalSymbol, /*dontResolveAlias*/ false, moduleSpecifier);
3096030976
if (hasSyntheticDefault) {
3096130977
const memberTable = createSymbolTable();
3096230978
const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default);

tests/baselines/reference/nodeModulesAllowJsSynchronousCallErrors(module=node12).types

+6-6
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ export async function f() {
2323
>"../index.js" : "../index.js"
2424

2525
const mod4 = await import ("./index.js");
26-
>mod4 : typeof mod2
27-
>await import ("./index.js") : typeof mod2
28-
>import ("./index.js") : Promise<typeof mod2>
26+
>mod4 : { default: typeof mod2; f(): Promise<void>; }
27+
>await import ("./index.js") : { default: typeof mod2; f(): Promise<void>; }
28+
>import ("./index.js") : Promise<{ default: typeof mod2; f(): Promise<void>; }>
2929
>"./index.js" : "./index.js"
3030

3131
h();
@@ -57,9 +57,9 @@ export async function h() {
5757
>"./index.js" : "./index.js"
5858

5959
const mod4 = await import ("./subfolder/index.js");
60-
>mod4 : typeof mod2
61-
>await import ("./subfolder/index.js") : typeof mod2
62-
>import ("./subfolder/index.js") : Promise<typeof mod2>
60+
>mod4 : { default: typeof mod2; f(): Promise<void>; }
61+
>await import ("./subfolder/index.js") : { default: typeof mod2; f(): Promise<void>; }
62+
>import ("./subfolder/index.js") : Promise<{ default: typeof mod2; f(): Promise<void>; }>
6363
>"./subfolder/index.js" : "./subfolder/index.js"
6464

6565
f();

tests/baselines/reference/nodeModulesAllowJsSynchronousCallErrors(module=nodenext).types

+6-6
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ export async function f() {
2323
>"../index.js" : "../index.js"
2424

2525
const mod4 = await import ("./index.js");
26-
>mod4 : typeof mod2
27-
>await import ("./index.js") : typeof mod2
28-
>import ("./index.js") : Promise<typeof mod2>
26+
>mod4 : { default: typeof mod2; f(): Promise<void>; }
27+
>await import ("./index.js") : { default: typeof mod2; f(): Promise<void>; }
28+
>import ("./index.js") : Promise<{ default: typeof mod2; f(): Promise<void>; }>
2929
>"./index.js" : "./index.js"
3030

3131
h();
@@ -57,9 +57,9 @@ export async function h() {
5757
>"./index.js" : "./index.js"
5858

5959
const mod4 = await import ("./subfolder/index.js");
60-
>mod4 : typeof mod2
61-
>await import ("./subfolder/index.js") : typeof mod2
62-
>import ("./subfolder/index.js") : Promise<typeof mod2>
60+
>mod4 : { default: typeof mod2; f(): Promise<void>; }
61+
>await import ("./subfolder/index.js") : { default: typeof mod2; f(): Promise<void>; }
62+
>import ("./subfolder/index.js") : Promise<{ default: typeof mod2; f(): Promise<void>; }>
6363
>"./subfolder/index.js" : "./subfolder/index.js"
6464

6565
f();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [tests/cases/conformance/node/nodeModulesCjsFormatFileAlwaysHasDefault.ts] ////
2+
3+
//// [index.ts]
4+
// cjs format file
5+
export const a = 1;
6+
//// [index.ts]
7+
// esm format file
8+
import mod from "./subfolder/index.js";
9+
mod;
10+
//// [package.json]
11+
{
12+
"name": "package",
13+
"private": true,
14+
"type": "module"
15+
}
16+
//// [package.json]
17+
{
18+
"type": "commonjs"
19+
}
20+
21+
//// [index.js]
22+
"use strict";
23+
Object.defineProperty(exports, "__esModule", { value: true });
24+
exports.a = void 0;
25+
// cjs format file
26+
exports.a = 1;
27+
//// [index.js]
28+
// esm format file
29+
import mod from "./subfolder/index.js";
30+
mod;
31+
32+
33+
//// [index.d.ts]
34+
export declare const a = 1;
35+
//// [index.d.ts]
36+
export {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/conformance/node/subfolder/index.ts ===
2+
// cjs format file
3+
export const a = 1;
4+
>a : Symbol(a, Decl(index.ts, 1, 12))
5+
6+
=== tests/cases/conformance/node/index.ts ===
7+
// esm format file
8+
import mod from "./subfolder/index.js";
9+
>mod : Symbol(mod, Decl(index.ts, 1, 6))
10+
11+
mod;
12+
>mod : Symbol(mod, Decl(index.ts, 1, 6))
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/conformance/node/subfolder/index.ts ===
2+
// cjs format file
3+
export const a = 1;
4+
>a : 1
5+
>1 : 1
6+
7+
=== tests/cases/conformance/node/index.ts ===
8+
// esm format file
9+
import mod from "./subfolder/index.js";
10+
>mod : typeof mod
11+
12+
mod;
13+
>mod : typeof mod
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [tests/cases/conformance/node/nodeModulesCjsFormatFileAlwaysHasDefault.ts] ////
2+
3+
//// [index.ts]
4+
// cjs format file
5+
export const a = 1;
6+
//// [index.ts]
7+
// esm format file
8+
import mod from "./subfolder/index.js";
9+
mod;
10+
//// [package.json]
11+
{
12+
"name": "package",
13+
"private": true,
14+
"type": "module"
15+
}
16+
//// [package.json]
17+
{
18+
"type": "commonjs"
19+
}
20+
21+
//// [index.js]
22+
"use strict";
23+
Object.defineProperty(exports, "__esModule", { value: true });
24+
exports.a = void 0;
25+
// cjs format file
26+
exports.a = 1;
27+
//// [index.js]
28+
// esm format file
29+
import mod from "./subfolder/index.js";
30+
mod;
31+
32+
33+
//// [index.d.ts]
34+
export declare const a = 1;
35+
//// [index.d.ts]
36+
export {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/conformance/node/subfolder/index.ts ===
2+
// cjs format file
3+
export const a = 1;
4+
>a : Symbol(a, Decl(index.ts, 1, 12))
5+
6+
=== tests/cases/conformance/node/index.ts ===
7+
// esm format file
8+
import mod from "./subfolder/index.js";
9+
>mod : Symbol(mod, Decl(index.ts, 1, 6))
10+
11+
mod;
12+
>mod : Symbol(mod, Decl(index.ts, 1, 6))
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/conformance/node/subfolder/index.ts ===
2+
// cjs format file
3+
export const a = 1;
4+
>a : 1
5+
>1 : 1
6+
7+
=== tests/cases/conformance/node/index.ts ===
8+
// esm format file
9+
import mod from "./subfolder/index.js";
10+
>mod : typeof mod
11+
12+
mod;
13+
>mod : typeof mod
14+

tests/baselines/reference/nodeModulesDeclarationEmitDynamicImportWithPackageExports.js

+28-34
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ export {};
122122
//// [index.d.cts]
123123
export {};
124124
//// [other.d.ts]
125-
export declare const a: typeof import("package/cjs");
125+
export declare const a: {
126+
default: typeof import("package/cjs");
127+
};
126128
export declare const b: typeof import("package/mjs");
127129
export declare const c: typeof import("package");
128130
export declare const f: {
@@ -134,12 +136,11 @@ export declare const d: {
134136
default: typeof import("inner/cjs");
135137
cjsNonmain: true;
136138
};
137-
export declare const e: {
138-
default: typeof import("inner/mjs");
139-
esm: true;
140-
};
139+
export declare const e: typeof import("inner/mjs");
141140
//// [other.d.mts]
142-
export declare const a: typeof import("package/cjs");
141+
export declare const a: {
142+
default: typeof import("package/cjs");
143+
};
143144
export declare const b: typeof import("package/mjs");
144145
export declare const c: typeof import("package");
145146
export declare const f: {
@@ -151,12 +152,11 @@ export declare const d: {
151152
default: typeof import("inner/cjs");
152153
cjsNonmain: true;
153154
};
154-
export declare const e: {
155-
default: typeof import("inner/mjs");
156-
esm: true;
157-
};
155+
export declare const e: typeof import("inner/mjs");
158156
//// [other.d.cts]
159-
export declare const a: Promise<typeof import("package/cjs")>;
157+
export declare const a: Promise<{
158+
default: typeof import("package/cjs");
159+
}>;
160160
export declare const b: Promise<typeof import("package/mjs")>;
161161
export declare const c: Promise<typeof import("package")>;
162162
export declare const f: Promise<{
@@ -168,18 +168,15 @@ export declare const d: Promise<{
168168
default: typeof import("inner/cjs");
169169
cjsNonmain: true;
170170
}>;
171-
export declare const e: Promise<{
172-
default: typeof import("inner/mjs");
173-
esm: true;
174-
}>;
171+
export declare const e: Promise<typeof import("inner/mjs")>;
175172

176173

177174
//// [DtsFileErrors]
178175

179176

180-
tests/cases/conformance/node/other.d.cts(2,47): error TS1471: Module 'package/mjs' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
181-
tests/cases/conformance/node/other.d.cts(3,47): error TS1471: Module 'package' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
182-
tests/cases/conformance/node/other2.d.cts(6,28): error TS1471: Module 'inner/mjs' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
177+
tests/cases/conformance/node/other.d.cts(4,47): error TS1471: Module 'package/mjs' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
178+
tests/cases/conformance/node/other.d.cts(5,47): error TS1471: Module 'package' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
179+
tests/cases/conformance/node/other2.d.cts(5,47): error TS1471: Module 'inner/mjs' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
183180

184181

185182
==== tests/cases/conformance/node/index.d.ts (0 errors) ====
@@ -192,7 +189,9 @@ tests/cases/conformance/node/other2.d.cts(6,28): error TS1471: Module 'inner/mjs
192189
export {};
193190

194191
==== tests/cases/conformance/node/other.d.ts (0 errors) ====
195-
export declare const a: typeof import("package/cjs");
192+
export declare const a: {
193+
default: typeof import("package/cjs");
194+
};
196195
export declare const b: typeof import("package/mjs");
197196
export declare const c: typeof import("package");
198197
export declare const f: {
@@ -205,13 +204,12 @@ tests/cases/conformance/node/other2.d.cts(6,28): error TS1471: Module 'inner/mjs
205204
default: typeof import("inner/cjs");
206205
cjsNonmain: true;
207206
};
208-
export declare const e: {
209-
default: typeof import("inner/mjs");
210-
esm: true;
211-
};
207+
export declare const e: typeof import("inner/mjs");
212208

213209
==== tests/cases/conformance/node/other.d.mts (0 errors) ====
214-
export declare const a: typeof import("package/cjs");
210+
export declare const a: {
211+
default: typeof import("package/cjs");
212+
};
215213
export declare const b: typeof import("package/mjs");
216214
export declare const c: typeof import("package");
217215
export declare const f: {
@@ -224,13 +222,12 @@ tests/cases/conformance/node/other2.d.cts(6,28): error TS1471: Module 'inner/mjs
224222
default: typeof import("inner/cjs");
225223
cjsNonmain: true;
226224
};
227-
export declare const e: {
228-
default: typeof import("inner/mjs");
229-
esm: true;
230-
};
225+
export declare const e: typeof import("inner/mjs");
231226

232227
==== tests/cases/conformance/node/other.d.cts (2 errors) ====
233-
export declare const a: Promise<typeof import("package/cjs")>;
228+
export declare const a: Promise<{
229+
default: typeof import("package/cjs");
230+
}>;
234231
export declare const b: Promise<typeof import("package/mjs")>;
235232
~~~~~~~~~~~~~
236233
!!! error TS1471: Module 'package/mjs' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
@@ -247,12 +244,9 @@ tests/cases/conformance/node/other2.d.cts(6,28): error TS1471: Module 'inner/mjs
247244
default: typeof import("inner/cjs");
248245
cjsNonmain: true;
249246
}>;
250-
export declare const e: Promise<{
251-
default: typeof import("inner/mjs");
252-
~~~~~~~~~~~
247+
export declare const e: Promise<typeof import("inner/mjs")>;
248+
~~~~~~~~~~~
253249
!!! error TS1471: Module 'inner/mjs' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
254-
esm: true;
255-
}>;
256250

257251
==== tests/cases/conformance/node/node_modules/inner/index.d.ts (0 errors) ====
258252
// cjs format file

0 commit comments

Comments
 (0)