Skip to content

Commit 257b848

Browse files
committed
Walk back late import handling
1 parent 7521c63 commit 257b848

24 files changed

+101
-102
lines changed

src/compiler/checker.ts

+6-15
Original file line numberDiff line numberDiff line change
@@ -2116,16 +2116,7 @@ namespace ts {
21162116
return ambientModule;
21172117
}
21182118
const currentSourceFile = getSourceFileOfNode(location);
2119-
const resolvedModuleState = getResolvedModule(currentSourceFile, moduleReference);
2120-
let resolvedModule: ResolvedModuleFull;
2121-
if (currentSourceFile && resolvedModuleState === undefined) {
2122-
// Fallback to uncached lookup for late-bound module names which have not been resolved
2123-
const resolutions = host.resolveModuleName([moduleReference], currentSourceFile.fileName);
2124-
resolvedModule = firstOrUndefined(resolutions);
2125-
}
2126-
else {
2127-
resolvedModule = getResolvedModuleFromState(resolvedModuleState);
2128-
}
2119+
const resolvedModule = getResolvedModule(currentSourceFile, moduleReference);
21292120
const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule);
21302121
const sourceFile = resolvedModule && !resolutionDiagnostic && host.getSourceFile(resolvedModule.resolvedFileName);
21312122
if (sourceFile) {
@@ -8513,14 +8504,14 @@ namespace ts {
85138504
links.resolvedSymbol = unknownSymbol;
85148505
return links.resolvedType = unknownType;
85158506
}
8516-
const argumentType = getTypeFromTypeNode(node.argument);
8517-
const targetMeaning = node.isTypeOf ? SymbolFlags.Value : SymbolFlags.Type;
8518-
// TODO: Future work: support unions/generics/whatever via a deferred import-type
8519-
if (!argumentType || !(argumentType.flags & TypeFlags.StringLiteral)) {
8520-
error(node.argument, Diagnostics.Import_specifier_must_be_a_string_literal_type_but_here_is_0, argumentType ? typeToString(argumentType) : "undefined");
8507+
if (!isLiteralImportTypeNode(node)) {
8508+
error(node.argument, Diagnostics.String_literal_expected);
85218509
links.resolvedSymbol = unknownSymbol;
85228510
return links.resolvedType = unknownType;
85238511
}
8512+
const argumentType = getTypeFromTypeNode(node.argument);
8513+
const targetMeaning = node.isTypeOf ? SymbolFlags.Value : SymbolFlags.Type;
8514+
// TODO: Future work: support unions/generics/whatever via a deferred import-type
85248515
const moduleName = (argumentType as StringLiteralType).value;
85258516
const innerModuleSymbol = resolveExternalModule(node, moduleName, Diagnostics.Cannot_find_module_0, node, /*isForAugmentation*/ false);
85268517
if (!innerModuleSymbol) {

src/compiler/program.ts

+15-9
Original file line numberDiff line numberDiff line change
@@ -674,8 +674,7 @@ namespace ts {
674674
sourceFileToPackageName,
675675
redirectTargetsSet,
676676
isEmittedFile,
677-
getConfigFileParsingDiagnostics,
678-
resolveModuleName: resolveModuleNamesWorker
677+
getConfigFileParsingDiagnostics
679678
};
680679

681680
verifyCompilerOptions();
@@ -749,7 +748,7 @@ namespace ts {
749748
const result: ResolvedModuleFull[] = [];
750749
for (const moduleName of moduleNames) {
751750
const resolvedModule = file.resolvedModules.get(moduleName);
752-
result.push(getResolvedModuleFromState(resolvedModule));
751+
result.push(resolvedModule);
753752
}
754753
return result;
755754
}
@@ -778,7 +777,7 @@ namespace ts {
778777
const moduleName = moduleNames[i];
779778
// If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
780779
if (file === oldSourceFile && !hasInvalidatedResolution(oldSourceFile.path)) {
781-
const oldResolvedModule = getResolvedModuleFromState(oldSourceFile && oldSourceFile.resolvedModules.get(moduleName));
780+
const oldResolvedModule = oldSourceFile && oldSourceFile.resolvedModules.get(moduleName);
782781
if (oldResolvedModule) {
783782
if (isTraceEnabled(options, host)) {
784783
trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, containingFile);
@@ -844,7 +843,7 @@ namespace ts {
844843
// If we change our policy of rechecking failed lookups on each program create,
845844
// we should adjust the value returned here.
846845
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState: OldProgramState): boolean {
847-
const resolutionToFile = getResolvedModuleFromState(getResolvedModule(oldProgramState.oldSourceFile, moduleName));
846+
const resolutionToFile = getResolvedModule(oldProgramState.oldSourceFile, moduleName);
848847
const resolvedFile = resolutionToFile && oldProgramState.program && oldProgramState.program.getSourceFile(resolutionToFile.resolvedFileName);
849848
if (resolutionToFile && resolvedFile && !resolvedFile.externalModuleIndicator) {
850849
// In the old program, we resolved to an ambient module that was in the same
@@ -1027,10 +1026,10 @@ namespace ts {
10271026
const oldProgramState: OldProgramState = { program: oldProgram, oldSourceFile, modifiedFilePaths };
10281027
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, oldProgramState);
10291028
// ensure that module resolution results are still correct
1030-
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo, getResolvedModuleFromState);
1029+
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
10311030
if (resolutionsChanged) {
10321031
oldProgram.structureIsReused = StructureIsReused.SafeModules;
1033-
newSourceFile.resolvedModules = zipToMap(moduleNames, map(resolutions, r => ({ tag: "success" as "success", data: r })));
1032+
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
10341033
}
10351034
else {
10361035
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
@@ -1688,12 +1687,19 @@ namespace ts {
16881687
imports = append(imports, node.arguments[0] as StringLiteralLike);
16891688
}
16901689
else if (isLiteralImportTypeNode(node)) {
1691-
(imports || (imports = [])).push(node.argument.literal);
1690+
imports = append(imports, node.argument.literal);
16921691
}
16931692
else {
1694-
forEachChild(node, collectDynamicImportOrRequireCalls);
1693+
collectDynamicImportOrRequireCallsForEachChild(node);
1694+
if (hasJSDocNodes(node)) {
1695+
forEach(node.jsDoc, collectDynamicImportOrRequireCallsForEachChild);
1696+
}
16951697
}
16961698
}
1699+
1700+
function collectDynamicImportOrRequireCallsForEachChild(node: Node) {
1701+
forEachChild(node, collectDynamicImportOrRequireCalls);
1702+
}
16971703
}
16981704

16991705
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */

src/compiler/types.ts

+1-9
Original file line numberDiff line numberDiff line change
@@ -2584,7 +2584,7 @@ namespace ts {
25842584
// Stores a mapping 'external module reference text' -> 'resolved file name' | undefined
25852585
// It is used to resolve module names in the checker.
25862586
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
2587-
/* @internal */ resolvedModules: Map<ResolvedModuleState>;
2587+
/* @internal */ resolvedModules: Map<ResolvedModuleFull>;
25882588
/* @internal */ resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
25892589
/* @internal */ imports: ReadonlyArray<StringLiteralLike>;
25902590
// Identifier only if `declare global`
@@ -2598,9 +2598,6 @@ namespace ts {
25982598
/* @internal */ localJsxFactory?: EntityName;
25992599
}
26002600

2601-
/* @internal */
2602-
export type ResolvedModuleState = { tag: "success", data: ResolvedModuleFull } | { tag: "fail" };
2603-
26042601
export interface Bundle extends Node {
26052602
kind: SyntaxKind.Bundle;
26062603
sourceFiles: ReadonlyArray<SourceFile>;
@@ -2725,9 +2722,6 @@ namespace ts {
27252722
/* @internal */ redirectTargetsSet: Map<true>;
27262723
/** Is the file emitted file */
27272724
/* @internal */ isEmittedFile(file: string): boolean;
2728-
2729-
/* Used by the type checker to resolve module names which are encountered late */
2730-
/* @internal */ resolveModuleName(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModuleFull[];
27312725
}
27322726

27332727
/* @internal */
@@ -2801,8 +2795,6 @@ namespace ts {
28012795
getSourceFiles(): ReadonlyArray<SourceFile>;
28022796
getSourceFile(fileName: string): SourceFile | undefined;
28032797
getResolvedTypeReferenceDirectives(): ReadonlyMap<ResolvedTypeReferenceDirective>;
2804-
2805-
resolveModuleName(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModuleFull[];
28062798
}
28072799

28082800
export interface TypeChecker {

src/compiler/utilities.ts

+7-14
Original file line numberDiff line numberDiff line change
@@ -83,21 +83,16 @@ namespace ts {
8383
return node.end - node.pos;
8484
}
8585

86-
export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModuleState {
86+
export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModuleFull {
8787
return sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules.get(moduleNameText);
8888
}
8989

90-
export function getResolvedModuleFromState(state: ResolvedModuleState): ResolvedModuleFull | undefined {
91-
return state && state.tag === "success" ? state.data : undefined;
92-
}
93-
94-
const failedLookup: { tag: "fail" } = { tag: "fail" };
9590
export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleFull): void {
9691
if (!sourceFile.resolvedModules) {
97-
sourceFile.resolvedModules = createMap<ResolvedModuleState>();
92+
sourceFile.resolvedModules = createMap<ResolvedModuleFull>();
9893
}
9994

100-
sourceFile.resolvedModules.set(moduleNameText, resolvedModule ? { tag: "success", data: resolvedModule } : failedLookup);
95+
sourceFile.resolvedModules.set(moduleNameText, resolvedModule);
10196
}
10297

10398
export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective): void {
@@ -129,18 +124,16 @@ namespace ts {
129124
return oldResolution.resolvedFileName === newResolution.resolvedFileName && oldResolution.primary === newResolution.primary;
130125
}
131126

132-
export function hasChangesInResolutions<T, U = T>(
127+
export function hasChangesInResolutions<T>(
133128
names: ReadonlyArray<string>,
134129
newResolutions: ReadonlyArray<T>,
135-
oldResolutions: ReadonlyMap<U>,
136-
comparer: (oldResolution: T, newResolution: T) => boolean,
137-
oldMapper?: (x: U) => T): boolean {
130+
oldResolutions: ReadonlyMap<T>,
131+
comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
138132
Debug.assert(names.length === newResolutions.length);
139133

140134
for (let i = 0; i < names.length; i++) {
141135
const newResolution = newResolutions[i];
142-
const oldRes = oldResolutions && oldResolutions.get(names[i]);
143-
const oldResolution = oldMapper ? oldMapper(oldRes) : oldRes as {} as T;
136+
const oldResolution = oldResolutions && oldResolutions.get(names[i]);
144137
const changed =
145138
oldResolution
146139
? !newResolution || !comparer(oldResolution, newResolution)

src/harness/unittests/reuseProgramStructure.ts

+1-13
Original file line numberDiff line numberDiff line change
@@ -219,19 +219,7 @@ namespace ts {
219219
}
220220

221221
function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map<ResolvedModule>): void {
222-
checkCache("resolved modules", program, fileName, expectedContent, f => {
223-
if (!f.resolvedModules) return undefined;
224-
const mapped: Map<ResolvedModule> = createMap();
225-
forEachEntry(f.resolvedModules, (value, key) => {
226-
if (value.tag === "success") {
227-
mapped.set(key, value.data);
228-
}
229-
else {
230-
mapped.set(key, undefined);
231-
}
232-
});
233-
return mapped;
234-
}, checkResolvedModule);
222+
checkCache("resolved modules", program, fileName, expectedContent, f => f.resolvedModules, checkResolvedModule);
235223
}
236224

237225
function checkResolvedTypeDirectivesCache(program: Program, fileName: string, expectedContent: Map<ResolvedTypeReferenceDirective>): void {

src/server/project.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ namespace ts.server {
780780
if (file.resolvedModules) {
781781
file.resolvedModules.forEach((resolvedModule, name) => {
782782
// pick unresolved non-relative names
783-
if (!getResolvedModuleFromState(resolvedModule) && !isExternalModuleNameRelative(name) && !isAmbientlyDeclaredModule(name)) {
783+
if (!resolvedModule && !isExternalModuleNameRelative(name) && !isAmbientlyDeclaredModule(name)) {
784784
// for non-scoped names extract part up-to the first slash
785785
// for scoped names - extract up to the second slash
786786
let trimmed = name.trim();

src/services/codefixes/convertToEs6Module.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace ts.codefix {
1919

2020
function fixImportOfModuleExports(importingFile: SourceFile, exportingFile: SourceFile, changes: textChanges.ChangeTracker) {
2121
for (const moduleSpecifier of importingFile.imports) {
22-
const imported = getResolvedModuleFromState(getResolvedModule(importingFile, moduleSpecifier.text));
22+
const imported = getResolvedModule(importingFile, moduleSpecifier.text);
2323
if (!imported || imported.resolvedFileName !== exportingFile.fileName) {
2424
continue;
2525
}

src/services/codefixes/fixSpelling.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ namespace ts.codefix {
7575
function getResolvedSourceFileFromImportDeclaration (sourceFile: SourceFile, context: CodeFixContextBase, importDeclaration: ImportDeclaration): SourceFile | undefined {
7676
if (!importDeclaration || !isStringLiteralLike(importDeclaration.moduleSpecifier)) return undefined;
7777

78-
const resolvedModule = getResolvedModuleFromState(getResolvedModule(sourceFile, importDeclaration.moduleSpecifier.text));
78+
const resolvedModule = getResolvedModule(sourceFile, importDeclaration.moduleSpecifier.text);
7979
if (!resolvedModule) return undefined;
8080

8181
return context.program.getSourceFile(resolvedModule.resolvedFileName);

src/services/codefixes/importFixes.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,7 @@ namespace ts.codefix {
323323
*/
324324
function getAllModulePaths(program: Program, { fileName }: SourceFile): ReadonlyArray<string> {
325325
const symlinks = mapDefined(program.getSourceFiles(), sf =>
326-
sf.resolvedModules && firstDefinedIterator(sf.resolvedModules.values(), state => {
327-
const res = getResolvedModuleFromState(state);
326+
sf.resolvedModules && firstDefinedIterator(sf.resolvedModules.values(), res => {
328327
return res && res.resolvedFileName === fileName ? res.originalPath : undefined;
329328
}));
330329
return symlinks.length === 0 ? [fileName] : symlinks;

src/services/services.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ namespace ts {
572572
public languageVariant: LanguageVariant;
573573
public identifiers: Map<string>;
574574
public nameTable: UnderscoreEscapedMap<number>;
575-
public resolvedModules: Map<ResolvedModuleState>;
575+
public resolvedModules: Map<ResolvedModuleFull>;
576576
public resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
577577
public imports: ReadonlyArray<StringLiteralLike>;
578578
public moduleAugmentations: StringLiteral[];

src/services/suggestionDiagnostics.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ namespace ts {
3737
const importNode = importFromModuleSpecifier(moduleSpecifier);
3838
const name = importNameForConvertToDefaultImport(importNode);
3939
if (!name) continue;
40-
const module = getResolvedModuleFromState(getResolvedModule(sourceFile, moduleSpecifier.text));
40+
const module = getResolvedModule(sourceFile, moduleSpecifier.text);
4141
const resolvedFile = module && program.getSourceFile(module.resolvedFileName);
4242
if (resolvedFile && resolvedFile.externalModuleIndicator && isExportAssignment(resolvedFile.externalModuleIndicator) && resolvedFile.externalModuleIndicator.isExportEquals) {
4343
diags.push(createDiagnosticForNode(name, Diagnostics.Import_may_be_converted_to_a_default_import));

tests/baselines/reference/importTypeGeneric.errors.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
tests/cases/conformance/types/import/usage.ts(1,67): error TS1341: Import specifier must be a string literal type, but here is 'T'.
2-
tests/cases/conformance/types/import/usage.ts(5,72): error TS1341: Import specifier must be a string literal type, but here is 'T'.
1+
tests/cases/conformance/types/import/usage.ts(1,67): error TS1141: String literal expected.
2+
tests/cases/conformance/types/import/usage.ts(5,72): error TS1141: String literal expected.
33

44

55
==== tests/cases/conformance/types/import/a.d.ts (0 errors) ====
@@ -13,13 +13,13 @@ tests/cases/conformance/types/import/usage.ts(5,72): error TS1341: Import specif
1313
==== tests/cases/conformance/types/import/usage.ts (2 errors) ====
1414
export function getFooFrom<T extends "./a" | "./b">(v: T): import(T).Foo {
1515
~
16-
!!! error TS1341: Import specifier must be a string literal type, but here is 'T'.
16+
!!! error TS1141: String literal expected.
1717
return undefined as any;
1818
}
1919

2020
export function getFooValueFrom<T extends "./a" | "./b">(v: T): import(T).Foo["a"] {
2121
~
22-
!!! error TS1341: Import specifier must be a string literal type, but here is 'T'.
22+
!!! error TS1141: String literal expected.
2323
return undefined as any;
2424
}
2525

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
tests/cases/conformance/types/import/chainer.ts(1,24): error TS1141: String literal expected.
2+
3+
4+
==== tests/cases/conformance/types/import/a.d.ts (0 errors) ====
5+
export type LookAt = "./b";
6+
==== tests/cases/conformance/types/import/b.d.ts (0 errors) ====
7+
export type Value = "yes";
8+
==== tests/cases/conformance/types/import/chainer.ts (1 errors) ====
9+
export const x: import(import("./a").LookAt).Value = "yes";
10+
~~~~~~~~~~~~~~~~~~~~
11+
!!! error TS1141: String literal expected.
12+

tests/baselines/reference/importTypeNested.symbols

-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,4 @@ export type Value = "yes";
1010
export const x: import(import("./a").LookAt).Value = "yes";
1111
>x : Symbol(x, Decl(chainer.ts, 0, 12))
1212
>LookAt : Symbol(LookAt, Decl(a.d.ts, 0, 0))
13-
>Value : Symbol(Value, Decl(b.d.ts, 0, 0))
1413

tests/baselines/reference/importTypeNested.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ export type Value = "yes";
88

99
=== tests/cases/conformance/types/import/chainer.ts ===
1010
export const x: import(import("./a").LookAt).Value = "yes";
11-
>x : "yes"
11+
>x : any
1212
>LookAt : "./b"
13-
>Value : "yes"
13+
>Value : No type information available!
1414
>"yes" : "yes"
1515

tests/baselines/reference/importTypeNestedNoRef.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
tests/cases/conformance/types/import/chainer.ts(1,17): error TS2307: Cannot find module './b'.
1+
tests/cases/conformance/types/import/chainer.ts(1,24): error TS1141: String literal expected.
22

33

44
==== tests/cases/conformance/types/import/chainer.ts (1 errors) ====
55
export const x: import(import("./a").LookAt).Value = "yes"; // expect outter import to fail, since b.d.ts isn't in the build
6-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7-
!!! error TS2307: Cannot find module './b'.
6+
~~~~~~~~~~~~~~~~~~~~
7+
!!! error TS1141: String literal expected.
88

99
==== tests/cases/conformance/types/import/a.d.ts (0 errors) ====
1010
export type LookAt = "./b";

0 commit comments

Comments
 (0)