Skip to content

Commit 3eae883

Browse files
committed
Experiment: use limited impliedNodeFormat for interop checking
1 parent 3c504d8 commit 3eae883

File tree

46 files changed

+552
-153
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+552
-153
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4058,22 +4058,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
40584058
|| isNamespaceExport(node));
40594059
}
40604060

4061-
function getUsageModeForExpression(usage: Expression) {
4062-
return isStringLiteralLike(usage) ? host.getModeForUsageLocation(getSourceFileOfNode(usage), usage) : undefined;
4061+
function getEmitSyntaxForModuleSpecifierExpression(usage: Expression) {
4062+
return isStringLiteralLike(usage) ? host.getEmitSyntaxForUsageLocation(getSourceFileOfNode(usage), usage) : undefined;
40634063
}
40644064

40654065
function isESMFormatImportImportingCommonjsFormatFile(usageMode: ResolutionMode, targetMode: ResolutionMode) {
40664066
return usageMode === ModuleKind.ESNext && targetMode === ModuleKind.CommonJS;
40674067
}
40684068

40694069
function isOnlyImportedAsDefault(usage: Expression) {
4070-
const usageMode = getUsageModeForExpression(usage);
4070+
const usageMode = getEmitSyntaxForModuleSpecifierExpression(usage);
40714071
return usageMode === ModuleKind.ESNext && endsWith((usage as StringLiteralLike).text, Extension.Json);
40724072
}
40734073

40744074
function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean, usage: Expression) {
4075-
const usageMode = file && getUsageModeForExpression(usage);
4076-
if (file && usageMode !== undefined && ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext) {
4075+
const usageMode = file && getEmitSyntaxForModuleSpecifierExpression(usage);
4076+
if (file && usageMode !== undefined) {
40774077
const result = isESMFormatImportImportingCommonjsFormatFile(usageMode, impliedNodeFormatForInteropChecking(file, compilerOptions));
40784078
if (usageMode === ModuleKind.ESNext || result) {
40794079
return result;
@@ -5301,7 +5301,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
53015301
}
53025302

53035303
const targetFile = moduleSymbol?.declarations?.find(isSourceFile);
5304-
const isEsmCjsRef = targetFile && isESMFormatImportImportingCommonjsFormatFile(getUsageModeForExpression(reference), impliedNodeFormatForInteropChecking(targetFile, compilerOptions));
5304+
const isEsmCjsRef = targetFile && isESMFormatImportImportingCommonjsFormatFile(getEmitSyntaxForModuleSpecifierExpression(reference), impliedNodeFormatForInteropChecking(targetFile, compilerOptions));
53055305
if (getESModuleInterop(compilerOptions) || isEsmCjsRef) {
53065306
let sigs = getSignaturesOfStructuredType(type, SignatureKind.Call);
53075307
if (!sigs || !sigs.length) {
@@ -46071,7 +46071,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4607146071
return; // Other grammar checks do not apply to type-only imports with resolution mode assertions
4607246072
}
4607346073

46074-
const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getUsageModeForExpression(declaration.moduleSpecifier);
46074+
const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getEmitSyntaxForModuleSpecifierExpression(declaration.moduleSpecifier);
4607546075
if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.Preserve) {
4607646076
const message = isImportAttributes
4607746077
? moduleKind === ModuleKind.NodeNext

src/compiler/program.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ import {
136136
getPositionOfLineAndCharacter,
137137
getPropertyArrayElementValue,
138138
getResolveJsonModule,
139+
getResolvePackageJsonExports,
140+
getResolvePackageJsonImports,
139141
getRootLength,
140142
getSetExternalModuleIndicator,
141143
getSourceFileOfNode,
@@ -913,23 +915,46 @@ function getModeForUsageLocationWorker(file: { impliedNodeFormat?: ResolutionMod
913915
return override;
914916
}
915917
}
918+
919+
const emitSyntax = getEmitSyntaxForUsageLocationWorker(file, usage, compilerOptions);
920+
const moduleResolution = compilerOptions && getEmitModuleResolutionKind(compilerOptions);
921+
if (
922+
compilerOptions && (
923+
getResolvePackageJsonExports(compilerOptions) ||
924+
getResolvePackageJsonImports(compilerOptions) ||
925+
ModuleResolutionKind.Node16 <= moduleResolution! && moduleResolution! <= ModuleResolutionKind.NodeNext
926+
)
927+
) {
928+
return emitSyntax;
929+
}
930+
}
931+
932+
function getEmitSyntaxForUsageLocationWorker(file: { impliedNodeFormat?: ResolutionMode; }, usage: StringLiteralLike, compilerOptions?: CompilerOptions): ResolutionMode {
916933
if (compilerOptions && getEmitModuleKind(compilerOptions) === ModuleKind.Preserve) {
917934
return (usage.parent.parent && isImportEqualsDeclaration(usage.parent.parent) || isRequireCall(usage.parent, /*requireStringLiteralLikeArgument*/ false))
918935
? ModuleKind.CommonJS
919936
: ModuleKind.ESNext;
920937
}
921-
if (file.impliedNodeFormat === undefined || !(compilerOptions && impliedNodeFormatAffectsModuleResolution(compilerOptions))) {
922-
// TODO: we don't know whether this is being called for module resolution, emit, or interop checking
938+
if (!compilerOptions) {
939+
// This should always be provided, but we try to fail somewhat
940+
// gracefully to allow projects like ts-node time to update.
923941
return undefined;
924942
}
925-
if (file.impliedNodeFormat !== ModuleKind.ESNext) {
926-
// in cjs files, import call expressions are esm format, otherwise everything is cjs
927-
return isImportCall(walkUpParenthesizedExpressions(usage.parent)) ? ModuleKind.ESNext : ModuleKind.CommonJS;
943+
if (impliedNodeFormatAffectsModuleResolution(compilerOptions)) {
944+
if (file.impliedNodeFormat !== ModuleKind.ESNext) {
945+
// in cjs files, import call expressions are esm format, otherwise everything is cjs
946+
return isImportCall(walkUpParenthesizedExpressions(usage.parent)) ? ModuleKind.ESNext : ModuleKind.CommonJS;
947+
}
948+
const exprParentParent = walkUpParenthesizedExpressions(usage.parent)?.parent;
949+
return exprParentParent && isImportEqualsDeclaration(exprParentParent) ? ModuleKind.CommonJS : ModuleKind.ESNext;
928950
}
929-
// in esm files, import=require statements are cjs format, otherwise everything is esm
930-
// imports are only parent'd up to their containing declaration/expression, so access farther parents with care
931-
const exprParentParent = walkUpParenthesizedExpressions(usage.parent)?.parent;
932-
return exprParentParent && isImportEqualsDeclaration(exprParentParent) ? ModuleKind.CommonJS : ModuleKind.ESNext;
951+
if (getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS) {
952+
return ModuleKind.CommonJS;
953+
}
954+
if (emitModuleKindIsNonNodeESM(getEmitModuleKind(compilerOptions))) {
955+
return ModuleKind.ESNext;
956+
}
957+
return undefined;
933958
}
934959

935960
/** @internal */
@@ -1897,6 +1922,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
18971922
isSourceFileFromExternalLibrary,
18981923
isSourceFileDefaultLibrary,
18991924
getModeForUsageLocation,
1925+
getEmitSyntaxForUsageLocation,
19001926
getModeForResolutionAtIndex,
19011927
getSourceFileFromReference,
19021928
getLibFileFromReference,
@@ -4931,6 +4957,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
49314957
return getModeForUsageLocationWorker(file, usage, optionsForFile);
49324958
}
49334959

4960+
function getEmitSyntaxForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode {
4961+
const optionsForFile = getRedirectReferenceForResolution(file)?.commandLine.options || options;
4962+
return getEmitSyntaxForUsageLocationWorker(file, usage, optionsForFile);
4963+
}
4964+
49344965
function getModeForResolutionAtIndex(file: SourceFile, index: number): ResolutionMode {
49354966
return getModeForUsageLocation(file, getModuleNameStringLiteralAt(file, index));
49364967
}

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4847,6 +4847,7 @@ export interface TypeCheckerHost extends ModuleSpecifierResolutionHost {
48474847
getResolvedTypeReferenceDirectives(): ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
48484848
getProjectReferenceRedirect(fileName: string): string | undefined;
48494849
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
4850+
getEmitSyntaxForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode;
48504851
getModeForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode;
48514852

48524853
getResolvedModule(f: SourceFile, moduleName: string, mode: ResolutionMode): ResolvedModuleWithFailedLookupLocations | undefined;

src/compiler/utilities.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8610,12 +8610,6 @@ export function impliedNodeFormatAffectsModuleResolution(options: CompilerOption
86108610
return ModuleResolutionKind.Node16 <= moduleResolution && moduleResolution <= ModuleResolutionKind.NodeNext;
86118611
}
86128612

8613-
/** @internal */
8614-
export function impliedNodeFormatAffectsInteropChecking(options: CompilerOptions) {
8615-
const moduleKind = getEmitModuleKind(options);
8616-
return ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext;
8617-
}
8618-
86198613
/** @internal */
86208614
export function impliedNodeFormatForModuleResolution(sourceFile: SourceFile, options: CompilerOptions): ResolutionMode {
86218615
return impliedNodeFormatAffectsModuleResolution(options) ? sourceFile.impliedNodeFormat : undefined;
@@ -8628,7 +8622,25 @@ export function impliedNodeFormatForEmit(sourceFile: SourceFile, options: Compil
86288622

86298623
/** @internal */
86308624
export function impliedNodeFormatForInteropChecking(sourceFile: SourceFile, options: CompilerOptions): ResolutionMode {
8631-
return impliedNodeFormatAffectsInteropChecking(options) ? sourceFile.impliedNodeFormat : undefined;
8625+
const moduleKind = getEmitModuleKind(options);
8626+
if (ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext) {
8627+
return sourceFile.impliedNodeFormat;
8628+
}
8629+
if (
8630+
sourceFile.impliedNodeFormat === ModuleKind.CommonJS
8631+
&& (sourceFile.packageJsonScope?.contents.packageJsonContent.type === "commonjs"
8632+
|| fileExtensionIsOneOf(sourceFile.fileName, [Extension.Cjs, Extension.Cts]))
8633+
) {
8634+
return ModuleKind.CommonJS;
8635+
}
8636+
if (
8637+
sourceFile.impliedNodeFormat === ModuleKind.ESNext
8638+
&& (sourceFile.packageJsonScope?.contents.packageJsonContent.type === "module"
8639+
|| fileExtensionIsOneOf(sourceFile.fileName, [Extension.Mjs, Extension.Mts]))
8640+
) {
8641+
return ModuleKind.ESNext;
8642+
}
8643+
return undefined;
86328644
}
86338645

86348646
type CompilerOptionKeys = keyof { [K in keyof CompilerOptions as string extends K ? never : K]: any; };

tests/baselines/reference/api/typescript.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9875,7 +9875,7 @@ declare namespace ts {
98759875
},
98769876
usage: StringLiteralLike,
98779877
compilerOptions: CompilerOptions,
9878-
): ModuleKind.CommonJS | ModuleKind.ESNext | undefined;
9878+
): ResolutionMode;
98799879
function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): readonly Diagnostic[];
98809880
/**
98819881
* A function for determining if a given file is esm or cjs format, assuming modern node module resolution rules, as configured by the

tests/baselines/reference/bundlerDirectoryModule(moduleresolution=bundler).trace.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,8 @@
937937
"Directory '/.src/node_modules' does not exist, skipping all lookups in it.",
938938
"Directory '/node_modules' does not exist, skipping all lookups in it.",
939939
"======== Module name '@typescript/lib-es2023/collection' was not resolved. ========",
940-
<<<<<<< HEAD
940+
"File '/.ts/package.json' does not exist according to earlier cached lookups.",
941+
"File '/package.json' does not exist according to earlier cached lookups.",
941942
"======== Resolving module '@typescript/lib-es2023/intl' from '/.src/__lib_node_modules_lookup_lib.es2023.intl.d.ts__.ts'. ========",
942943
"Explicitly specified module resolution kind: 'Node10'.",
943944
"Loading module '@typescript/lib-es2023/intl' from 'node_modules' folder, target file types: TypeScript, Declaration.",
@@ -951,10 +952,8 @@
951952
"Directory '/.src/node_modules' does not exist, skipping all lookups in it.",
952953
"Directory '/node_modules' does not exist, skipping all lookups in it.",
953954
"======== Module name '@typescript/lib-es2023/intl' was not resolved. ========",
954-
=======
955955
"File '/.ts/package.json' does not exist according to earlier cached lookups.",
956956
"File '/package.json' does not exist according to earlier cached lookups.",
957-
>>>>>>> a3b9541d66 (Update trace baselines)
958957
"======== Resolving module '@typescript/lib-esnext/intl' from '/.src/__lib_node_modules_lookup_lib.esnext.intl.d.ts__.ts'. ========",
959958
"Explicitly specified module resolution kind: 'Node10'.",
960959
"Loading module '@typescript/lib-esnext/intl' from 'node_modules' folder, target file types: TypeScript, Declaration.",

tests/baselines/reference/bundlerRelative1(module=esnext).errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/main.ts(4,16): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
22
/main.ts(7,16): error TS2307: Cannot find module './redirect/index' or its corresponding type declarations.
3+
/main.ts(11,8): error TS1192: Module '"/types/cjs"' has no default export.
34

45

56
==== /dir/index.ts (0 errors) ====
@@ -19,7 +20,7 @@
1920
declare const _: string;
2021
export = _;
2122

22-
==== /main.ts (2 errors) ====
23+
==== /main.ts (3 errors) ====
2324
import { x } from "./dir";
2425
import {} from "./dir/index";
2526
import {} from "./dir/index.js";
@@ -35,5 +36,7 @@
3536
import a from "./types/esm";
3637
import * as esm from "./types/esm";
3738
import b from "./types/cjs";
39+
~
40+
!!! error TS1192: Module '"/types/cjs"' has no default export.
3841
import * as cjs from "./types/cjs";
3942

tests/baselines/reference/bundlerRelative1(module=esnext).types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import * as esm from "./types/esm";
4444
>esm : typeof esm
4545

4646
import b from "./types/cjs";
47-
>b : string
47+
>b : any
4848

4949
import * as cjs from "./types/cjs";
5050
>cjs : string

tests/baselines/reference/bundlerRelative1(module=preserve).errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/main.ts(4,16): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
22
/main.ts(7,16): error TS2307: Cannot find module './redirect/index' or its corresponding type declarations.
3+
/main.ts(11,8): error TS1192: Module '"/types/cjs"' has no default export.
34

45

56
==== /dir/index.ts (0 errors) ====
@@ -19,7 +20,7 @@
1920
declare const _: string;
2021
export = _;
2122

22-
==== /main.ts (2 errors) ====
23+
==== /main.ts (3 errors) ====
2324
import { x } from "./dir";
2425
import {} from "./dir/index";
2526
import {} from "./dir/index.js";
@@ -35,5 +36,7 @@
3536
import a from "./types/esm";
3637
import * as esm from "./types/esm";
3738
import b from "./types/cjs";
39+
~
40+
!!! error TS1192: Module '"/types/cjs"' has no default export.
3841
import * as cjs from "./types/cjs";
3942

tests/baselines/reference/bundlerRelative1(module=preserve).types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import * as esm from "./types/esm";
4444
>esm : typeof esm
4545

4646
import b from "./types/cjs";
47-
>b : string
47+
>b : any
4848

4949
import * as cjs from "./types/cjs";
5050
>cjs : string

tests/baselines/reference/conditionalExportsResolutionFallback(moduleresolution=bundler).errors.txt

Lines changed: 0 additions & 29 deletions
This file was deleted.

tests/baselines/reference/customConditions(resolvepackagejsonexports=false).errors.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
error TS5095: Option 'bundler' can only be used when 'module' is set to 'preserve' or to 'es2015' or later.
1+
/index.ts(1,8): error TS1192: Module '"/node_modules/lodash/index"' has no default export.
22

33

4-
!!! error TS5095: Option 'bundler' can only be used when 'module' is set to 'preserve' or to 'es2015' or later.
54
==== /node_modules/lodash/package.json (0 errors) ====
65
{
76
"name": "lodash",
@@ -26,6 +25,8 @@ error TS5095: Option 'bundler' can only be used when 'module' is set to 'preserv
2625
declare const _: "webpack";
2726
export = _;
2827

29-
==== /index.ts (0 errors) ====
28+
==== /index.ts (1 errors) ====
3029
import _ from "lodash";
30+
~
31+
!!! error TS1192: Module '"/node_modules/lodash/index"' has no default export.
3132

tests/baselines/reference/customConditions(resolvepackagejsonexports=false).js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,3 @@ import _ from "lodash";
2929

3030

3131
//// [index.js]
32-
"use strict";
33-
Object.defineProperty(exports, "__esModule", { value: true });

tests/baselines/reference/customConditions(resolvepackagejsonexports=false).types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ export = _;
2323

2424
=== /index.ts ===
2525
import _ from "lodash";
26-
>_ : "index"
26+
>_ : any
2727

tests/baselines/reference/customConditions(resolvepackagejsonexports=true).errors.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
error TS5095: Option 'bundler' can only be used when 'module' is set to 'preserve' or to 'es2015' or later.
1+
/index.ts(1,8): error TS1192: Module '"/node_modules/lodash/webpack"' has no default export.
22

33

4-
!!! error TS5095: Option 'bundler' can only be used when 'module' is set to 'preserve' or to 'es2015' or later.
54
==== /node_modules/lodash/package.json (0 errors) ====
65
{
76
"name": "lodash",
@@ -26,6 +25,8 @@ error TS5095: Option 'bundler' can only be used when 'module' is set to 'preserv
2625
declare const _: "webpack";
2726
export = _;
2827

29-
==== /index.ts (0 errors) ====
28+
==== /index.ts (1 errors) ====
3029
import _ from "lodash";
30+
~
31+
!!! error TS1192: Module '"/node_modules/lodash/webpack"' has no default export.
3132

tests/baselines/reference/customConditions(resolvepackagejsonexports=true).js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,3 @@ import _ from "lodash";
2929

3030

3131
//// [index.js]
32-
"use strict";
33-
Object.defineProperty(exports, "__esModule", { value: true });

tests/baselines/reference/customConditions(resolvepackagejsonexports=true).types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ export = _;
2323

2424
=== /index.ts ===
2525
import _ from "lodash";
26-
>_ : "webpack"
26+
>_ : any
2727

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
a.ts(1,8): error TS1192: Module '"b"' has no default export.
2+
3+
4+
==== a.ts (1 errors) ====
5+
import Namespace from "./b";
6+
~~~~~~~~~
7+
!!! error TS1192: Module '"b"' has no default export.
8+
export var x = new Namespace.Foo();
9+
10+
==== b.d.ts (0 errors) ====
11+
export class Foo {
12+
member: string;
13+
}
14+

tests/baselines/reference/esModuleInteropEnablesSyntheticDefaultImports.symbols

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ import Namespace from "./b";
66

77
export var x = new Namespace.Foo();
88
>x : Symbol(x, Decl(a.ts, 1, 10))
9-
>Namespace.Foo : Symbol(Namespace.Foo, Decl(b.d.ts, 0, 0))
109
>Namespace : Symbol(Namespace, Decl(a.ts, 0, 6))
11-
>Foo : Symbol(Namespace.Foo, Decl(b.d.ts, 0, 0))
1210

1311
=== b.d.ts ===
1412
export class Foo {

0 commit comments

Comments
 (0)