Skip to content

Commit

Permalink
Initial import/export/self-name support
Browse files Browse the repository at this point in the history
  • Loading branch information
weswigham committed Sep 14, 2021
1 parent 15e1904 commit cea73a2
Show file tree
Hide file tree
Showing 50 changed files with 8,921 additions and 309 deletions.
10 changes: 8 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ namespace ts {
host.getSourceFiles().forEach(sf => {
if (!sf.resolvedModules) return;

forEachEntry(sf.resolvedModules, r => {
sf.resolvedModules.forEach(r => {
if (r && r.packageId) map.set(r.packageId.name, r.extension === Extension.Dts || !!map.get(r.packageId.name));
});
});
Expand Down Expand Up @@ -3325,7 +3325,13 @@ namespace ts {
return ambientModule;
}
const currentSourceFile = getSourceFileOfNode(location);
const resolvedModule = getResolvedModule(currentSourceFile, moduleReference)!; // TODO: GH#18217
const contextSpecifier = isStringLiteralLike(location)
? location
: (isImportCall(location) ? location : findAncestor(location, isImportCall))?.arguments[0] ||
(isImportDeclaration(location) ? location : findAncestor(location, isImportDeclaration))?.moduleSpecifier ||
(isExternalModuleImportEqualsDeclaration(location) ? location : findAncestor(location, isExternalModuleImportEqualsDeclaration))?.moduleReference.expression ||
(isExportDeclaration(location) ? location : findAncestor(location, isExportDeclaration))?.moduleSpecifier;
const resolvedModule = getResolvedModule(currentSourceFile, moduleReference, contextSpecifier && isStringLiteralLike(contextSpecifier) ? getModeForUsageLocation(currentSourceFile, contextSpecifier) : undefined)!; // TODO: GH#18217
const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule);
const sourceFile = resolvedModule && !resolutionDiagnostic && host.getSourceFile(resolvedModule.resolvedFileName);
if (sourceFile) {
Expand Down
29 changes: 29 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4843,6 +4843,35 @@
"code": 6257
},

"Directory '{0}' has no containing package.json scope. Imports will not resolve.": {
"category": "Message",
"code": 6270
},
"Import specifier '{0}' does not exist in package.json scope at path '{1}'.": {
"category": "Message",
"code": 6271
},
"Invalid import specifier '{0}' has no possible resolutions.": {
"category": "Message",
"code": 6272
},
"package.json scope '{0}' has no imports defined.": {
"category": "Message",
"code": 6273
},
"package.json scope '{0}' explicitly maps specifier '{1}' to null.": {
"category": "Message",
"code": 6274
},
"package.json scope '{0}' has invalid type for target of specifier '{1}'": {
"category": "Message",
"code": 6275
},
"Export specifier '{0}' does not exist in package.json scope at path '{1}'.": {
"category": "Message",
"code": 6276
},

"Enable project compilation": {
"category": "Message",
"code": 6302
Expand Down
487 changes: 433 additions & 54 deletions src/compiler/moduleNameResolver.ts

Large diffs are not rendered by default.

142 changes: 98 additions & 44 deletions src/compiler/program.ts

Large diffs are not rendered by default.

62 changes: 34 additions & 28 deletions src/compiler/resolutionCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ namespace ts {
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;

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

invalidateResolutionsOfFailedLookupLocations(): boolean;
Expand Down Expand Up @@ -166,8 +166,8 @@ namespace ts {
// The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file.
// The key in the map is source file's path.
// The values are Map of resolutions with key being name lookedup.
const resolvedModuleNames = new Map<Path, ESMap<string, CachedResolvedModuleWithFailedLookupLocations>>();
const perDirectoryResolvedModuleNames: CacheWithRedirects<ESMap<string, CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
const resolvedModuleNames = new Map<Path, ModeAwareCache<CachedResolvedModuleWithFailedLookupLocations>>();
const perDirectoryResolvedModuleNames: CacheWithRedirects<ModeAwareCache<CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
const nonRelativeModuleNameCache: CacheWithRedirects<PerModuleNameCache> = createCacheWithRedirects();
const moduleResolutionCache = createModuleResolutionCache(
getCurrentDirectory(),
Expand All @@ -177,8 +177,8 @@ namespace ts {
nonRelativeModuleNameCache,
);

const resolvedTypeReferenceDirectives = new Map<Path, ESMap<string, CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<ESMap<string, CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
const resolvedTypeReferenceDirectives = new Map<Path, ModeAwareCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<ModeAwareCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
const typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(
getCurrentDirectory(),
resolutionHost.getCanonicalFileName,
Expand Down Expand Up @@ -354,27 +354,28 @@ namespace ts {
names: readonly string[];
containingFile: string;
redirectedReference: ResolvedProjectReference | undefined;
cache: ESMap<Path, ESMap<string, T>>;
perDirectoryCacheWithRedirects: CacheWithRedirects<ESMap<string, T>>;
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T;
cache: ESMap<Path, ModeAwareCache<T>>;
perDirectoryCacheWithRedirects: CacheWithRedirects<ModeAwareCache<T>>;
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile) => T;
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>;
shouldRetryResolution: (t: T) => boolean;
reusedNames?: readonly string[];
logChanges?: boolean;
containingSourceFile?: SourceFile;
}
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
names, containingFile, redirectedReference,
cache, perDirectoryCacheWithRedirects,
loader, getResolutionWithResolvedFileName,
shouldRetryResolution, reusedNames, logChanges
shouldRetryResolution, reusedNames, logChanges, containingSourceFile
}: ResolveNamesWithLocalCacheInput<T, R>): (R | undefined)[] {
const path = resolutionHost.toPath(containingFile);
const resolutionsInFile = cache.get(path) || cache.set(path, new Map()).get(path)!;
const resolutionsInFile = cache.get(path) || cache.set(path, createModeAwareCache()).get(path)!;
const dirPath = getDirectoryPath(path);
const perDirectoryCache = perDirectoryCacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
let perDirectoryResolution = perDirectoryCache.get(dirPath);
if (!perDirectoryResolution) {
perDirectoryResolution = new Map();
perDirectoryResolution = createModeAwareCache();
perDirectoryCache.set(dirPath, perDirectoryResolution);
}
const resolvedModules: (R | undefined)[] = [];
Expand All @@ -388,16 +389,19 @@ namespace ts {
!redirectedReference || redirectedReference.sourceFile.path !== oldRedirect.sourceFile.path :
!!redirectedReference;

const seenNamesInFile = new Map<string, true>();
const seenNamesInFile = createModeAwareCache<true>();
let i = 0;
for (const name of names) {
let resolution = resolutionsInFile.get(name);
const mode = containingSourceFile ? getModeForResolutionAtIndex(containingSourceFile, i) : undefined;
i++;
let resolution = resolutionsInFile.get(name, mode);
// Resolution is valid if it is present and not invalidated
if (!seenNamesInFile.has(name) &&
if (!seenNamesInFile.has(name, mode) &&
unmatchedRedirects || !resolution || resolution.isInvalidated ||
// If the name is unresolved import that was invalidated, recalculate
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && shouldRetryResolution(resolution))) {
const existingResolution = resolution;
const resolutionInDirectory = perDirectoryResolution.get(name);
const resolutionInDirectory = perDirectoryResolution.get(name, mode);
if (resolutionInDirectory) {
resolution = resolutionInDirectory;
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
Expand Down Expand Up @@ -425,10 +429,10 @@ namespace ts {
}
}
else {
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference);
perDirectoryResolution.set(name, resolution);
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference, containingSourceFile);
perDirectoryResolution.set(name, mode, resolution);
}
resolutionsInFile.set(name, resolution);
resolutionsInFile.set(name, mode, resolution);
watchFailedLookupLocationsOfExternalModuleResolutions(name, resolution, path, getResolutionWithResolvedFileName);
if (existingResolution) {
stopWatchFailedLookupLocationOfResolution(existingResolution, path, getResolutionWithResolvedFileName);
Expand All @@ -442,7 +446,7 @@ namespace ts {
}
else {
const host = resolutionHost.getCompilerHost?.() || resolutionHost;
if (isTraceEnabled(compilerOptions, host) && !seenNamesInFile.has(name)) {
if (isTraceEnabled(compilerOptions, host) && !seenNamesInFile.has(name, mode)) {
const resolved = getResolutionWithResolvedFileName(resolution);
trace(
host,
Expand All @@ -465,15 +469,15 @@ namespace ts {
}
}
Debug.assert(resolution !== undefined && !resolution.isInvalidated);
seenNamesInFile.set(name, true);
seenNamesInFile.set(name, mode, true);
resolvedModules.push(getResolutionWithResolvedFileName(resolution));
}

// Stop watching and remove the unused name
resolutionsInFile.forEach((resolution, name) => {
if (!seenNamesInFile.has(name) && !contains(reusedNames, name)) {
resolutionsInFile.forEach((resolution, name, mode) => {
if (!seenNamesInFile.has(name, mode) && !contains(reusedNames, name)) {
stopWatchFailedLookupLocationOfResolution(resolution, path, getResolutionWithResolvedFileName);
resolutionsInFile.delete(name);
resolutionsInFile.delete(name, mode);
}
});

Expand Down Expand Up @@ -511,7 +515,7 @@ namespace ts {
});
}

function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): (ResolvedModuleFull | undefined)[] {
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[] {
return resolveNamesWithLocalCache<CachedResolvedModuleWithFailedLookupLocations, ResolvedModuleFull>({
names: moduleNames,
containingFile,
Expand All @@ -523,12 +527,14 @@ namespace ts {
shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
reusedNames,
logChanges: logChangesWhenResolvingModule,
containingSourceFile,
});
}

function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): CachedResolvedModuleWithFailedLookupLocations | undefined {
function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): CachedResolvedModuleWithFailedLookupLocations | undefined {
const cache = resolvedModuleNames.get(resolutionHost.toPath(containingFile));
return cache && cache.get(moduleName);
if (!cache) return undefined;
return cache.get(moduleName, resolutionMode);
}

function isNodeModulesAtTypesDirectory(dirPath: Path) {
Expand Down Expand Up @@ -751,7 +757,7 @@ namespace ts {
}

function removeResolutionsOfFileFromCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
cache: ESMap<string, ESMap<string, T>>,
cache: ESMap<string, ModeAwareCache<T>>,
filePath: Path,
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
) {
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,9 @@ namespace ts {
const moduleResolutionCache = !compilerHost.resolveModuleNames ? createModuleResolutionCache(currentDirectory, getCanonicalFileName) : undefined;
const typeReferenceDirectiveResolutionCache = !compilerHost.resolveTypeReferenceDirectives ? createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache()) : undefined;
if (!compilerHost.resolveModuleNames) {
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, state.projectCompilerOptions, compilerHost, moduleResolutionCache, redirectedReference).resolvedModule!;
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference) =>
loadWithLocalCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader);
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!;
compilerHost.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, _options, containingSourceFile) =>
loadWithModeAwareCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), Debug.checkDefined(containingSourceFile), containingFile, redirectedReference, loader);
compilerHost.getModuleResolutionCache = () => moduleResolutionCache;
}
if (!compilerHost.resolveTypeReferenceDirectives) {
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3607,8 +3607,8 @@ namespace ts {
// Stores a mapping 'external module reference text' -> 'resolved file name' | undefined
// It is used to resolve module names in the checker.
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
/* @internal */ resolvedModules?: ESMap<string, ResolvedModuleFull | undefined>;
/* @internal */ resolvedTypeReferenceDirectiveNames: ESMap<string, ResolvedTypeReferenceDirective | undefined>;
/* @internal */ resolvedModules?: ModeAwareCache<ResolvedModuleFull | undefined>;
/* @internal */ resolvedTypeReferenceDirectiveNames: ModeAwareCache<ResolvedTypeReferenceDirective | undefined>;
/* @internal */ imports: readonly StringLiteralLike[];
// Identifier only if `declare global`
/* @internal */ moduleAugmentations: readonly (StringLiteral | Identifier)[];
Expand Down Expand Up @@ -3990,7 +3990,7 @@ namespace ts {
/* @internal */ getFileIncludeReasons(): MultiMap<Path, FileIncludeReason>;
/* @internal */ useCaseSensitiveFileNames(): boolean;

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

getProjectReferences(): readonly ProjectReference[] | undefined;
getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
Expand Down Expand Up @@ -6614,7 +6614,7 @@ namespace ts {
* If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just
* 'throw new Error("NotImplemented")'
*/
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[];
/**
* 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
*/
Expand Down

0 comments on commit cea73a2

Please sign in to comment.