Skip to content

Commit 8f3fbdb

Browse files
committed
Experiment: use more emit info in interop checking
1 parent 3c504d8 commit 8f3fbdb

File tree

72 files changed

+288
-352
lines changed

Some content is hidden

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

72 files changed

+288
-352
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: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8611,9 +8611,8 @@ export function impliedNodeFormatAffectsModuleResolution(options: CompilerOption
86118611
}
86128612

86138613
/** @internal */
8614-
export function impliedNodeFormatAffectsInteropChecking(options: CompilerOptions) {
8615-
const moduleKind = getEmitModuleKind(options);
8616-
return ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext;
8614+
export function impliedNodeFormatAffectsInteropChecking(_options: CompilerOptions) {
8615+
return true;
86178616
}
86188617

86198618
/** @internal */

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).types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { y } from "./redirect";
3838
import {} from "./redirect/index";
3939

4040
import a from "./types/esm";
41-
>a : string
41+
>a : typeof a
4242

4343
import * as esm from "./types/esm";
4444
>esm : typeof esm
@@ -47,5 +47,5 @@ import b from "./types/cjs";
4747
>b : string
4848

4949
import * as cjs from "./types/cjs";
50-
>cjs : string
50+
>cjs : { default: string; }
5151

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { y } from "./redirect";
3838
import {} from "./redirect/index";
3939

4040
import a from "./types/esm";
41-
>a : string
41+
>a : typeof a
4242

4343
import * as esm from "./types/esm";
4444
>esm : typeof esm
@@ -47,5 +47,5 @@ import b from "./types/cjs";
4747
>b : string
4848

4949
import * as cjs from "./types/cjs";
50-
>cjs : string
50+
>cjs : { default: string; }
5151

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ declare module "path" {
3333
=== /mainJs.js ===
3434
import {} from "./a";
3535
import("./a");
36-
>import("./a") : Promise<typeof _>
36+
>import("./a") : Promise<{ default: typeof _; a: "a"; }>
3737
>"./a" : "./a"
3838

3939
const _ = require("./a");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ declare module "path" {
3333
=== /mainJs.js ===
3434
import {} from "./a";
3535
import("./a");
36-
>import("./a") : Promise<typeof _>
36+
>import("./a") : Promise<{ default: typeof _; a: "a"; }>
3737
>"./a" : "./a"
3838

3939
const _ = require("./a");

0 commit comments

Comments
 (0)