Merge pull request #27560 from Microsoft/transitiveReferences

Resolve modules, type reference directives in context of referenced file
This commit is contained in:
Sheetal Nandi authored and GitHub committed 2018-10-18 13:03:10 -07:00
commit 91bb32ade4
25 files changed
+1156 -385

No files matched your search

+3
View File
@@ -1293,6 +1293,9 @@ namespace ts {
const result = parseJsonText(configFileName, configFileText);
const cwd = host.getCurrentDirectory();
result.path = toPath(configFileName, cwd, createGetCanonicalFileName(host.useCaseSensitiveFileNames));
result.resolvedPath = result.path;
result.originalFileName = result.fileName;
return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
}
+4
View File
@@ -3763,6 +3763,10 @@
"category": "Message",
"code": 6214
},
"Using compiler options of project reference redirect '{0}'.": {
"category": "Message",
"code": 6215
},
"Projects to reference": {
"category": "Message",
+88 -38
View File
@@ -258,8 +258,11 @@ namespace ts {
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
* is assumed to be the same as root directory of the project.
*/
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(options, host);
if (redirectedReference) {
options = redirectedReference.commandLine.options;
}
const failedLookupLocations: string[] = [];
const moduleResolutionState: ModuleResolutionState = { compilerOptions: options, host, traceEnabled, failedLookupLocations };
@@ -281,6 +284,9 @@ namespace ts {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots);
}
}
if (redirectedReference) {
trace(host, Diagnostics.Using_compiler_options_of_project_reference_redirect_0, redirectedReference.sourceFile.fileName);
}
}
let resolved = primaryLookup();
@@ -339,7 +345,7 @@ namespace ts {
}
let result: SearchResult<Resolved> | undefined;
if (!isExternalModuleNameRelative(typeReferenceDirectiveName)) {
result = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined);
result = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined, /*redirectedReference*/ undefined);
}
else {
const { path: candidate } = normalizePathAndParts(combinePaths(initialLocationForSecondaryLookup, typeReferenceDirectiveName));
@@ -409,7 +415,7 @@ namespace ts {
* This assumes that any module id will have the same resolution for sibling files located in the same folder.
*/
export interface ModuleResolutionCache extends NonRelativeModuleNameResolutionCache {
getOrCreateCacheForDirectory(directoryName: string): Map<ResolvedModuleWithFailedLookupLocations>;
getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map<ResolvedModuleWithFailedLookupLocations>;
}
/**
@@ -417,7 +423,7 @@ namespace ts {
* We support only non-relative module names because resolution of relative module names is usually more deterministic and thus less expensive.
*/
export interface NonRelativeModuleNameResolutionCache {
getOrCreateCacheForModuleName(nonRelativeModuleName: string): PerModuleNameCache;
getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache;
}
export interface PerModuleNameCache {
@@ -427,40 +433,78 @@ namespace ts {
export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): ModuleResolutionCache {
return createModuleResolutionCacheWithMaps(
createMap<Map<ResolvedModuleWithFailedLookupLocations>>(),
createMap<PerModuleNameCache>(),
createCacheWithRedirects(),
createCacheWithRedirects(),
currentDirectory,
getCanonicalFileName
);
}
/*@internal*/
export interface CacheWithRedirects<T> {
ownMap: Map<T>;
redirectsMap: Map<Map<T>>;
getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map<T>;
clear(): void;
}
/*@internal*/
export function createCacheWithRedirects<T>(): CacheWithRedirects<T> {
const ownMap: Map<T> = createMap();
const redirectsMap: Map<Map<T>> = createMap();
return {
ownMap,
redirectsMap,
getOrCreateMapOfCacheRedirects,
clear
};
function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined) {
if (!redirectedReference) {
return ownMap;
}
const path = redirectedReference.sourceFile.path;
let redirects = redirectsMap.get(path);
if (!redirects) {
redirects = createMap();
redirectsMap.set(path, redirects);
}
return redirects;
}
function clear() {
ownMap.clear();
redirectsMap.clear();
}
}
/*@internal*/
export function createModuleResolutionCacheWithMaps(
directoryToModuleNameMap: Map<Map<ResolvedModuleWithFailedLookupLocations>>,
moduleNameToDirectoryMap: Map<PerModuleNameCache>,
directoryToModuleNameMap: CacheWithRedirects<Map<ResolvedModuleWithFailedLookupLocations>>,
moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>,
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName): ModuleResolutionCache {
return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName };
function getOrCreateCacheForDirectory(directoryName: string) {
function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) {
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
let perFolderCache = directoryToModuleNameMap.get(path);
if (!perFolderCache) {
perFolderCache = createMap<ResolvedModuleWithFailedLookupLocations>();
directoryToModuleNameMap.set(path, perFolderCache);
}
return perFolderCache;
return getOrCreateCache<Map<ResolvedModuleWithFailedLookupLocations>>(directoryToModuleNameMap, redirectedReference, path, createMap);
}
function getOrCreateCacheForModuleName(nonRelativeModuleName: string): PerModuleNameCache {
function getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache {
Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName));
let perModuleNameCache = moduleNameToDirectoryMap.get(nonRelativeModuleName);
if (!perModuleNameCache) {
perModuleNameCache = createPerModuleNameCache();
moduleNameToDirectoryMap.set(nonRelativeModuleName, perModuleNameCache);
return getOrCreateCache(moduleNameToDirectoryMap, redirectedReference, nonRelativeModuleName, createPerModuleNameCache);
}
function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirectedReference: ResolvedProjectReference | undefined, key: string, create: () => T): T {
const cache = cacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
let result = cache.get(key);
if (!result) {
result = create();
cache.set(key, result);
}
return perModuleNameCache;
return result;
}
function createPerModuleNameCache(): PerModuleNameCache {
@@ -542,13 +586,19 @@ namespace ts {
return perFolderCache && perFolderCache.get(moduleName);
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (redirectedReference) {
compilerOptions = redirectedReference.commandLine.options;
}
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
if (redirectedReference) {
trace(host, Diagnostics.Using_compiler_options_of_project_reference_redirect_0, redirectedReference.sourceFile.fileName);
}
}
const containingDirectory = getDirectoryPath(containingFile);
const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory);
const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory, redirectedReference);
let result = perFolderCache && perFolderCache.get(moduleName);
if (result) {
@@ -572,10 +622,10 @@ namespace ts {
switch (moduleResolution) {
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache);
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache);
result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference);
break;
default:
return Debug.fail(`Unexpected moduleResolution: ${moduleResolution}`);
@@ -585,7 +635,7 @@ namespace ts {
perFolderCache.set(moduleName, result);
if (!isExternalModuleNameRelative(moduleName)) {
// put result in per-module name cache
cache!.getOrCreateCacheForModuleName(moduleName).set(containingDirectory, result);
cache!.getOrCreateCacheForModuleName(moduleName, redirectedReference).set(containingDirectory, result);
}
}
}
@@ -805,14 +855,14 @@ namespace ts {
}
function tryResolveJSModuleWorker(moduleName: string, initialDir: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
return nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
return nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*redirectedReference*/ undefined, /*jsOnly*/ true);
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, /*jsOnly*/ false);
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, redirectedReference, /*jsOnly*/ false);
}
function nodeModuleNameResolverWorker(moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, jsOnly: boolean): ResolvedModuleWithFailedLookupLocations {
function nodeModuleNameResolverWorker(moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined, jsOnly: boolean): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
@@ -840,7 +890,7 @@ namespace ts {
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
}
const resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache);
const resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
if (!resolved) return undefined;
let resolvedValue = resolved.value;
@@ -1178,17 +1228,17 @@ namespace ts {
return idx === -1 ? { packageName: moduleName, rest: "" } : { packageName: moduleName.slice(0, idx), rest: moduleName.slice(idx + 1) };
}
function loadModuleFromNearestNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache | undefined): SearchResult<Resolved> {
return loadModuleFromNearestNodeModulesDirectoryWorker(extensions, moduleName, directory, state, /*typesScopeOnly*/ false, cache);
function loadModuleFromNearestNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
return loadModuleFromNearestNodeModulesDirectoryWorker(extensions, moduleName, directory, state, /*typesScopeOnly*/ false, cache, redirectedReference);
}
function loadModuleFromNearestNodeModulesDirectoryTypesScope(moduleName: string, directory: string, state: ModuleResolutionState): SearchResult<Resolved> {
// Extensions parameter here doesn't actually matter, because typesOnly ensures we're just doing @types lookup, which is always DtsOnly.
return loadModuleFromNearestNodeModulesDirectoryWorker(Extensions.DtsOnly, moduleName, directory, state, /*typesScopeOnly*/ true, /*cache*/ undefined);
return loadModuleFromNearestNodeModulesDirectoryWorker(Extensions.DtsOnly, moduleName, directory, state, /*typesScopeOnly*/ true, /*cache*/ undefined, /*redirectedReference*/ undefined);
}
function loadModuleFromNearestNodeModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: NonRelativeModuleNameResolutionCache | undefined): SearchResult<Resolved> {
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName);
function loadModuleFromNearestNodeModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference);
return forEachAncestorDirectory(normalizeSlashes(directory), ancestorDirectory => {
if (getBaseFileName(ancestorDirectory) !== "node_modules") {
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, ancestorDirectory, state);
@@ -1356,7 +1406,7 @@ namespace ts {
}
}
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache): ResolvedModuleWithFailedLookupLocations {
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations };
@@ -1373,7 +1423,7 @@ namespace ts {
}
if (!isExternalModuleNameRelative(moduleName)) {
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName);
const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference);
// Climb up parent directories looking for a module.
const resolved = forEachAncestorDirectory(containingDirectory, directory => {
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, directory, state);
+245 -135
View File
@@ -406,7 +406,7 @@ namespace ts {
}
}
function loadWithLocalCache<T>(names: string[], containingFile: string, loader: (name: string, containingFile: string) => T): T[] {
function loadWithLocalCache<T>(names: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, loader: (name: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
if (names.length === 0) {
return [];
}
@@ -418,7 +418,7 @@ namespace ts {
result = cache.get(name)!;
}
else {
cache.set(name, result = loader(name, containingFile));
cache.set(name, result = loader(name, containingFile, redirectedReference));
}
resolutions.push(result);
}
@@ -454,6 +454,8 @@ namespace ts {
return false;
}
let seenResolvedRefs: ResolvedProjectReference[] | undefined;
// If project references dont match
if (!arrayIsEqualTo(program.getProjectReferences(), projectReferences, projectReferenceUptoDate)) {
return false;
@@ -485,7 +487,7 @@ namespace ts {
function sourceFileNotUptoDate(sourceFile: SourceFile) {
return !sourceFileVersionUptoDate(sourceFile) ||
hasInvalidatedResolution(sourceFile.resolvedPath);
hasInvalidatedResolution(sourceFile.path);
}
function sourceFileVersionUptoDate(sourceFile: SourceFile) {
@@ -496,11 +498,29 @@ namespace ts {
if (!projectReferenceIsEqualTo(oldRef, newRef)) {
return false;
}
const oldResolvedRef = program!.getResolvedProjectReferences()![index];
return resolvedProjectReferenceUptoDate(program!.getResolvedProjectReferences()![index], oldRef);
}
function resolvedProjectReferenceUptoDate(oldResolvedRef: ResolvedProjectReference | undefined, oldRef: ProjectReference): boolean {
if (oldResolvedRef) {
if (contains(seenResolvedRefs, oldResolvedRef)) {
// Assume true
return true;
}
// If sourceFile for the oldResolvedRef existed, check the version for uptodate
return sourceFileVersionUptoDate(oldResolvedRef.sourceFile);
if (!sourceFileVersionUptoDate(oldResolvedRef.sourceFile)) {
return false;
}
// Add to seen before checking the referenced paths of this config file
(seenResolvedRefs || (seenResolvedRefs = [])).push(oldResolvedRef);
// If child project references are upto date, this project reference is uptodate
return !forEach(oldResolvedRef.references, (childResolvedRef, index) =>
!resolvedProjectReferenceUptoDate(childResolvedRef, oldResolvedRef.commandLine.projectReferences![index]));
}
// In old program, not able to resolve project reference path,
// so if config file doesnt exist, it is uptodate.
return !fileExists(resolveProjectReferencePath(oldRef));
@@ -615,13 +635,12 @@ namespace ts {
// Map storing if there is emit blocking diagnostics for given input
const hasEmitBlockingDiagnostics = createMap<boolean>();
let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | null | undefined;
let _referencesArrayLiteralSyntax: ArrayLiteralExpression | null | undefined;
let moduleResolutionCache: ModuleResolutionCache | undefined;
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[]) => ResolvedModuleFull[];
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[];
const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
if (host.resolveModuleNames) {
resolveModuleNamesWorker = (moduleNames, containingFile, reusedNames) => host.resolveModuleNames!(Debug.assertEachDefined(moduleNames), containingFile, reusedNames).map(resolved => {
resolveModuleNamesWorker = (moduleNames, containingFile, reusedNames, redirectedReference) => host.resolveModuleNames!(Debug.assertEachDefined(moduleNames), containingFile, reusedNames, redirectedReference).map(resolved => {
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
return resolved as ResolvedModuleFull;
@@ -633,17 +652,17 @@ namespace ts {
}
else {
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache).resolvedModule!; // TODO: GH#18217
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache<ResolvedModuleFull>(Debug.assertEachDefined(moduleNames), containingFile, loader);
const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache, redirectedReference).resolvedModule!; // TODO: GH#18217
resolveModuleNamesWorker = (moduleNames, containingFile, _reusedNames, redirectedReference) => loadWithLocalCache<ResolvedModuleFull>(Debug.assertEachDefined(moduleNames), containingFile, redirectedReference, loader);
}
let resolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[];
let resolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference) => ResolvedTypeReferenceDirective[];
if (host.resolveTypeReferenceDirectives) {
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives!(Debug.assertEachDefined(typeDirectiveNames), containingFile);
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference) => host.resolveTypeReferenceDirectives!(Debug.assertEachDefined(typeDirectiveNames), containingFile, redirectedReference);
}
else {
const loader = (typesRef: string, containingFile: string) => resolveTypeReferenceDirective(typesRef, containingFile, options, host).resolvedTypeReferenceDirective!; // TODO: GH#18217
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache<ResolvedTypeReferenceDirective>(Debug.assertEachDefined(typeReferenceDirectiveNames), containingFile, loader);
const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveTypeReferenceDirective(typesRef, containingFile, options, host, redirectedReference).resolvedTypeReferenceDirective!; // TODO: GH#18217
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile, redirectedReference) => loadWithLocalCache<ResolvedTypeReferenceDirective>(Debug.assertEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, loader);
}
// Map from a stringified PackageId to the source file with that id.
@@ -662,8 +681,8 @@ namespace ts {
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap<SourceFile>() : undefined;
// A parallel array to projectReferences storing the results of reading in the referenced tsconfig files
let resolvedProjectReferences: (ResolvedProjectReference | undefined)[] | undefined = projectReferences ? [] : undefined;
let projectReferenceRedirects: ParsedCommandLine[] | undefined;
let resolvedProjectReferences: ReadonlyArray<ResolvedProjectReference | undefined> | undefined;
let projectReferenceRedirects: Map<ResolvedProjectReference | false> | undefined;
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
const structuralIsReused = tryReuseStructureFromOldProgram();
@@ -672,16 +691,16 @@ namespace ts {
processingOtherFiles = [];
if (projectReferences) {
for (const ref of projectReferences) {
const parsedRef = parseProjectReferenceConfigFile(ref);
resolvedProjectReferences!.push(parsedRef);
if (!resolvedProjectReferences) {
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
}
for (const parsedRef of resolvedProjectReferences) {
if (parsedRef) {
const out = parsedRef.commandLine.options.outFile || parsedRef.commandLine.options.out;
if (out) {
const dtsOutfile = changeExtension(out, ".d.ts");
processSourceFile(dtsOutfile, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
}
addProjectReferenceRedirects(parsedRef.commandLine);
}
}
}
@@ -732,10 +751,18 @@ namespace ts {
if (oldProgram && host.onReleaseOldSourceFile) {
const oldSourceFiles = oldProgram.getSourceFiles();
for (const oldSourceFile of oldSourceFiles) {
if (!getSourceFile(oldSourceFile.path) || shouldCreateNewSourceFile) {
host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions());
const newFile = getSourceFileByPath(oldSourceFile.resolvedPath);
if (shouldCreateNewSourceFile || !newFile ||
// old file wasnt redirect but new file is
(oldSourceFile.resolvedPath === oldSourceFile.path && newFile.resolvedPath !== oldSourceFile.path)) {
host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions(), !!getSourceFileByPath(oldSourceFile.path));
}
}
oldProgram.forEachResolvedProjectReference((resolvedProjectReference, resolvedProjectReferencePath) => {
if (resolvedProjectReference && !getResolvedProjectReferenceByPath(resolvedProjectReferencePath)) {
host.onReleaseOldSourceFile!(resolvedProjectReference.sourceFile, oldProgram!.getCompilerOptions(), /*hasSourceFileByPath*/ false);
}
});
}
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
@@ -778,7 +805,10 @@ namespace ts {
getResolvedModuleWithFailedLookupLocationsFromCache,
getProjectReferences,
getResolvedProjectReferences,
getProjectReferenceRedirect
getProjectReferenceRedirect,
getResolvedProjectReferenceToRedirect,
getResolvedProjectReferenceByPath,
forEachResolvedProjectReference
};
verifyCompilerOptions();
@@ -861,7 +891,7 @@ namespace ts {
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
return resolveModuleNamesWorker(moduleNames, containingFile);
return resolveModuleNamesWorker(moduleNames, containingFile, /*reusedNames*/ undefined, getResolvedProjectReferenceToRedirect(file.originalFileName));
}
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
@@ -941,7 +971,7 @@ namespace ts {
}
const resolutions = unknownModuleNames && unknownModuleNames.length
? resolveModuleNamesWorker(unknownModuleNames, containingFile, reusedNames)
? resolveModuleNamesWorker(unknownModuleNames, containingFile, reusedNames, getResolvedProjectReferenceToRedirect(file.originalFileName))
: emptyArray;
// Combine results of resolutions and predicted results
@@ -1001,6 +1031,30 @@ namespace ts {
}
}
function canReuseProjectReferences(): boolean {
return !forEachProjectReference(
oldProgram!.getProjectReferences(),
oldProgram!.getResolvedProjectReferences(),
(oldResolvedRef, index, parent) => {
const newRef = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
const newResolvedRef = parseProjectReferenceConfigFile(newRef);
if (oldResolvedRef) {
// Resolved project reference has gone missing or changed
return !newResolvedRef || newResolvedRef.sourceFile !== oldResolvedRef.sourceFile;
}
else {
// A previously-unresolved reference may be resolved now
return newResolvedRef !== undefined;
}
},
(oldProjectReferences, parent) => {
// If array of references is changed, we cant resue old program
const newReferences = parent ? getResolvedProjectReferenceByPath(parent.sourceFile.path)!.commandLine.projectReferences : projectReferences;
return !arrayIsEqualTo(oldProjectReferences, newReferences, projectReferenceIsEqualTo);
}
);
}
function tryReuseStructureFromOldProgram(): StructureIsReused {
if (!oldProgram) {
return StructureIsReused.Not;
@@ -1026,43 +1080,15 @@ namespace ts {
}
// Check if any referenced project tsconfig files are different
// If array of references is changed, we cant resue old program
const oldProjectReferences = oldProgram.getProjectReferences();
if (!arrayIsEqualTo(oldProjectReferences!, projectReferences, projectReferenceIsEqualTo)) {
if (!canReuseProjectReferences()) {
return oldProgram.structureIsReused = StructureIsReused.Not;
}
// Check the json files for the project references
const oldRefs = oldProgram.getResolvedProjectReferences();
if (projectReferences) {
// Resolved project referenced should be array if projectReferences provided are array
Debug.assert(!!oldRefs);
for (let i = 0; i < projectReferences.length; i++) {
const oldRef = oldRefs![i];
const newRef = parseProjectReferenceConfigFile(projectReferences[i]);
if (oldRef) {
if (!newRef || newRef.sourceFile !== oldRef.sourceFile) {
// Resolved project reference has gone missing or changed
return oldProgram.structureIsReused = StructureIsReused.Not;
}
}
else {
// A previously-unresolved reference may be resolved now
if (newRef !== undefined) {
return oldProgram.structureIsReused = StructureIsReused.Not;
}
}
}
}
else {
// Resolved project referenced should be undefined if projectReferences is undefined
Debug.assert(!oldRefs);
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
}
// check if program source files has changed in the way that can affect structure of the program
const newSourceFiles: SourceFile[] = [];
const filePaths: Path[] = [];
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
oldProgram.structureIsReused = StructureIsReused.Completely;
@@ -1115,7 +1141,6 @@ namespace ts {
newSourceFile.originalFileName = oldSourceFile.originalFileName;
newSourceFile.resolvedPath = oldSourceFile.resolvedPath;
newSourceFile.fileName = oldSourceFile.fileName;
filePaths.push(newSourceFile.path);
const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
if (packageName !== undefined) {
@@ -1209,7 +1234,7 @@ namespace ts {
if (resolveTypeReferenceDirectiveNamesWorker) {
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, ref => ref.fileName.toLocaleLowerCase());
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFilePath);
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFilePath, getResolvedProjectReferenceToRedirect(newSourceFile.originalFileName));
// ensure that types resolutions are still correct
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
if (resolutionsChanged) {
@@ -1233,11 +1258,12 @@ namespace ts {
missingFilePaths = oldProgram.getMissingFilePaths();
// update fileName -> file mapping
for (let i = 0; i < newSourceFiles.length; i++) {
filesByName.set(filePaths[i], newSourceFiles[i]);
for (const newSourceFile of newSourceFiles) {
const filePath = newSourceFile.path;
addFileToFilesByName(newSourceFile, filePath, newSourceFile.resolvedPath);
// Set the file as found during node modules search if it was found that way in old progra,
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(filePaths[i])!)) {
sourceFilesFoundSearchingNodeModules.set(filePaths[i], true);
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(filePath)!)) {
sourceFilesFoundSearchingNodeModules.set(filePath, true);
}
}
@@ -1248,14 +1274,6 @@ namespace ts {
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
}
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
resolvedProjectReferences = oldProgram.getResolvedProjectReferences();
if (resolvedProjectReferences) {
resolvedProjectReferences.forEach(ref => {
if (ref) {
addProjectReferenceRedirects(ref.commandLine);
}
});
}
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
redirectTargetsMap = oldProgram.redirectTargetsMap;
@@ -1810,11 +1828,22 @@ namespace ts {
fileProcessingDiagnostics.getGlobalDiagnostics(),
concatenate(
programDiagnostics.getGlobalDiagnostics(),
options.configFile ? programDiagnostics.getDiagnostics(options.configFile.fileName) : []
getOptionsDiagnosticsOfConfigFile()
)
));
}
function getOptionsDiagnosticsOfConfigFile() {
if (!options.configFile) { return emptyArray; }
let diagnostics = programDiagnostics.getDiagnostics(options.configFile.fileName);
forEachResolvedProjectReference(resolvedRef => {
if (resolvedRef) {
diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName));
}
});
return diagnostics;
}
function getGlobalDiagnostics(): Diagnostic[] {
return sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice());
}
@@ -2089,7 +2118,7 @@ namespace ts {
return file;
}
let redirectedPath: string | undefined;
let redirectedPath: Path | undefined;
if (refFile) {
const redirect = getProjectReferenceRedirect(fileName);
if (redirect) {
@@ -2123,7 +2152,7 @@ namespace ts {
// Instead of creating a duplicate, just redirect to the existing one.
const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName); // TODO: GH#18217
redirectTargetsMap.add(fileFromPackageId.path, fileName);
filesByName.set(path, dupFile);
addFileToFilesByName(dupFile, path, redirectedPath);
sourceFileToPackageName.set(path, packageId.name);
processingOtherFiles!.push(dupFile);
return dupFile;
@@ -2134,11 +2163,7 @@ namespace ts {
sourceFileToPackageName.set(path, packageId.name);
}
}
filesByName.set(path, file);
if (redirectedPath) {
filesByName.set(redirectedPath, file);
}
addFileToFilesByName(file, path, redirectedPath);
if (file) {
sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
@@ -2181,27 +2206,109 @@ namespace ts {
return file;
}
function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
filesByName.set(path, file);
if (redirectedPath) {
filesByName.set(redirectedPath, file);
}
}
function getProjectReferenceRedirect(fileName: string): string | undefined {
// Ignore dts or any of the non ts files
if (!projectReferenceRedirects || fileExtensionIs(fileName, Extension.Dts) || !fileExtensionIsOneOf(fileName, supportedTSExtensions)) {
if (!resolvedProjectReferences || !resolvedProjectReferences.length || fileExtensionIs(fileName, Extension.Dts) || !fileExtensionIsOneOf(fileName, supportedTSExtensions)) {
return undefined;
}
// If this file is produced by a referenced project, we need to rewrite it to
// look in the output folder of the referenced project rather than the input
return forEach(projectReferenceRedirects, referencedProject => {
const referencedProject = getResolvedProjectReferenceToRedirect(fileName);
if (!referencedProject) {
return undefined;
}
const out = referencedProject.commandLine.options.outFile || referencedProject.commandLine.options.out;
return out ?
changeExtension(out, Extension.Dts) :
getOutputDeclarationFileName(fileName, referencedProject.commandLine);
}
/**
* Get the referenced project if the file is input file from that reference project
*/
function getResolvedProjectReferenceToRedirect(fileName: string) {
return forEachResolvedProjectReference((referencedProject, referenceProjectPath) => {
// not input file from the referenced project, ignore
if (!contains(referencedProject.fileNames, fileName, isSameFile)) {
if (!referencedProject ||
toPath(options.configFilePath!) === referenceProjectPath ||
!contains(referencedProject.commandLine.fileNames, fileName, isSameFile)) {
return undefined;
}
const out = referencedProject.options.outFile || referencedProject.options.out;
return out ?
changeExtension(out, Extension.Dts) :
getOutputDeclarationFileName(fileName, referencedProject);
return referencedProject;
});
}
function forEachResolvedProjectReference<T>(
cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined
): T | undefined {
return forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, index, parent) => {
const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
const resolvedRefPath = toPath(resolveProjectReferencePath(ref));
return cb(resolvedRef, resolvedRefPath);
});
}
function forEachProjectReference<T>(
projectReferences: ReadonlyArray<ProjectReference> | undefined,
resolvedProjectReferences: ReadonlyArray<ResolvedProjectReference | undefined> | undefined,
cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, index: number, parent: ResolvedProjectReference | undefined) => T | undefined,
cbRef?: (projectReferences: ReadonlyArray<ProjectReference> | undefined, parent: ResolvedProjectReference | undefined) => T | undefined
): T | undefined {
let seenResolvedRefs: ResolvedProjectReference[] | undefined;
return worker(projectReferences, resolvedProjectReferences, /*parent*/ undefined, cbResolvedRef, cbRef);
function worker(
projectReferences: ReadonlyArray<ProjectReference> | undefined,
resolvedProjectReferences: ReadonlyArray<ResolvedProjectReference | undefined> | undefined,
parent: ResolvedProjectReference | undefined,
cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, index: number, parent: ResolvedProjectReference | undefined) => T | undefined,
cbRef?: (projectReferences: ReadonlyArray<ProjectReference> | undefined, parent: ResolvedProjectReference | undefined) => T | undefined,
): T | undefined {
// Visit project references first
if (cbRef) {
const result = cbRef(projectReferences, parent);
if (result) { return result; }
}
return forEach(resolvedProjectReferences, (resolvedRef, index) => {
if (contains(seenResolvedRefs, resolvedRef)) {
// ignore recursives
return undefined;
}
const result = cbResolvedRef(resolvedRef, index, parent);
if (result) {
return result;
}
if (!resolvedRef) return undefined;
(seenResolvedRefs || (seenResolvedRefs = [])).push(resolvedRef);
return worker(resolvedRef.commandLine.projectReferences, resolvedRef.references, resolvedRef, cbResolvedRef, cbRef);
});
}
}
function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined {
if (!projectReferenceRedirects) {
return undefined;
}
return projectReferenceRedirects.get(projectReferencePath) || undefined;
}
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
forEach(file.referencedFiles, ref => {
const referencedFileName = resolveTripleslashReference(ref.fileName, file.originalFileName);
@@ -2216,7 +2323,7 @@ namespace ts {
return;
}
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.originalFileName);
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.originalFileName, getResolvedProjectReferenceToRedirect(file.originalFileName));
for (let i = 0; i < typeDirectives.length; i++) {
const ref = file.typeReferenceDirectives[i];
@@ -2389,22 +2496,37 @@ namespace ts {
return allFilesBelongToPath;
}
function parseProjectReferenceConfigFile(ref: ProjectReference): { commandLine: ParsedCommandLine, sourceFile: SourceFile } | undefined {
function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined {
if (!projectReferenceRedirects) {
projectReferenceRedirects = createMap<ResolvedProjectReference | false>();
}
// The actual filename (i.e. add "/tsconfig.json" if necessary)
const refPath = resolveProjectReferencePath(ref);
const sourceFilePath = toPath(refPath);
const fromCache = projectReferenceRedirects.get(sourceFilePath);
if (fromCache !== undefined) {
return fromCache || undefined;
}
// An absolute path pointing to the containing directory of the config file
const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory());
const sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined;
addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
if (sourceFile === undefined) {
projectReferenceRedirects.set(sourceFilePath, false);
return undefined;
}
sourceFile.path = toPath(refPath);
sourceFile.path = sourceFilePath;
sourceFile.resolvedPath = sourceFilePath;
sourceFile.originalFileName = refPath;
const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
return { commandLine, sourceFile };
}
function addProjectReferenceRedirects(referencedProject: ParsedCommandLine) {
(projectReferenceRedirects || (projectReferenceRedirects = [])).push(referencedProject);
const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile };
projectReferenceRedirects.set(sourceFilePath, resolvedRef);
if (commandLine.projectReferences) {
resolvedRef.references = commandLine.projectReferences.map(parseProjectReferenceConfigFile);
}
return resolvedRef;
}
function verifyCompilerOptions() {
@@ -2449,30 +2571,7 @@ namespace ts {
}
}
if (projectReferences) {
for (let i = 0; i < projectReferences.length; i++) {
const ref = projectReferences[i];
const resolvedRefOpts = resolvedProjectReferences![i] && resolvedProjectReferences![i]!.commandLine.options;
if (resolvedRefOpts === undefined) {
createDiagnosticForReference(i, Diagnostics.File_0_does_not_exist, ref.path);
continue;
}
if (!resolvedRefOpts.composite) {
createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
}
if (ref.prepend) {
const out = resolvedRefOpts.outFile || resolvedRefOpts.out;
if (out) {
if (!host.fileExists(out)) {
createDiagnosticForReference(i, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path);
}
}
else {
createDiagnosticForReference(i, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path);
}
}
}
}
verifyProjectReferences();
// List of collected files is complete; validate exhautiveness if this is a project with a file list
if (options.composite) {
@@ -2690,6 +2789,32 @@ namespace ts {
}
}
function verifyProjectReferences() {
forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, index, parent) => {
const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
const parentFile = parent && parent.sourceFile as JsonSourceFile;
if (!resolvedRef) {
createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path);
return;
}
const options = resolvedRef.commandLine.options;
if (!options.composite) {
createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
}
if (ref.prepend) {
const out = options.outFile || options.out;
if (out) {
if (!host.fileExists(out)) {
createDiagnosticForReference(parentFile, index, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path);
}
}
else {
createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path);
}
}
});
}
function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, arg0: string | number, arg1: string | number, arg2?: string | number) {
let needCompilerDiagnostic = true;
const pathsSyntax = getOptionPathsSyntax();
@@ -2746,10 +2871,11 @@ namespace ts {
createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
}
function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
const referencesSyntax = getProjectReferencesSyntax();
function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile || options.configFile, "references"),
property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
if (referencesSyntax && referencesSyntax.elements.length > index) {
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
programDiagnostics.add(createDiagnosticForNodeInSourceFile(sourceFile || options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
}
else {
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
@@ -2766,22 +2892,6 @@ namespace ts {
}
}
function getProjectReferencesSyntax(): ArrayLiteralExpression | null {
if (_referencesArrayLiteralSyntax === undefined) {
_referencesArrayLiteralSyntax = null; // tslint:disable-line:no-null-keyword
if (options.configFile) {
const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile)!; // TODO: GH#18217
for (const prop of getPropertyAssignment(jsonObjectLiteral, "references")) {
if (isArrayLiteralExpression(prop.initializer)) {
_referencesArrayLiteralSyntax = prop.initializer;
break;
}
}
}
}
return _referencesArrayLiteralSyntax;
}
function getCompilerOptionsObjectLiteralSyntax() {
if (_compilerOptionsObjectLiteralSyntax === undefined) {
_compilerOptionsObjectLiteralSyntax = null; // tslint:disable-line:no-null-keyword
+43 -17
View File
@@ -5,12 +5,13 @@ namespace ts {
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[];
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): ResolvedModuleFull[];
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): CachedResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirective[];
invalidateResolutionOfFile(filePath: Path): void;
removeResolutionsOfFile(filePath: Path): void;
removeResolutionsFromProjectReferenceRedirects(filePath: Path): void;
setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map<ReadonlyArray<string>>): void;
createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution;
@@ -90,17 +91,17 @@ namespace ts {
// The key in the map is source file's path.
// The values are Map of resolutions with key being name lookedup.
const resolvedModuleNames = createMap<Map<CachedResolvedModuleWithFailedLookupLocations>>();
const perDirectoryResolvedModuleNames = createMap<Map<CachedResolvedModuleWithFailedLookupLocations>>();
const nonRelaticeModuleNameCache = createMap<PerModuleNameCache>();
const perDirectoryResolvedModuleNames: CacheWithRedirects<Map<CachedResolvedModuleWithFailedLookupLocations>> = createCacheWithRedirects();
const nonRelativeModuleNameCache: CacheWithRedirects<PerModuleNameCache> = createCacheWithRedirects();
const moduleResolutionCache = createModuleResolutionCacheWithMaps(
perDirectoryResolvedModuleNames,
nonRelaticeModuleNameCache,
nonRelativeModuleNameCache,
getCurrentDirectory(),
resolutionHost.getCanonicalFileName
);
const resolvedTypeReferenceDirectives = createMap<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const perDirectoryResolvedTypeReferenceDirectives = createMap<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects<Map<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations>> = createCacheWithRedirects();
/**
* These are the extensions that failed lookup files will have by default,
@@ -128,6 +129,7 @@ namespace ts {
resolveModuleNames,
getResolvedModuleWithFailedLookupLocationsFromCache,
resolveTypeReferenceDirectives,
removeResolutionsFromProjectReferenceRedirects,
removeResolutionsOfFile,
invalidateResolutionOfFile,
setFilesWithInvalidatedNonRelativeUnresolvedImports,
@@ -199,7 +201,7 @@ namespace ts {
function clearPerDirectoryResolutions() {
perDirectoryResolvedModuleNames.clear();
nonRelaticeModuleNameCache.clear();
nonRelativeModuleNameCache.clear();
perDirectoryResolvedTypeReferenceDirectives.clear();
nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
nonRelativeExternalModuleResolutions.clear();
@@ -217,8 +219,8 @@ namespace ts {
});
}
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): CachedResolvedModuleWithFailedLookupLocations {
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache);
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): CachedResolvedModuleWithFailedLookupLocations {
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference);
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
if (!resolutionHost.getGlobalCache) {
return primaryResult;
@@ -242,9 +244,10 @@ namespace ts {
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>(
names: string[],
containingFile: string,
redirectedReference: ResolvedProjectReference | undefined,
cache: Map<Map<T>>,
perDirectoryCache: Map<Map<T>>,
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
perDirectoryCacheWithRedirects: CacheWithRedirects<Map<T>>,
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T,
getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName<T, R>,
reusedNames: string[] | undefined,
logChanges: boolean): R[] {
@@ -252,6 +255,7 @@ namespace ts {
const path = resolutionHost.toPath(containingFile);
const resolutionsInFile = cache.get(path) || cache.set(path, createMap()).get(path)!;
const dirPath = getDirectoryPath(path);
const perDirectoryCache = perDirectoryCacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
let perDirectoryResolution = perDirectoryCache.get(dirPath);
if (!perDirectoryResolution) {
perDirectoryResolution = createMap();
@@ -260,12 +264,20 @@ namespace ts {
const resolvedModules: R[] = [];
const compilerOptions = resolutionHost.getCompilationSettings();
const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
// All the resolutions in this file are invalidated if this file wasnt resolved using same redirect
const program = resolutionHost.getCurrentProgram();
const oldRedirect = program && program.getResolvedProjectReferenceToRedirect(containingFile);
const unmatchedRedirects = oldRedirect ?
!redirectedReference || redirectedReference.sourceFile.path !== oldRedirect.sourceFile.path :
!!redirectedReference;
const seenNamesInFile = createMap<true>();
for (const name of names) {
let resolution = resolutionsInFile.get(name);
// Resolution is valid if it is present and not invalidated
if (!seenNamesInFile.has(name) &&
allFilesHaveInvalidatedResolution || !resolution || resolution.isInvalidated ||
allFilesHaveInvalidatedResolution || unmatchedRedirects || !resolution || resolution.isInvalidated ||
// If the name is unresolved import that was invalidated, recalculate
(hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && !getResolutionWithResolvedFileName(resolution))) {
const existingResolution = resolution;
@@ -274,7 +286,7 @@ namespace ts {
resolution = resolutionInDirectory;
}
else {
resolution = loader(name, containingFile, compilerOptions, resolutionHost);
resolution = loader(name, containingFile, compilerOptions, resolutionHost, redirectedReference);
perDirectoryResolution.set(name, resolution);
}
resolutionsInFile.set(name, resolution);
@@ -323,18 +335,18 @@ namespace ts {
}
}
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirective[] {
return resolveNamesWithLocalCache<CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations, ResolvedTypeReferenceDirective>(
typeDirectiveNames, containingFile,
typeDirectiveNames, containingFile, redirectedReference,
resolvedTypeReferenceDirectives, perDirectoryResolvedTypeReferenceDirectives,
resolveTypeReferenceDirective, getResolvedTypeReferenceDirective,
/*reusedNames*/ undefined, /*logChanges*/ false
);
}
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[] {
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): ResolvedModuleFull[] {
return resolveNamesWithLocalCache<CachedResolvedModuleWithFailedLookupLocations, ResolvedModuleFull>(
moduleNames, containingFile,
moduleNames, containingFile, redirectedReference,
resolvedModuleNames, perDirectoryResolvedModuleNames,
resolveModuleName, getResolvedModule,
reusedNames, logChangesWhenResolvingModule
@@ -594,6 +606,20 @@ namespace ts {
}
}
function removeResolutionsFromProjectReferenceRedirects(filePath: Path) {
if (!fileExtensionIs(filePath, Extension.Json)) { return; }
const program = resolutionHost.getCurrentProgram();
if (!program) { return; }
// If this file is input file for the referenced project, get it
const resolvedProjectReference = program.getResolvedProjectReferenceByPath(filePath);
if (!resolvedProjectReference) { return; }
// filePath is for the projectReference and the containing file is from this project reference, invalidate the resolution
resolvedProjectReference.commandLine.fileNames.forEach(f => removeResolutionsOfFile(resolutionHost.toPath(f)));
}
function removeResolutionsOfFile(filePath: Path) {
removeResolutionsOfFileFromCache(resolvedModuleNames, filePath);
removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath);
+8 -4
View File
@@ -2906,8 +2906,11 @@ namespace ts {
/* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined;
getProjectReferences(): ReadonlyArray<ProjectReference> | undefined;
getResolvedProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined;
getResolvedProjectReferences(): ReadonlyArray<ResolvedProjectReference | undefined> | undefined;
/*@internal*/ getProjectReferenceRedirect(fileName: string): string | undefined;
/*@internal*/ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
/*@internal*/ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
/*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
}
/* @internal */
@@ -2916,6 +2919,7 @@ namespace ts {
export interface ResolvedProjectReference {
commandLine: ParsedCommandLine;
sourceFile: SourceFile;
references?: ReadonlyArray<ResolvedProjectReference | undefined>;
}
/* @internal */
@@ -4957,13 +4961,13 @@ 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[]): (ResolvedModule | undefined)[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference): (ResolvedModule | undefined)[];
/**
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
*/
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirective[];
getEnvironmentVariable?(name: string): string | undefined;
/* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void;
/* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void;
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
createHash?(data: string): string;
+13 -10
View File
@@ -338,9 +338,9 @@ namespace ts {
getEnvironmentVariable?(name: string): string | undefined;
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirective[];
}
/** Internal interface used to wire emit through same host */
@@ -558,11 +558,11 @@ namespace ts {
);
// Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names
compilerHost.resolveModuleNames = host.resolveModuleNames ?
((moduleNames, containingFile, reusedNames) => host.resolveModuleNames!(moduleNames, containingFile, reusedNames)) :
((moduleNames, containingFile, reusedNames) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames));
((moduleNames, containingFile, reusedNames, redirectedReference) => host.resolveModuleNames!(moduleNames, containingFile, reusedNames, redirectedReference)) :
((moduleNames, containingFile, reusedNames, redirectedReference) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference));
compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ?
((typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives!(typeDirectiveNames, containingFile)) :
((typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile));
((typeDirectiveNames, containingFile, redirectedReference) => host.resolveTypeReferenceDirectives!(typeDirectiveNames, containingFile, redirectedReference)) :
((typeDirectiveNames, containingFile, redirectedReference) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference));
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
synchronizeProgram();
@@ -764,8 +764,8 @@ namespace ts {
return !hostSourceFile || isFileMissingOnHost(hostSourceFile) ? undefined : hostSourceFile.version.toString();
}
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions) {
const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.path);
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions, hasSourceFileByPath: boolean) {
const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.resolvedPath);
// If this is the source file thats in the cache and new program doesnt need it,
// remove the cached entry.
// Note we arent deleting entry if file became missing in new program or
@@ -779,8 +779,10 @@ namespace ts {
if ((hostSourceFileInfo as FilePresentOnHost).fileWatcher) {
(hostSourceFileInfo as FilePresentOnHost).fileWatcher.close();
}
sourceFilesCache.delete(oldSourceFile.path);
resolutionCache.removeResolutionsOfFile(oldSourceFile.path);
sourceFilesCache.delete(oldSourceFile.resolvedPath);
if (!hasSourceFileByPath) {
resolutionCache.removeResolutionsOfFile(oldSourceFile.path);
}
}
}
}
@@ -875,6 +877,7 @@ namespace ts {
if (eventKind === FileWatcherEventKind.Deleted && sourceFilesCache.get(path)) {
resolutionCache.invalidateResolutionOfFile(path);
}
resolutionCache.removeResolutionsFromProjectReferenceRedirects(path);
nextSourceFileVersion(path);
// Update the program