mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-26 21:23:53 -06:00
Address PR feedback
Make Program.getMissingFilePaths required Assume getMissingFilePaths always returns a defined value Make getMissingFilePaths internal Replace nullable-bool with enum Update type to reflect possibility of undefined Use deepEqual to simplify tests Make condition const Don't bother cleaning up map before freeing it Switch from foreach to for-of to simplify debugging Use a Map, rather than a FileMap, to track open FileWatchers Fix compilation errors Introduce and consume arrayToSet Fix lint warnings about misplaced braces Delete incorrect comment Delete from map during iteration Eliminate unnecessary type annotations
This commit is contained in:
parent
0f683ac2ad
commit
569ecabb0a
@ -1097,6 +1097,15 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set from the elements of an array.
|
||||
*
|
||||
* @param array the array of input elements.
|
||||
*/
|
||||
export function arrayToSet<T>(array: T[], makeKey: (value: T) => string): Map<true> {
|
||||
return arrayToMap<T, true>(array, makeKey, () => true);
|
||||
}
|
||||
|
||||
export function cloneMap<T>(map: Map<T>) {
|
||||
const clone = createMap<T>();
|
||||
copyEntries(map, clone);
|
||||
|
||||
@ -472,7 +472,7 @@ namespace ts {
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(typeReferenceDirectiveNames, containingFile, loader);
|
||||
}
|
||||
|
||||
const filesByName = createMap<SourceFile>();
|
||||
const filesByName = createMap<SourceFile | undefined>();
|
||||
// stores 'filename -> file association' ignoring case
|
||||
// used to track cases when two file names differ only in casing
|
||||
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createFileMap<SourceFile>(fileName => fileName.toLowerCase()) : undefined;
|
||||
@ -513,7 +513,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
const missingFilePaths = filesByName.getKeys().filter(p => !filesByName.get(p));
|
||||
const missingFilePaths = arrayFrom(filesByName.keys(), p => <Path>p).filter(p => !filesByName.get(p));
|
||||
|
||||
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
|
||||
moduleResolutionCache = undefined;
|
||||
@ -872,17 +872,12 @@ namespace ts {
|
||||
// will be created until we encounter a change that prevents complete structure reuse.
|
||||
// During this interval, creation of the file will go unnoticed. We expect this to be
|
||||
// both rare and low-impact.
|
||||
if (oldProgram.getMissingFilePaths) {
|
||||
const missingFilePaths: Path[] = oldProgram.getMissingFilePaths() || emptyArray;
|
||||
for (const missingFilePath of missingFilePaths) {
|
||||
if (host.fileExists(missingFilePath)) {
|
||||
return oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
}
|
||||
if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) {
|
||||
return oldProgram.structureIsReused = StructureIsReused.SafeModules;
|
||||
}
|
||||
|
||||
for (const p of oldProgram.getMissingFilePaths()) {
|
||||
filesByName.set(p, undefined);
|
||||
}
|
||||
for (const p of oldProgram.getMissingFilePaths()) {
|
||||
filesByName.set(p, undefined);
|
||||
}
|
||||
|
||||
// update fileName -> file mapping
|
||||
|
||||
@ -4,7 +4,13 @@ declare function setTimeout(handler: (...args: any[]) => void, timeout: number):
|
||||
declare function clearTimeout(handle: any): void;
|
||||
|
||||
namespace ts {
|
||||
export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
|
||||
export enum FileWatcherEventKind {
|
||||
Created,
|
||||
Changed,
|
||||
Deleted
|
||||
}
|
||||
|
||||
export type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind) => void;
|
||||
export type DirectoryWatcherCallback = (fileName: string) => void;
|
||||
export interface WatchedFile {
|
||||
fileName: string;
|
||||
@ -174,7 +180,7 @@ namespace ts {
|
||||
const callbacks = fileWatcherCallbacks.get(fileName);
|
||||
if (callbacks) {
|
||||
for (const fileCallback of callbacks) {
|
||||
fileCallback(fileName);
|
||||
fileCallback(fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -342,18 +348,20 @@ namespace ts {
|
||||
function fileChanged(curr: any, prev: any) {
|
||||
const isCurrZero = +curr.mtime === 0;
|
||||
const isPrevZero = +prev.mtime === 0;
|
||||
const added = !isCurrZero && isPrevZero;
|
||||
const created = !isCurrZero && isPrevZero;
|
||||
const deleted = isCurrZero && !isPrevZero;
|
||||
|
||||
// This value is consistent with poll() in createPollingWatchedFileSet()
|
||||
// and depended upon by the file watchers created in Project.updateGraphWorker.
|
||||
const removed = deleted ? true : (added ? false : undefined);
|
||||
const eventKind = created
|
||||
? FileWatcherEventKind.Created
|
||||
: deleted
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
|
||||
if (!added && !deleted && +curr.mtime <= +prev.mtime) {
|
||||
if (eventKind === FileWatcherEventKind.Changed && +curr.mtime <= +prev.mtime) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(fileName, removed);
|
||||
callback(fileName, eventKind);
|
||||
}
|
||||
},
|
||||
watchDirectory: (directoryName, callback, recursive) => {
|
||||
|
||||
@ -286,18 +286,15 @@ namespace ts {
|
||||
setCachedProgram(compileResult.program);
|
||||
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes));
|
||||
|
||||
if (compileResult.program.getMissingFilePaths) {
|
||||
const missingPaths = compileResult.program.getMissingFilePaths() || [];
|
||||
missingPaths.forEach((path: Path): void => {
|
||||
const fileWatcher = sys.watchFile(path, (_fileName: string, removed?: boolean) => {
|
||||
// removed = deleted ? true : (added ? false : undefined)
|
||||
if (removed === false) {
|
||||
fileWatcher.close();
|
||||
startTimerForRecompilation();
|
||||
}
|
||||
});
|
||||
const missingPaths = compileResult.program.getMissingFilePaths();
|
||||
missingPaths.forEach(path => {
|
||||
const fileWatcher = sys.watchFile(path, (_fileName, eventKind) => {
|
||||
if (eventKind === FileWatcherEventKind.Created) {
|
||||
fileWatcher.close();
|
||||
startTimerForRecompilation();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cachedFileExists(fileName: string): boolean {
|
||||
@ -321,7 +318,7 @@ namespace ts {
|
||||
const sourceFile = hostGetSourceFile(fileName, languageVersion, onError);
|
||||
if (sourceFile && isWatchSet(compilerOptions) && sys.watchFile) {
|
||||
// Attach a file watcher
|
||||
sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, (_fileName: string, removed?: boolean) => sourceFileChanged(sourceFile, removed));
|
||||
sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, (_fileName, eventKind) => sourceFileChanged(sourceFile, eventKind));
|
||||
}
|
||||
return sourceFile;
|
||||
}
|
||||
@ -343,10 +340,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
// If a source file changes, mark it as unwatched and start the recompilation timer
|
||||
function sourceFileChanged(sourceFile: SourceFile, removed?: boolean) {
|
||||
function sourceFileChanged(sourceFile: SourceFile, eventKind: FileWatcherEventKind) {
|
||||
sourceFile.fileWatcher.close();
|
||||
sourceFile.fileWatcher = undefined;
|
||||
if (removed) {
|
||||
if (eventKind === FileWatcherEventKind.Deleted) {
|
||||
unorderedRemoveItem(rootFileNames, sourceFile.fileName);
|
||||
}
|
||||
startTimerForRecompilation();
|
||||
|
||||
@ -2430,7 +2430,8 @@ namespace ts {
|
||||
* Get a list of file names that were passed to 'createProgram' or referenced in a
|
||||
* program source file but could not be located.
|
||||
*/
|
||||
getMissingFilePaths?(): Path[];
|
||||
/* @internal */
|
||||
getMissingFilePaths(): Path[];
|
||||
|
||||
/**
|
||||
* Emits the JavaScript and declaration files. If targetSourceFile is not specified, then
|
||||
|
||||
@ -208,7 +208,7 @@ namespace ts.projectSystem {
|
||||
|
||||
file1Consumer1.content = `let y = 10;`;
|
||||
host.reloadFS([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
|
||||
host.triggerFileWatcherCallback(file1Consumer1.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(file1Consumer1.path, FileWatcherEventKind.Changed);
|
||||
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer2] }]);
|
||||
@ -225,7 +225,7 @@ namespace ts.projectSystem {
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
// Delete file1Consumer2
|
||||
host.reloadFS([moduleFile1, file1Consumer1, configFile, libFile]);
|
||||
host.triggerFileWatcherCallback(file1Consumer2.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(file1Consumer2.path, FileWatcherEventKind.Deleted);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
|
||||
});
|
||||
|
||||
@ -475,7 +475,7 @@ namespace ts.projectSystem {
|
||||
|
||||
openFilesForSession([referenceFile1], session);
|
||||
host.reloadFS([referenceFile1, configFile]);
|
||||
host.triggerFileWatcherCallback(moduleFile1.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(moduleFile1.path, FileWatcherEventKind.Deleted);
|
||||
|
||||
const request = makeSessionRequest<server.protocol.FileRequestArgs>(CommandNames.CompileOnSaveAffectedFileList, { file: referenceFile1.path });
|
||||
sendAffectedFileRequestAndCheckResult(session, request, [
|
||||
|
||||
@ -41,39 +41,33 @@ namespace ts {
|
||||
const program = createProgram([emptyFileRelativePath], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths();
|
||||
assert.isDefined(missing);
|
||||
assert.equal(missing.length, 0);
|
||||
assert.deepEqual(missing, []);
|
||||
});
|
||||
|
||||
it("handles missing root file", () => {
|
||||
const program = createProgram(["./nonexistent.ts"], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths();
|
||||
assert.isDefined(missing);
|
||||
assert.equal(missing.length, 1);
|
||||
assert.equal(missing[0].toString(), "d:/pretend/nonexistent.ts"); // Absolute path
|
||||
assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]); // Absolute path
|
||||
});
|
||||
|
||||
it("handles multiple missing root files", () => {
|
||||
const program = createProgram(["./nonexistent0.ts", "./nonexistent1.ts"], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths().sort();
|
||||
assert.equal(missing.length, 2);
|
||||
assert.equal(missing[0].toString(), "d:/pretend/nonexistent0.ts");
|
||||
assert.equal(missing[1].toString(), "d:/pretend/nonexistent1.ts");
|
||||
assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
|
||||
});
|
||||
|
||||
it("handles a mix of present and missing root files", () => {
|
||||
const program = createProgram(["./nonexistent0.ts", emptyFileRelativePath, "./nonexistent1.ts"], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths().sort();
|
||||
assert.equal(missing.length, 2);
|
||||
assert.equal(missing[0].toString(), "d:/pretend/nonexistent0.ts");
|
||||
assert.equal(missing[1].toString(), "d:/pretend/nonexistent1.ts");
|
||||
assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
|
||||
});
|
||||
|
||||
it("handles repeatedly specified root files", () => {
|
||||
const program = createProgram(["./nonexistent.ts", "./nonexistent.ts"], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths();
|
||||
assert.isDefined(missing);
|
||||
assert.equal(missing.length, 1);
|
||||
assert.equal(missing[0].toString(), "d:/pretend/nonexistent.ts");
|
||||
assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]);
|
||||
});
|
||||
|
||||
it("normalizes file paths", () => {
|
||||
@ -89,21 +83,21 @@ namespace ts {
|
||||
const program = createProgram([referenceFileRelativePath], options, testCompilerHost);
|
||||
const missing = program.getMissingFilePaths().sort();
|
||||
assert.isDefined(missing);
|
||||
assert.equal(missing.length, 6);
|
||||
assert.deepEqual(missing, [
|
||||
// From absolute reference
|
||||
"d:/imaginary/nonexistent1.ts",
|
||||
|
||||
// From absolute reference
|
||||
assert.equal(missing[0].toString(), "d:/imaginary/nonexistent1.ts");
|
||||
// From relative reference
|
||||
"d:/pretend/nonexistent2.ts",
|
||||
|
||||
// From relative reference
|
||||
assert.equal(missing[1].toString(), "d:/pretend/nonexistent2.ts");
|
||||
// From unqualified reference
|
||||
"d:/pretend/nonexistent3.ts",
|
||||
|
||||
// From unqualified reference
|
||||
assert.equal(missing[2].toString(), "d:/pretend/nonexistent3.ts");
|
||||
|
||||
// From no-extension reference
|
||||
assert.equal(missing[3].toString(), "d:/pretend/nonexistent4.d.ts");
|
||||
assert.equal(missing[4].toString(), "d:/pretend/nonexistent4.ts");
|
||||
assert.equal(missing[5].toString(), "d:/pretend/nonexistent4.tsx");
|
||||
// From no-extension reference
|
||||
"d:/pretend/nonexistent4.d.ts",
|
||||
"d:/pretend/nonexistent4.ts",
|
||||
"d:/pretend/nonexistent4.tsx"
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -135,7 +135,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
// fix config and trigger watcher
|
||||
host.reloadFS([file1, file2, correctConfig]);
|
||||
host.triggerFileWatcherCallback(correctConfig.path, /*false*/);
|
||||
host.triggerFileWatcherCallback(correctConfig.path, FileWatcherEventKind.Changed);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
@ -177,7 +177,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
// break config and trigger watcher
|
||||
host.reloadFS([file1, file2, corruptedConfig]);
|
||||
host.triggerFileWatcherCallback(corruptedConfig.path, /*false*/);
|
||||
host.triggerFileWatcherCallback(corruptedConfig.path, FileWatcherEventKind.Changed);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
|
||||
@ -485,12 +485,12 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}
|
||||
|
||||
triggerFileWatcherCallback(fileName: string, removed?: boolean): void {
|
||||
triggerFileWatcherCallback(fileName: string, eventKind: FileWatcherEventKind): void {
|
||||
const path = this.toPath(fileName);
|
||||
const callbacks = this.watchedFiles.get(path);
|
||||
if (callbacks) {
|
||||
for (const callback of callbacks) {
|
||||
callback(path, removed);
|
||||
callback(path, eventKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -771,7 +771,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// remove the tsconfig file
|
||||
host.reloadFS(filesWithoutConfig);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 2);
|
||||
checkNumberOfConfiguredProjects(projectService, 0);
|
||||
@ -928,7 +928,7 @@ namespace ts.projectSystem {
|
||||
"files": ["${commonFile1.path}"]
|
||||
}`;
|
||||
host.reloadFS(files);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
checkProjectRootFiles(project, [commonFile1.path]);
|
||||
@ -1002,7 +1002,7 @@ namespace ts.projectSystem {
|
||||
"files": ["${file1.path}"]
|
||||
}`;
|
||||
host.reloadFS(files);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
checkProjectActualFiles(project, [file1.path, classicModuleFile.path, configFile.path]);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
});
|
||||
@ -1433,7 +1433,7 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
host.reloadFS([file1, modifiedFile2, file3]);
|
||||
host.triggerFileWatcherCallback(modifiedFile2.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(modifiedFile2.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path, modifiedFile2.path, file3.path]);
|
||||
@ -1465,7 +1465,7 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
|
||||
host.reloadFS([file1, file3]);
|
||||
host.triggerFileWatcherCallback(file2.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(file2.path, FileWatcherEventKind.Deleted);
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
|
||||
@ -1663,7 +1663,7 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
host.reloadFS([file1, file2, modifiedConfigFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectRootFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
@ -1696,7 +1696,7 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
host.reloadFS([file1, file2, modifiedConfigFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectRootFiles(projectService.configuredProjects[0], [file1.path, file2.path]);
|
||||
@ -1776,7 +1776,7 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [file1.path, file2.path, config.path]);
|
||||
|
||||
host.reloadFS([file1, file2]);
|
||||
host.triggerFileWatcherCallback(config.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(config.path, FileWatcherEventKind.Deleted);
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path]);
|
||||
@ -2258,7 +2258,7 @@ namespace ts.projectSystem {
|
||||
assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state");
|
||||
|
||||
host.reloadFS([f1, f2, configWithExclude]);
|
||||
host.triggerFileWatcherCallback(config.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(config.path, FileWatcherEventKind.Changed);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
assert.isTrue(project.languageServiceEnabled, "Language service enabled");
|
||||
@ -2492,7 +2492,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// rename tsconfig.json back to lib.ts
|
||||
host.reloadFS([f1, f2]);
|
||||
host.triggerFileWatcherCallback(tsconfig.path, /*removed*/ true);
|
||||
host.triggerFileWatcherCallback(tsconfig.path, FileWatcherEventKind.Deleted);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
@ -2637,7 +2637,7 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, app.path, config1.path]);
|
||||
|
||||
host.reloadFS([libES5, libES2015Promise, app, config2]);
|
||||
host.triggerFileWatcherCallback(config1.path);
|
||||
host.triggerFileWatcherCallback(config1.path, FileWatcherEventKind.Changed);
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.configuredProjects[0], [libES5.path, libES2015Promise.path, app.path, config2.path]);
|
||||
@ -2860,7 +2860,7 @@ namespace ts.projectSystem {
|
||||
const moduleFileNewPath = "/a/b/moduleFile1.ts";
|
||||
moduleFile.path = moduleFileNewPath;
|
||||
host.reloadFS([moduleFile, file1]);
|
||||
host.triggerFileWatcherCallback(moduleFileOldPath);
|
||||
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Changed);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
@ -2868,7 +2868,7 @@ namespace ts.projectSystem {
|
||||
|
||||
moduleFile.path = moduleFileOldPath;
|
||||
host.reloadFS([moduleFile, file1]);
|
||||
host.triggerFileWatcherCallback(moduleFileNewPath);
|
||||
host.triggerFileWatcherCallback(moduleFileNewPath, FileWatcherEventKind.Changed);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
@ -2912,7 +2912,7 @@ namespace ts.projectSystem {
|
||||
const moduleFileNewPath = "/a/b/moduleFile1.ts";
|
||||
moduleFile.path = moduleFileNewPath;
|
||||
host.reloadFS([moduleFile, file1, configFile]);
|
||||
host.triggerFileWatcherCallback(moduleFileOldPath);
|
||||
host.triggerFileWatcherCallback(moduleFileOldPath, FileWatcherEventKind.Changed);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
@ -2920,7 +2920,7 @@ namespace ts.projectSystem {
|
||||
|
||||
moduleFile.path = moduleFileOldPath;
|
||||
host.reloadFS([moduleFile, file1, configFile]);
|
||||
host.triggerFileWatcherCallback(moduleFileNewPath);
|
||||
host.triggerFileWatcherCallback(moduleFileNewPath, FileWatcherEventKind.Changed);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
diags = <server.protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
@ -3085,7 +3085,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}`;
|
||||
host.reloadFS([file, configFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
serverEventManager.checkEventCountOfType("configFileDiag", 2);
|
||||
|
||||
@ -3093,7 +3093,7 @@ namespace ts.projectSystem {
|
||||
"compilerOptions": {}
|
||||
}`;
|
||||
host.reloadFS([file, configFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
serverEventManager.checkEventCountOfType("configFileDiag", 3);
|
||||
});
|
||||
@ -4021,7 +4021,7 @@ namespace ts.projectSystem {
|
||||
|
||||
configFile.content = configFileContentWithoutCommentLine;
|
||||
host.reloadFS([file, configFile]);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed);
|
||||
|
||||
const diagsAfterEdit = session.executeCommand(<server.protocol.SemanticDiagnosticsSyncRequest>{
|
||||
type: "request",
|
||||
|
||||
@ -820,7 +820,7 @@ namespace ts.projectSystem {
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
|
||||
host.reloadFS([f, fixedPackageJson]);
|
||||
host.triggerFileWatcherCallback(fixedPackageJson.path, /*removed*/ false);
|
||||
host.triggerFileWatcherCallback(fixedPackageJson.path, FileWatcherEventKind.Changed);
|
||||
// expected install request
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
|
||||
@ -107,7 +107,7 @@ namespace ts.server {
|
||||
private rootFilesMap: Map<ScriptInfo> = createMap<ScriptInfo>();
|
||||
private program: ts.Program;
|
||||
private externalFiles: SortedReadonlyArray<string>;
|
||||
private missingFilesMap: FileMap<FileWatcher> = createFileMap<FileWatcher>();
|
||||
private missingFilesMap: Map<FileWatcher> = createMap<FileWatcher>();
|
||||
|
||||
private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap();
|
||||
private lastCachedUnresolvedImportsList: SortedReadonlyArray<string>;
|
||||
@ -312,10 +312,7 @@ namespace ts.server {
|
||||
this.lsHost = undefined;
|
||||
|
||||
// Clean up file watchers waiting for missing files
|
||||
for (const p of this.missingFilesMap.getKeys()) {
|
||||
this.missingFilesMap.get(p).close();
|
||||
this.missingFilesMap.remove(p);
|
||||
}
|
||||
this.missingFilesMap.forEach(fileWatcher => fileWatcher.close());
|
||||
this.missingFilesMap = undefined;
|
||||
|
||||
// signal language service to release source files acquired from document registry
|
||||
@ -594,12 +591,12 @@ namespace ts.server {
|
||||
const oldProgram = this.program;
|
||||
this.program = this.languageService.getProgram();
|
||||
|
||||
let hasChanges = false;
|
||||
// bump up the version if
|
||||
// - oldProgram is not set - this is a first time updateGraph is called
|
||||
// - newProgram is different from the old program and structure of the old program was not reused.
|
||||
if (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely))) {
|
||||
hasChanges = true;
|
||||
const hasChanges = !oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely));
|
||||
|
||||
if (hasChanges) {
|
||||
if (oldProgram) {
|
||||
for (const f of oldProgram.getSourceFiles()) {
|
||||
if (this.program.getSourceFileByPath(f.path)) {
|
||||
@ -612,39 +609,35 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanges && this.program.getMissingFilePaths) {
|
||||
const missingFilePaths = this.program.getMissingFilePaths() || emptyArray;
|
||||
const missingFilePathsSet = createMap<true>();
|
||||
missingFilePaths.forEach(p => missingFilePathsSet.set(p, true));
|
||||
const missingFilePaths = this.program.getMissingFilePaths();
|
||||
const missingFilePathsSet = arrayToSet(missingFilePaths, p => p);
|
||||
|
||||
// Files that are no longer missing (e.g. because they are no longer required)
|
||||
// should no longer be watched.
|
||||
this.missingFilesMap.getKeys().forEach(p => {
|
||||
if (!missingFilePathsSet.has(p)) {
|
||||
this.missingFilesMap.get(p).close();
|
||||
this.missingFilesMap.remove(p);
|
||||
this.missingFilesMap.forEach((fileWatcher, missingFilePath) => {
|
||||
if (!missingFilePathsSet.has(missingFilePath)) {
|
||||
this.missingFilesMap.delete(missingFilePath);
|
||||
fileWatcher.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Missing files that are not yet watched should be added to the map.
|
||||
missingFilePaths.forEach(p => {
|
||||
if (!this.missingFilesMap.contains(p)) {
|
||||
const fileWatcher = this.projectService.host.watchFile(p, (_filename: string, removed?: boolean) => {
|
||||
// removed = deleted ? true : (added ? false : undefined)
|
||||
if (removed === false && this.missingFilesMap.contains(p)) {
|
||||
for (const missingFilePath of missingFilePaths) {
|
||||
if (!this.missingFilesMap.has(missingFilePath)) {
|
||||
const fileWatcher = this.projectService.host.watchFile(missingFilePath, (_filename: string, eventKind: FileWatcherEventKind) => {
|
||||
if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap.has(missingFilePath)) {
|
||||
fileWatcher.close();
|
||||
this.missingFilesMap.remove(p);
|
||||
this.missingFilesMap.delete(missingFilePath);
|
||||
|
||||
// When a missing file is created, we should update the graph.
|
||||
this.markAsDirty();
|
||||
this.updateGraph();
|
||||
}
|
||||
});
|
||||
this.missingFilesMap.set(p, fileWatcher);
|
||||
this.missingFilesMap.set(missingFilePath, fileWatcher);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const oldExternalFiles = this.externalFiles || emptyArray as SortedReadonlyArray<string>;
|
||||
@ -668,7 +661,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
isWatchedMissingFile(path: Path) {
|
||||
return this.missingFilesMap.contains(path);
|
||||
return this.missingFilesMap.has(path);
|
||||
}
|
||||
|
||||
getScriptInfoLSHost(fileName: string) {
|
||||
|
||||
@ -522,16 +522,16 @@ namespace ts.server {
|
||||
return;
|
||||
}
|
||||
|
||||
// removed = deleted ? true : (added ? false : undefined)
|
||||
// This value is consistent with sys.watchFile()
|
||||
// and depended upon by the file watchers created in performCompilation() in tsc's executeCommandLine().
|
||||
fs.stat(watchedFile.fileName, (err: any, stats: any) => {
|
||||
if (err) {
|
||||
watchedFile.callback(watchedFile.fileName);
|
||||
watchedFile.callback(watchedFile.fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) {
|
||||
watchedFile.mtime = stats.mtime;
|
||||
watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0);
|
||||
const eventKind = watchedFile.mtime.getTime() === 0
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
watchedFile.callback(watchedFile.fileName, eventKind);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user