mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-10 06:41:59 -06:00
Instead of watching individual script infos, watch the node modules folder for script infos in node modules
This commit is contained in:
parent
1a69f78fba
commit
e2edb69638
@ -317,18 +317,22 @@ namespace ts {
|
||||
const newTime = modifiedTime.getTime();
|
||||
if (oldTime !== newTime) {
|
||||
watchedFile.mtime = modifiedTime;
|
||||
const eventKind = oldTime === 0
|
||||
? FileWatcherEventKind.Created
|
||||
: newTime === 0
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
watchedFile.callback(watchedFile.fileName, eventKind);
|
||||
watchedFile.callback(watchedFile.fileName, getFileWatcherEventKind(oldTime, newTime));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function getFileWatcherEventKind(oldTime: number, newTime: number) {
|
||||
return oldTime === 0
|
||||
? FileWatcherEventKind.Created
|
||||
: newTime === 0
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export interface RecursiveDirectoryWatcherHost {
|
||||
watchDirectory: HostWatchDirectory;
|
||||
|
||||
@ -291,7 +291,8 @@ namespace ts.server {
|
||||
ClosedScriptInfo = "Closed Script info",
|
||||
ConfigFileForInferredRoot = "Config file for the inferred project root",
|
||||
FailedLookupLocation = "Directory of Failed lookup locations in module resolution",
|
||||
TypeRoots = "Type root directory"
|
||||
TypeRoots = "Type root directory",
|
||||
NodeModulesForClosedScriptInfo = "node_modules for closed script infos in them",
|
||||
}
|
||||
|
||||
const enum ConfigFileWatcherStatus {
|
||||
@ -353,10 +354,18 @@ namespace ts.server {
|
||||
return !!(infoOrFileName as ScriptInfo).containingProjects;
|
||||
}
|
||||
|
||||
interface ScriptInfoInNodeModulesWatcher extends FileWatcher {
|
||||
refCount: number;
|
||||
}
|
||||
|
||||
function getDetailWatchInfo(watchType: WatchType, project: Project | undefined) {
|
||||
return `Project: ${project ? project.getProjectName() : ""} WatchType: ${watchType}`;
|
||||
}
|
||||
|
||||
function isScriptInfoWatchedFromNodeModules(info: ScriptInfo) {
|
||||
return !info.isScriptOpen() && info.mTime !== undefined;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function updateProjectIfDirty(project: Project) {
|
||||
return project.dirty && project.updateGraph();
|
||||
@ -380,6 +389,7 @@ namespace ts.server {
|
||||
* Container of all known scripts
|
||||
*/
|
||||
private readonly filenameToScriptInfo = createMap<ScriptInfo>();
|
||||
private readonly scriptInfoInNodeModulesWatchers = createMap <ScriptInfoInNodeModulesWatcher>();
|
||||
/**
|
||||
* Contains all the deleted script info's version information so that
|
||||
* it does not reset when creating script info again
|
||||
@ -1923,18 +1933,97 @@ namespace ts.server {
|
||||
if (!info.isDynamicOrHasMixedContent() &&
|
||||
(!this.globalCacheLocationDirectoryPath ||
|
||||
!startsWith(info.path, this.globalCacheLocationDirectoryPath))) {
|
||||
const { fileName } = info;
|
||||
info.fileWatcher = this.watchFactory.watchFilePath(
|
||||
this.host,
|
||||
fileName,
|
||||
(fileName, eventKind, path) => this.onSourceFileChanged(fileName, eventKind, path),
|
||||
PollingInterval.Medium,
|
||||
info.path,
|
||||
WatchType.ClosedScriptInfo
|
||||
);
|
||||
const indexOfNodeModules = info.path.indexOf("/node_modules/");
|
||||
if (!this.host.getModifiedTime || indexOfNodeModules === -1) {
|
||||
info.fileWatcher = this.watchFactory.watchFilePath(
|
||||
this.host,
|
||||
info.fileName,
|
||||
(fileName, eventKind, path) => this.onSourceFileChanged(fileName, eventKind, path),
|
||||
PollingInterval.Medium,
|
||||
info.path,
|
||||
WatchType.ClosedScriptInfo
|
||||
);
|
||||
}
|
||||
else {
|
||||
info.mTime = this.getModifiedTime(info);
|
||||
info.fileWatcher = this.watchClosedScriptInfoInNodeModules(info.path.substr(0, indexOfNodeModules) as Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private watchClosedScriptInfoInNodeModules(dir: Path): ScriptInfoInNodeModulesWatcher {
|
||||
// Watch only directory
|
||||
const existing = this.scriptInfoInNodeModulesWatchers.get(dir);
|
||||
if (existing) {
|
||||
existing.refCount++;
|
||||
return existing;
|
||||
}
|
||||
|
||||
const watchDir = dir + "/node_modules" as Path;
|
||||
const watcher = this.watchFactory.watchDirectory(
|
||||
this.host,
|
||||
watchDir,
|
||||
(fileOrDirectory) => {
|
||||
const fileOrDirectoryPath = this.toPath(fileOrDirectory);
|
||||
// Has extension
|
||||
Debug.assert(result.refCount > 0);
|
||||
if (watchDir === fileOrDirectoryPath) {
|
||||
this.refreshScriptInfosInDirectory(watchDir);
|
||||
}
|
||||
else {
|
||||
const info = this.getScriptInfoForPath(fileOrDirectoryPath);
|
||||
if (info) {
|
||||
if (isScriptInfoWatchedFromNodeModules(info)) {
|
||||
this.refreshScriptInfo(info);
|
||||
}
|
||||
}
|
||||
// Folder
|
||||
else if (!hasExtension(fileOrDirectoryPath)) {
|
||||
this.refreshScriptInfosInDirectory(fileOrDirectoryPath);
|
||||
}
|
||||
}
|
||||
},
|
||||
WatchDirectoryFlags.Recursive,
|
||||
WatchType.NodeModulesForClosedScriptInfo
|
||||
);
|
||||
const result: ScriptInfoInNodeModulesWatcher = {
|
||||
close: () => {
|
||||
if (result.refCount === 1) {
|
||||
watcher.close();
|
||||
this.scriptInfoInNodeModulesWatchers.delete(dir);
|
||||
}
|
||||
else {
|
||||
result.refCount--;
|
||||
}
|
||||
},
|
||||
refCount: 1
|
||||
};
|
||||
this.scriptInfoInNodeModulesWatchers.set(dir, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private getModifiedTime(info: ScriptInfo) {
|
||||
return (this.host.getModifiedTime!(info.path) || missingFileModifiedTime).getTime();
|
||||
}
|
||||
|
||||
private refreshScriptInfo(info: ScriptInfo) {
|
||||
const mTime = this.getModifiedTime(info);
|
||||
if (mTime !== info.mTime) {
|
||||
const eventKind = getFileWatcherEventKind(info.mTime!, mTime);
|
||||
info.mTime = mTime;
|
||||
this.onSourceFileChanged(info.fileName, eventKind, info.path);
|
||||
}
|
||||
}
|
||||
|
||||
private refreshScriptInfosInDirectory(dir: Path) {
|
||||
dir = dir + directorySeparator as Path;
|
||||
this.filenameToScriptInfo.forEach(info => {
|
||||
if (isScriptInfoWatchedFromNodeModules(info) && startsWith(info.path, dir)) {
|
||||
this.refreshScriptInfo(info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private stopWatchingScriptInfo(info: ScriptInfo) {
|
||||
if (info.fileWatcher) {
|
||||
info.fileWatcher.close();
|
||||
|
||||
@ -250,6 +250,9 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
cacheSourceFile: DocumentRegistrySourceFileCache;
|
||||
|
||||
/*@internal*/
|
||||
mTime?: number;
|
||||
|
||||
constructor(
|
||||
private readonly host: ServerHost,
|
||||
readonly fileName: NormalizedPath,
|
||||
|
||||
@ -3136,7 +3136,7 @@ namespace ts.projectSystem {
|
||||
const project = projectService.configuredProjects.get(configFile.path)!;
|
||||
assert.isDefined(project);
|
||||
checkProjectActualFiles(project, [file1.path, libFile.path, module1.path, module2.path, configFile.path]);
|
||||
checkWatchedFiles(host, [libFile.path, module1.path, module2.path, configFile.path]);
|
||||
checkWatchedFiles(host, [libFile.path, configFile.path]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
const watchedRecursiveDirectories = getTypeRootsFromLocation(root + "/a/b/src");
|
||||
watchedRecursiveDirectories.push(`${root}/a/b/src/node_modules`, `${root}/a/b/node_modules`);
|
||||
@ -7435,7 +7435,7 @@ namespace ts.projectSystem {
|
||||
const projectFilePaths = map(projectFiles, f => f.path);
|
||||
checkProjectActualFiles(project, projectFilePaths);
|
||||
|
||||
const filesWatched = filter(projectFilePaths, p => p !== app.path);
|
||||
const filesWatched = filter(projectFilePaths, p => p !== app.path && p.indexOf("/a/b/node_modules") === -1);
|
||||
checkWatchedFiles(host, filesWatched);
|
||||
checkWatchedDirectories(host, typeRootDirectories.concat(recursiveWatchedDirectories), /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
@ -8658,10 +8658,21 @@ new C();`
|
||||
}
|
||||
|
||||
function verifyWatchesWithConfigFile(host: TestServerHost, files: File[], openFile: File, extraExpectedDirectories?: ReadonlyArray<string>) {
|
||||
checkWatchedFiles(host, mapDefined(files, f => f === openFile ? undefined : f.path));
|
||||
const expectedRecursiveDirectories = arrayToSet([projectLocation, `${projectLocation}/${nodeModulesAtTypes}`, ...(extraExpectedDirectories || emptyArray)]);
|
||||
checkWatchedFiles(host, mapDefined(files, f => {
|
||||
if (f === openFile) {
|
||||
return undefined;
|
||||
}
|
||||
const indexOfNodeModules = f.path.indexOf("/node_modules/");
|
||||
if (indexOfNodeModules === -1) {
|
||||
return f.path;
|
||||
}
|
||||
expectedRecursiveDirectories.set(f.path.substr(0, indexOfNodeModules + "/node_modules".length), true);
|
||||
return undefined;
|
||||
}));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [projectLocation, `${projectLocation}/${nodeModulesAtTypes}`, ...(extraExpectedDirectories || emptyArray)], /*recursive*/ true);
|
||||
}
|
||||
checkWatchedDirectories(host, arrayFrom(expectedRecursiveDirectories.keys()), /*recursive*/ true);
|
||||
}
|
||||
|
||||
describe("from files in same folder", () => {
|
||||
function getFiles(fileContent: string) {
|
||||
@ -8862,7 +8873,7 @@ new C();`
|
||||
verifyTrace(resolutionTrace, expectedTrace);
|
||||
|
||||
const currentDirectory = getDirectoryPath(file1.path);
|
||||
const watchedFiles = mapDefined(files, f => f === file1 ? undefined : f.path);
|
||||
const watchedFiles = mapDefined(files, f => f === file1 || f.path.indexOf("/node_modules/") !== -1 ? undefined : f.path);
|
||||
forEachAncestorDirectory(currentDirectory, d => {
|
||||
watchedFiles.push(combinePaths(d, "tsconfig.json"), combinePaths(d, "jsconfig.json"));
|
||||
});
|
||||
|
||||
@ -8382,6 +8382,7 @@ declare namespace ts.server {
|
||||
* Container of all known scripts
|
||||
*/
|
||||
private readonly filenameToScriptInfo;
|
||||
private readonly scriptInfoInNodeModulesWatchers;
|
||||
/**
|
||||
* Contains all the deleted script info's version information so that
|
||||
* it does not reset when creating script info again
|
||||
@ -8552,6 +8553,10 @@ declare namespace ts.server {
|
||||
private createInferredProject;
|
||||
getScriptInfo(uncheckedFileName: string): ScriptInfo | undefined;
|
||||
private watchClosedScriptInfo;
|
||||
private watchClosedScriptInfoInNodeModules;
|
||||
private getModifiedTime;
|
||||
private refreshScriptInfo;
|
||||
private refreshScriptInfosInDirectory;
|
||||
private stopWatchingScriptInfo;
|
||||
private getOrCreateScriptInfoNotOpenedByClientForNormalizedPath;
|
||||
private getOrCreateScriptInfoOpenedByClientForNormalizedPath;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user