Skip to content

Commit cea73a2

Browse files
committed
Initial import/export/self-name support
1 parent 15e1904 commit cea73a2

File tree

50 files changed

+8921
-309
lines changed

Some content is hidden

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

50 files changed

+8921
-309
lines changed

src/compiler/checker.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ namespace ts {
303303
host.getSourceFiles().forEach(sf => {
304304
if (!sf.resolvedModules) return;
305305

306-
forEachEntry(sf.resolvedModules, r => {
306+
sf.resolvedModules.forEach(r => {
307307
if (r && r.packageId) map.set(r.packageId.name, r.extension === Extension.Dts || !!map.get(r.packageId.name));
308308
});
309309
});
@@ -3325,7 +3325,13 @@ namespace ts {
33253325
return ambientModule;
33263326
}
33273327
const currentSourceFile = getSourceFileOfNode(location);
3328-
const resolvedModule = getResolvedModule(currentSourceFile, moduleReference)!; // TODO: GH#18217
3328+
const contextSpecifier = isStringLiteralLike(location)
3329+
? location
3330+
: (isImportCall(location) ? location : findAncestor(location, isImportCall))?.arguments[0] ||
3331+
(isImportDeclaration(location) ? location : findAncestor(location, isImportDeclaration))?.moduleSpecifier ||
3332+
(isExternalModuleImportEqualsDeclaration(location) ? location : findAncestor(location, isExternalModuleImportEqualsDeclaration))?.moduleReference.expression ||
3333+
(isExportDeclaration(location) ? location : findAncestor(location, isExportDeclaration))?.moduleSpecifier;
3334+
const resolvedModule = getResolvedModule(currentSourceFile, moduleReference, contextSpecifier && isStringLiteralLike(contextSpecifier) ? getModeForUsageLocation(currentSourceFile, contextSpecifier) : undefined)!; // TODO: GH#18217
33293335
const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule);
33303336
const sourceFile = resolvedModule && !resolutionDiagnostic && host.getSourceFile(resolvedModule.resolvedFileName);
33313337
if (sourceFile) {

src/compiler/diagnosticMessages.json

+29
Original file line numberDiff line numberDiff line change
@@ -4843,6 +4843,35 @@
48434843
"code": 6257
48444844
},
48454845

4846+
"Directory '{0}' has no containing package.json scope. Imports will not resolve.": {
4847+
"category": "Message",
4848+
"code": 6270
4849+
},
4850+
"Import specifier '{0}' does not exist in package.json scope at path '{1}'.": {
4851+
"category": "Message",
4852+
"code": 6271
4853+
},
4854+
"Invalid import specifier '{0}' has no possible resolutions.": {
4855+
"category": "Message",
4856+
"code": 6272
4857+
},
4858+
"package.json scope '{0}' has no imports defined.": {
4859+
"category": "Message",
4860+
"code": 6273
4861+
},
4862+
"package.json scope '{0}' explicitly maps specifier '{1}' to null.": {
4863+
"category": "Message",
4864+
"code": 6274
4865+
},
4866+
"package.json scope '{0}' has invalid type for target of specifier '{1}'": {
4867+
"category": "Message",
4868+
"code": 6275
4869+
},
4870+
"Export specifier '{0}' does not exist in package.json scope at path '{1}'.": {
4871+
"category": "Message",
4872+
"code": 6276
4873+
},
4874+
48464875
"Enable project compilation": {
48474876
"category": "Message",
48484877
"code": 6302

src/compiler/moduleNameResolver.ts

+433-54
Large diffs are not rendered by default.

src/compiler/program.ts

+98-44
Large diffs are not rendered by default.

src/compiler/resolutionCache.ts

+34-28
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ namespace ts {
55
startRecordingFilesWithChangedResolutions(): void;
66
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
77

8-
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): (ResolvedModuleFull | undefined)[];
9-
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): CachedResolvedModuleWithFailedLookupLocations | undefined;
8+
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[];
9+
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): CachedResolvedModuleWithFailedLookupLocations | undefined;
1010
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[];
1111

1212
invalidateResolutionsOfFailedLookupLocations(): boolean;
@@ -166,8 +166,8 @@ namespace ts {
166166
// The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file.
167167
// The key in the map is source file's path.
168168
// The values are Map of resolutions with key being name lookedup.
169-
const resolvedModuleNames = new Map<Path, ESMap<string, CachedResolvedModuleWithFailedLookupLocations>>();
170-
const perDirectoryResolvedModuleNames: CacheWithRedirects<ESMap<string, CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
169+
const resolvedModuleNames = new Map<Path, ModeAwareCache<CachedResolvedModuleWithFailedLookupLocations>>();
170+
const perDirectoryResolvedModuleNames: CacheWithRedirects<ModeAwareCache<CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
171171
const nonRelativeModuleNameCache: CacheWithRedirects<PerModuleNameCache> = createCacheWithRedirects();
172172
const moduleResolutionCache = createModuleResolutionCache(
173173
getCurrentDirectory(),
@@ -177,8 +177,8 @@ namespace ts {
177177
nonRelativeModuleNameCache,
178178
);
179179

180-
const resolvedTypeReferenceDirectives = new Map<Path, ESMap<string, CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
181-
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<ESMap<string, CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
180+
const resolvedTypeReferenceDirectives = new Map<Path, ModeAwareCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
181+
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<ModeAwareCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
182182
const typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(
183183
getCurrentDirectory(),
184184
resolutionHost.getCanonicalFileName,
@@ -354,27 +354,28 @@ namespace ts {
354354
names: readonly string[];
355355
containingFile: string;
356356
redirectedReference: ResolvedProjectReference | undefined;
357-
cache: ESMap<Path, ESMap<string, T>>;
358-
perDirectoryCacheWithRedirects: CacheWithRedirects<ESMap<string, T>>;
359-
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T;
357+
cache: ESMap<Path, ModeAwareCache<T>>;
358+
perDirectoryCacheWithRedirects: CacheWithRedirects<ModeAwareCache<T>>;
359+
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile) => T;
360360
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
361361
shouldRetryResolution: (t: T) => boolean;
362362
reusedNames?: readonly string[];
363363
logChanges?: boolean;
364+
containingSourceFile?: SourceFile;
364365
}
365366
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
366367
names, containingFile, redirectedReference,
367368
cache, perDirectoryCacheWithRedirects,
368369
loader, getResolutionWithResolvedFileName,
369-
shouldRetryResolution, reusedNames, logChanges
370+
shouldRetryResolution, reusedNames, logChanges, containingSourceFile
370371
}: ResolveNamesWithLocalCacheInput<T, R>): (R | undefined)[] {
371372
const path = resolutionHost.toPath(containingFile);
372-
const resolutionsInFile = cache.get(path) || cache.set(path, new Map()).get(path)!;
373+
const resolutionsInFile = cache.get(path) || cache.set(path, createModeAwareCache()).get(path)!;
373374
const dirPath = getDirectoryPath(path);
374375
const perDirectoryCache = perDirectoryCacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
375376
let perDirectoryResolution = perDirectoryCache.get(dirPath);
376377
if (!perDirectoryResolution) {
377-
perDirectoryResolution = new Map();
378+
perDirectoryResolution = createModeAwareCache();
378379
perDirectoryCache.set(dirPath, perDirectoryResolution);
379380
}
380381
const resolvedModules: (R | undefined)[] = [];
@@ -388,16 +389,19 @@ namespace ts {
388389
!redirectedReference || redirectedReference.sourceFile.path !== oldRedirect.sourceFile.path :
389390
!!redirectedReference;
390391

391-
const seenNamesInFile = new Map<string, true>();
392+
const seenNamesInFile = createModeAwareCache<true>();
393+
let i = 0;
392394
for (const name of names) {
393-
let resolution = resolutionsInFile.get(name);
395+
const mode = containingSourceFile ? getModeForResolutionAtIndex(containingSourceFile, i) : undefined;
396+
i++;
397+
let resolution = resolutionsInFile.get(name, mode);
394398
// Resolution is valid if it is present and not invalidated
395-
if (!seenNamesInFile.has(name) &&
399+
if (!seenNamesInFile.has(name, mode) &&
396400
unmatchedRedirects || !resolution || resolution.isInvalidated ||
397401
// If the name is unresolved import that was invalidated, recalculate
398402
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution))) {
399403
const existingResolution = resolution;
400-
const resolutionInDirectory = perDirectoryResolution.get(name);
404+
const resolutionInDirectory = perDirectoryResolution.get(name, mode);
401405
if (resolutionInDirectory) {
402406
resolution = resolutionInDirectory;
403407
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
@@ -425,10 +429,10 @@ namespace ts {
425429
}
426430
}
427431
else {
428-
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference);
429-
perDirectoryResolution.set(name, resolution);
432+
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference, containingSourceFile);
433+
perDirectoryResolution.set(name, mode, resolution);
430434
}
431-
resolutionsInFile.set(name, resolution);
435+
resolutionsInFile.set(name, mode, resolution);
432436
watchFailedLookupLocationsOfExternalModuleResolutions(name, resolution, path, getResolutionWithResolvedFileName);
433437
if (existingResolution) {
434438
stopWatchFailedLookupLocationOfResolution(existingResolution, path, getResolutionWithResolvedFileName);
@@ -442,7 +446,7 @@ namespace ts {
442446
}
443447
else {
444448
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
445-
if (isTraceEnabled(compilerOptions, host) && !seenNamesInFile.has(name)) {
449+
if (isTraceEnabled(compilerOptions, host) && !seenNamesInFile.has(name, mode)) {
446450
const resolved = getResolutionWithResolvedFileName(resolution);
447451
trace(
448452
host,
@@ -465,15 +469,15 @@ namespace ts {
465469
}
466470
}
467471
Debug.assert(resolution !== undefined && !resolution.isInvalidated);
468-
seenNamesInFile.set(name, true);
472+
seenNamesInFile.set(name, mode, true);
469473
resolvedModules.push(getResolutionWithResolvedFileName(resolution));
470474
}
471475

472476
// Stop watching and remove the unused name
473-
resolutionsInFile.forEach((resolution, name) => {
474-
if (!seenNamesInFile.has(name) && !contains(reusedNames, name)) {
477+
resolutionsInFile.forEach((resolution, name, mode) => {
478+
if (!seenNamesInFile.has(name, mode) && !contains(reusedNames, name)) {
475479
stopWatchFailedLookupLocationOfResolution(resolution, path, getResolutionWithResolvedFileName);
476-
resolutionsInFile.delete(name);
480+
resolutionsInFile.delete(name, mode);
477481
}
478482
});
479483

@@ -511,7 +515,7 @@ namespace ts {
511515
});
512516
}
513517

514-
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): (ResolvedModuleFull | undefined)[] {
518+
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[] {
515519
return resolveNamesWithLocalCache<CachedResolvedModuleWithFailedLookupLocations, ResolvedModuleFull>({
516520
names: moduleNames,
517521
containingFile,
@@ -523,12 +527,14 @@ namespace ts {
523527
shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
524528
reusedNames,
525529
logChanges: logChangesWhenResolvingModule,
530+
containingSourceFile,
526531
});
527532
}
528533

529-
function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): CachedResolvedModuleWithFailedLookupLocations | undefined {
534+
function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): CachedResolvedModuleWithFailedLookupLocations | undefined {
530535
const cache = resolvedModuleNames.get(resolutionHost.toPath(containingFile));
531-
return cache && cache.get(moduleName);
536+
if (!cache) return undefined;
537+
return cache.get(moduleName, resolutionMode);
532538
}
533539

534540
function isNodeModulesAtTypesDirectory(dirPath: Path) {
@@ -751,7 +757,7 @@ namespace ts {
751757
}
752758

753759
function removeResolutionsOfFileFromCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
754-
cache: ESMap<string, ESMap<string, T>>,
760+
cache: ESMap<string, ModeAwareCache<T>>,
755761
filePath: Path,
756762
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
757763
) {

src/compiler/tsbuildPublic.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,9 @@ namespace ts {
281281
const moduleResolutionCache = !compilerHost.resolveModuleNames ? createModuleResolutionCache(currentDirectory, getCanonicalFileName) : undefined;
282282
const typeReferenceDirectiveResolutionCache = !compilerHost.resolveTypeReferenceDirectives ? createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache()) : undefined;
283283
if (!compilerHost.resolveModuleNames) {
284-
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, state.projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference).resolvedModule!;
285-
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference) =>
286-
loadWithLocalCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader);
284+
const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, state.projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule!;
285+
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, _options, containingSourceFile) =>
286+
loadWithModeAwareCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), Debug.checkDefined(containingSourceFile), containingFile, redirectedReference, loader);
287287
compilerHost.getModuleResolutionCache = () => moduleResolutionCache;
288288
}
289289
if (!compilerHost.resolveTypeReferenceDirectives) {

src/compiler/types.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3607,8 +3607,8 @@ namespace ts {
36073607
// Stores a mapping 'external module reference text' -> 'resolved file name' | undefined
36083608
// It is used to resolve module names in the checker.
36093609
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
3610-
/* @internal */ resolvedModules?: ESMap<string, ResolvedModuleFull | undefined>;
3611-
/* @internal */ resolvedTypeReferenceDirectiveNames: ESMap<string, ResolvedTypeReferenceDirective | undefined>;
3610+
/* @internal */ resolvedModules?: ModeAwareCache<ResolvedModuleFull | undefined>;
3611+
/* @internal */ resolvedTypeReferenceDirectiveNames: ModeAwareCache<ResolvedTypeReferenceDirective | undefined>;
36123612
/* @internal */ imports: readonly StringLiteralLike[];
36133613
// Identifier only if `declare global`
36143614
/* @internal */ moduleAugmentations: readonly (StringLiteral | Identifier)[];
@@ -3990,7 +3990,7 @@ namespace ts {
39903990
/* @internal */ getFileIncludeReasons(): MultiMap<Path, FileIncludeReason>;
39913991
/* @internal */ useCaseSensitiveFileNames(): boolean;
39923992

3993-
/* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined;
3993+
/* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, mode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined;
39943994

39953995
getProjectReferences(): readonly ProjectReference[] | undefined;
39963996
getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
@@ -6614,7 +6614,7 @@ namespace ts {
66146614
* If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just
66156615
* 'throw new Error("NotImplemented")'
66166616
*/
6617-
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedModule | undefined)[];
6617+
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
66186618
/**
66196619
* Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it
66206620
*/

0 commit comments

Comments
 (0)