mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Handle resolution caching when referenced tsconfig changes
This commit is contained in:
@@ -1294,6 +1294,8 @@ 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));
|
||||
}
|
||||
|
||||
|
||||
+107
-54
@@ -487,7 +487,7 @@ namespace ts {
|
||||
|
||||
function sourceFileNotUptoDate(sourceFile: SourceFile) {
|
||||
return !sourceFileVersionUptoDate(sourceFile) ||
|
||||
hasInvalidatedResolution(sourceFile.resolvedPath);
|
||||
hasInvalidatedResolution(sourceFile.path);
|
||||
}
|
||||
|
||||
function sourceFileVersionUptoDate(sourceFile: SourceFile) {
|
||||
@@ -752,10 +752,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
|
||||
@@ -798,7 +806,10 @@ namespace ts {
|
||||
getResolvedModuleWithFailedLookupLocationsFromCache,
|
||||
getProjectReferences,
|
||||
getResolvedProjectReferences,
|
||||
getProjectReferenceRedirect
|
||||
getProjectReferenceRedirect,
|
||||
getResolvedProjectReferenceToRedirect,
|
||||
getResolvedProjectReferenceByPath,
|
||||
forEachResolvedProjectReference
|
||||
};
|
||||
|
||||
verifyCompilerOptions();
|
||||
@@ -881,7 +892,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, /*reusedNames*/ undefined, getProjectReferenceRedirectProject(file.originalFileName));
|
||||
return resolveModuleNamesWorker(moduleNames, containingFile, /*reusedNames*/ undefined, getResolvedProjectReferenceToRedirect(file.originalFileName));
|
||||
}
|
||||
|
||||
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
|
||||
@@ -961,7 +972,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const resolutions = unknownModuleNames && unknownModuleNames.length
|
||||
? resolveModuleNamesWorker(unknownModuleNames, containingFile, reusedNames, getProjectReferenceRedirectProject(file.originalFileName))
|
||||
? resolveModuleNamesWorker(unknownModuleNames, containingFile, reusedNames, getResolvedProjectReferenceToRedirect(file.originalFileName))
|
||||
: emptyArray;
|
||||
|
||||
// Combine results of resolutions and predicted results
|
||||
@@ -1021,46 +1032,28 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function canReuseProjectReferences(
|
||||
newProjectReferences: ReadonlyArray<ProjectReference> | undefined,
|
||||
oldProjectReferences: ReadonlyArray<ProjectReference> | undefined,
|
||||
oldResolvedReferences: ReadonlyArray<ResolvedProjectReference | undefined> | undefined): boolean {
|
||||
// If array of references is changed, we cant resue old program
|
||||
if (!arrayIsEqualTo(oldProjectReferences!, newProjectReferences, projectReferenceIsEqualTo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the json files for the project references
|
||||
if (newProjectReferences) {
|
||||
// Resolved project referenced should be array if projectReferences provided are array
|
||||
Debug.assert(!!oldResolvedReferences);
|
||||
for (let i = 0; i < newProjectReferences.length; i++) {
|
||||
const oldRef = oldResolvedReferences![i];
|
||||
const newRef = parseProjectReferenceConfigFile(newProjectReferences[i]);
|
||||
if (oldRef) {
|
||||
if (!newRef || newRef.sourceFile !== oldRef.sourceFile) {
|
||||
// Resolved project reference has gone missing or changed
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the transitive references can be reused then only this reference can be reused
|
||||
if (!canReuseProjectReferences(newRef.commandLine.projectReferences, oldRef.commandLine.projectReferences, oldRef.references)) {
|
||||
return false;
|
||||
}
|
||||
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
|
||||
if (newRef !== undefined) {
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Resolved project referenced should be undefined if projectReferences is undefined
|
||||
Debug.assert(!oldResolvedReferences);
|
||||
}
|
||||
return true;
|
||||
);
|
||||
}
|
||||
|
||||
function tryReuseStructureFromOldProgram(): StructureIsReused {
|
||||
@@ -1088,7 +1081,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Check if any referenced project tsconfig files are different
|
||||
if (!canReuseProjectReferences(projectReferences, oldProgram.getProjectReferences(), oldProgram.getResolvedProjectReferences())) {
|
||||
if (!canReuseProjectReferences()) {
|
||||
return oldProgram.structureIsReused = StructureIsReused.Not;
|
||||
}
|
||||
resolvedProjectReferences = oldProgram.getResolvedProjectReferences();
|
||||
@@ -1240,7 +1233,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, getProjectReferenceRedirectProject(newSourceFile.originalFileName));
|
||||
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) {
|
||||
@@ -2215,7 +2208,7 @@ namespace ts {
|
||||
|
||||
// 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
|
||||
const referencedProject = getProjectReferenceRedirectProject(fileName);
|
||||
const referencedProject = getResolvedProjectReferenceToRedirect(fileName);
|
||||
if (!referencedProject) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -2229,16 +2222,11 @@ namespace ts {
|
||||
/**
|
||||
* Get the referenced project if the file is input file from that reference project
|
||||
*/
|
||||
function getProjectReferenceRedirectProject(fileName: string) {
|
||||
if (!resolvedProjectReferences || !resolvedProjectReferences.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If this file is input file of the referenced projec
|
||||
return forEachEntry(projectReferenceRedirects!, referencedProject => {
|
||||
function getResolvedProjectReferenceToRedirect(fileName: string) {
|
||||
return forEachResolvedProjectReference((referencedProject, referenceProjectPath) => {
|
||||
// not input file from the referenced project, ignore
|
||||
if (!referencedProject ||
|
||||
options.configFilePath === referencedProject.commandLine.options.configFilePath ||
|
||||
toPath(options.configFilePath!) === referenceProjectPath ||
|
||||
!contains(referencedProject.commandLine.fileNames, fileName, isSameFile)) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -2247,6 +2235,67 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -2261,7 +2310,7 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.originalFileName, getProjectReferenceRedirectProject(file.originalFileName));
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.originalFileName, getResolvedProjectReferenceToRedirect(file.originalFileName));
|
||||
|
||||
for (let i = 0; i < typeDirectives.length; i++) {
|
||||
const ref = file.typeReferenceDirectives[i];
|
||||
@@ -2450,11 +2499,14 @@ namespace ts {
|
||||
// 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 = sourceFilePath;
|
||||
sourceFile.resolvedPath = sourceFilePath;
|
||||
sourceFile.originalFileName = refPath;
|
||||
const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
|
||||
const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile };
|
||||
projectReferenceRedirects.set(sourceFilePath, resolvedRef);
|
||||
@@ -2506,12 +2558,13 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:: Errors on transitive references
|
||||
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);
|
||||
createDiagnosticForReference(i, Diagnostics.File_0_not_found, ref.path);
|
||||
continue;
|
||||
}
|
||||
if (!resolvedRefOpts.composite) {
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace ts {
|
||||
|
||||
invalidateResolutionOfFile(filePath: Path): void;
|
||||
removeResolutionsOfFile(filePath: Path): void;
|
||||
removeResolutionsFromProjectReferenceRedirects(filePath: Path): void;
|
||||
setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map<ReadonlyArray<string>>): void;
|
||||
createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution;
|
||||
|
||||
@@ -128,6 +129,7 @@ namespace ts {
|
||||
resolveModuleNames,
|
||||
getResolvedModuleWithFailedLookupLocationsFromCache,
|
||||
resolveTypeReferenceDirectives,
|
||||
removeResolutionsFromProjectReferenceRedirects,
|
||||
removeResolutionsOfFile,
|
||||
invalidateResolutionOfFile,
|
||||
setFilesWithInvalidatedNonRelativeUnresolvedImports,
|
||||
@@ -262,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;
|
||||
@@ -596,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);
|
||||
|
||||
@@ -2831,6 +2831,9 @@ namespace ts {
|
||||
getProjectReferences(): ReadonlyArray<ProjectReference> | 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 */
|
||||
@@ -4882,7 +4885,7 @@ namespace ts {
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -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 (oldSourceFile.resolvedPath === oldSourceFile.path || !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
|
||||
|
||||
@@ -871,15 +871,20 @@ namespace ts.server {
|
||||
if (!info) {
|
||||
this.logger.msg(`Error: got watch notification for unknown file: ${fileName}`);
|
||||
}
|
||||
else if (eventKind === FileWatcherEventKind.Deleted) {
|
||||
// File was deleted
|
||||
this.handleDeletedFile(info);
|
||||
}
|
||||
else if (!info.isScriptOpen()) {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.delayReloadNonMixedContentFile();
|
||||
this.delayUpdateProjectGraphs(info.containingProjects);
|
||||
else {
|
||||
if (info.containingProjects) {
|
||||
info.containingProjects.forEach(project => project.resolutionCache.removeResolutionsFromProjectReferenceRedirects(info.path));
|
||||
}
|
||||
if (eventKind === FileWatcherEventKind.Deleted) {
|
||||
// File was deleted
|
||||
this.handleDeletedFile(info);
|
||||
}
|
||||
else if (!info.isScriptOpen()) {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.delayReloadNonMixedContentFile();
|
||||
this.delayUpdateProjectGraphs(info.containingProjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2434,17 +2439,14 @@ namespace ts.server {
|
||||
}
|
||||
else {
|
||||
// If the configured project for project reference has more than zero references, keep it alive
|
||||
const resolvedProjectReferences = project.getResolvedProjectReferences();
|
||||
if (resolvedProjectReferences) {
|
||||
for (const ref of resolvedProjectReferences) {
|
||||
if (ref) {
|
||||
const refProject = this.configuredProjects.get(ref.sourceFile.path);
|
||||
if (refProject && refProject.hasOpenRef()) {
|
||||
toRemoveConfiguredProjects.delete(project.canonicalConfigFilePath);
|
||||
}
|
||||
project.forEachResolvedProjectReference(ref => {
|
||||
if (ref) {
|
||||
const refProject = this.configuredProjects.get(ref.sourceFile.path);
|
||||
if (refProject && refProject.hasOpenRef()) {
|
||||
toRemoveConfiguredProjects.delete(project.canonicalConfigFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+21
-15
@@ -583,14 +583,11 @@ namespace ts.server {
|
||||
for (const f of this.program.getSourceFiles()) {
|
||||
this.detachScriptInfoIfNotRoot(f.fileName);
|
||||
}
|
||||
const projectReferences = this.program.getResolvedProjectReferences();
|
||||
if (projectReferences) {
|
||||
for (const ref of projectReferences) {
|
||||
if (ref) {
|
||||
this.detachScriptInfoFromProject(ref.sourceFile.fileName);
|
||||
}
|
||||
this.program.forEachResolvedProjectReference(ref => {
|
||||
if (ref) {
|
||||
this.detachScriptInfoFromProject(ref.sourceFile.fileName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Release external files
|
||||
forEach(this.externalFiles, externalFile => this.detachScriptInfoIfNotRoot(externalFile));
|
||||
@@ -925,12 +922,19 @@ namespace ts.server {
|
||||
if (hasNewProgram) {
|
||||
if (oldProgram) {
|
||||
for (const f of oldProgram.getSourceFiles()) {
|
||||
if (this.program.getSourceFileByPath(f.path)) {
|
||||
continue;
|
||||
const newFile = this.program.getSourceFileByPath(f.resolvedPath);
|
||||
if (!newFile || (f.resolvedPath === f.path && newFile.resolvedPath !== f.path)) {
|
||||
// new program does not contain this file - detach it from the project
|
||||
// - remove resolutions only if this is undirected file or doesnt have source file with the path in new program
|
||||
this.detachScriptInfoFromProject(f.fileName, f.path !== f.resolvedPath && !!this.program.getSourceFileByPath(f.path));
|
||||
}
|
||||
// new program does not contain this file - detach it from the project
|
||||
this.detachScriptInfoFromProject(f.fileName);
|
||||
}
|
||||
|
||||
oldProgram.forEachResolvedProjectReference((resolvedProjectReference, resolvedProjectReferencePath) => {
|
||||
if (resolvedProjectReference && !this.program.getResolvedProjectReferenceByPath(resolvedProjectReferencePath)) {
|
||||
this.detachScriptInfoFromProject(resolvedProjectReference.sourceFile.fileName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update the missing file paths watcher
|
||||
@@ -964,11 +968,13 @@ namespace ts.server {
|
||||
return hasNewProgram;
|
||||
}
|
||||
|
||||
private detachScriptInfoFromProject(uncheckedFileName: string) {
|
||||
private detachScriptInfoFromProject(uncheckedFileName: string, noRemoveResolution?: boolean) {
|
||||
const scriptInfoToDetach = this.projectService.getScriptInfo(uncheckedFileName);
|
||||
if (scriptInfoToDetach) {
|
||||
scriptInfoToDetach.detachFromProject(this);
|
||||
this.resolutionCache.removeResolutionsOfFile(scriptInfoToDetach.path);
|
||||
if (!noRemoveResolution) {
|
||||
this.resolutionCache.removeResolutionsOfFile(scriptInfoToDetach.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1400,9 +1406,9 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getResolvedProjectReferences() {
|
||||
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined {
|
||||
const program = this.getCurrentProgram();
|
||||
return program && program.getResolvedProjectReferences();
|
||||
return program && program.forEachResolvedProjectReference(cb);
|
||||
}
|
||||
|
||||
enablePlugins() {
|
||||
|
||||
@@ -1276,7 +1276,7 @@ namespace ts {
|
||||
// not part of the new program.
|
||||
function onReleaseOldSourceFile(oldSourceFile: SourceFile, oldOptions: CompilerOptions) {
|
||||
const oldSettingsKey = documentRegistry.getKeyForCompilationSettings(oldOptions);
|
||||
documentRegistry.releaseDocumentWithKey(oldSourceFile.path, oldSettingsKey);
|
||||
documentRegistry.releaseDocumentWithKey(oldSourceFile.resolvedPath, oldSettingsKey);
|
||||
}
|
||||
|
||||
function getOrCreateSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined {
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace ts {
|
||||
};
|
||||
testProjectReferences(spec, "/primary/tsconfig.json", program => {
|
||||
const errs = program.getOptionsDiagnostics();
|
||||
assertHasError("Reports an error about a missing file", errs, Diagnostics.File_0_does_not_exist);
|
||||
assertHasError("Reports an error about a missing file", errs, Diagnostics.File_0_not_found);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -543,8 +543,8 @@ export function gfoo() {
|
||||
}
|
||||
|
||||
function verifyProgram(host: WatchedSystem, watch: () => BuilderProgram) {
|
||||
verifyWatchesOfProject(host, expectedWatchedFiles, expectedWatchedDirectoriesRecursive);
|
||||
checkProgramActualFiles(watch().getProgram(), expectedProgramFiles);
|
||||
verifyWatchesOfProject(host, expectedWatchedFiles, expectedWatchedDirectoriesRecursive);
|
||||
verifyDependencies(watch, aDts, [aDts]);
|
||||
verifyDependencies(watch, bDts, [bDts, aDts]);
|
||||
verifyDependencies(watch, refs.path, [refs.path]);
|
||||
@@ -588,12 +588,74 @@ export function gfoo() {
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
|
||||
const nrefReplacer = (f: string) => f.replace("refs", "nrefs");
|
||||
verifyWatchesOfProject(host, expectedWatchedFiles.map(nrefReplacer), expectedWatchedDirectoriesRecursive.map(nrefReplacer));
|
||||
checkProgramActualFiles(watch().getProgram(), expectedProgramFiles.map(nrefReplacer));
|
||||
verifyWatchesOfProject(host, expectedWatchedFiles.map(nrefReplacer), expectedWatchedDirectoriesRecursive.map(nrefReplacer));
|
||||
verifyDependencies(watch, aDts, [aDts]);
|
||||
verifyDependencies(watch, bDts, [bDts, aDts]);
|
||||
verifyDependencies(watch, nrefs.path, [nrefs.path]);
|
||||
verifyDependencies(watch, cTs.path, [cTs.path, nrefs.path, bDts]);
|
||||
|
||||
// revert the update
|
||||
host.writeFile(cTsconfig.path, cTsconfig.content);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyProgram(host, watch);
|
||||
});
|
||||
|
||||
it("edit in referenced config file", () => {
|
||||
const { host, watch } = createSolutionAndWatchMode();
|
||||
|
||||
const nrefs: File = {
|
||||
path: getFilePathInProject(project, "nrefs/a.d.ts"),
|
||||
content: host.readFile(aDts)!
|
||||
};
|
||||
const bTsConfigJson = JSON.parse(bTsconfig.content);
|
||||
host.ensureFileOrFolder(nrefs);
|
||||
bTsConfigJson.compilerOptions.paths = { "@ref/*": ["./nrefs/*"] };
|
||||
host.writeFile(bTsconfig.path, JSON.stringify(bTsConfigJson));
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
|
||||
const expectedProgramFiles = [cTs.path, bDts, nrefs.path, refs.path, libFile.path];
|
||||
checkProgramActualFiles(watch().getProgram(), expectedProgramFiles);
|
||||
verifyWatchesOfProject(host, expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()), expectedWatchedDirectoriesRecursive.concat(getFilePathInProject(project, "nrefs").toLowerCase()));
|
||||
verifyDependencies(watch, nrefs.path, [nrefs.path]);
|
||||
verifyDependencies(watch, bDts, [bDts, nrefs.path]);
|
||||
verifyDependencies(watch, refs.path, [refs.path]);
|
||||
verifyDependencies(watch, cTs.path, [cTs.path, refs.path, bDts]);
|
||||
|
||||
// revert the update
|
||||
host.writeFile(bTsconfig.path, bTsconfig.content);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyProgram(host, watch);
|
||||
});
|
||||
|
||||
it("deleting referenced config file", () => {
|
||||
const { host, watch } = createSolutionAndWatchMode();
|
||||
|
||||
// Resolutions should change now
|
||||
// Should map to b.ts instead with options from our own config
|
||||
host.deleteFile(bTsconfig.path);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, [
|
||||
"tsconfig.c.json(9,21): error TS6053: File '/user/username/projects/transitiveReferences/tsconfig.b.json' not found.\n"
|
||||
]);
|
||||
|
||||
const expectedProgramFiles = [cTs.path, bTs.path, refs.path, libFile.path];
|
||||
checkProgramActualFiles(watch().getProgram(), expectedProgramFiles);
|
||||
verifyWatchesOfProject(host, expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path).map(s => s.toLowerCase()), expectedWatchedDirectoriesRecursive);
|
||||
verifyDependencies(watch, bTs.path, [bTs.path, refs.path]);
|
||||
verifyDependencies(watch, refs.path, [refs.path]);
|
||||
verifyDependencies(watch, cTs.path, [cTs.path, refs.path, bTs.path]);
|
||||
|
||||
// revert the update
|
||||
host.writeFile(bTsconfig.path, bTsconfig.content);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyProgram(host, watch);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5070,7 +5070,7 @@ namespace ts.projectSystem {
|
||||
|
||||
function getFileNotFoundDiagnostic(configFile: File, relativeFileName: string): ConfigFileDiagnostic {
|
||||
const findString = `{"path":"./${relativeFileName}"}`;
|
||||
const d = Diagnostics.File_0_does_not_exist;
|
||||
const d = Diagnostics.File_0_not_found;
|
||||
const start = configFile.content.indexOf(findString);
|
||||
return {
|
||||
fileName: configFile.path,
|
||||
|
||||
Reference in New Issue
Block a user