Skip to content

Commit

Permalink
Prefer non recursive watch outside rootDir and node_modules if host p…
Browse files Browse the repository at this point in the history
…refers it
  • Loading branch information
sheetalkamat committed Jun 14, 2024
1 parent 7f9d9e5 commit 560ac16
Show file tree
Hide file tree
Showing 24 changed files with 20,658 additions and 20,624 deletions.
22 changes: 17 additions & 5 deletions src/compiler/resolutionCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export interface ResolutionCacheHost extends MinimalResolutionCacheHost {
toPath(fileName: string): Path;
getCanonicalFileName: GetCanonicalFileName;
getCompilationSettings(): CompilerOptions;
preferNonRecursiveWatch: boolean | undefined;
watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher;
watchAffectingFileLocation(file: string, cb: FileWatcherCallback): FileWatcher;
onInvalidatedResolution(): void;
Expand Down Expand Up @@ -351,6 +352,7 @@ export function getDirectoryToWatchFailedLookupLocation(
rootPath: Path,
rootPathComponents: Readonly<PathPathComponents>,
getCurrentDirectory: () => string | undefined,
preferNonRecursiveWatch: boolean | undefined,
): DirectoryOfFailedLookupWatch | undefined {
const failedLookupPathComponents: Readonly<PathPathComponents> = getPathComponents(failedLookupLocationPath);
// Ensure failed look up is normalized path
Expand Down Expand Up @@ -390,6 +392,7 @@ export function getDirectoryToWatchFailedLookupLocation(
nodeModulesIndex,
rootPathComponents,
lastNodeModulesIndex,
preferNonRecursiveWatch,
);
}

Expand All @@ -401,6 +404,7 @@ function getDirectoryToWatchFromFailedLookupLocationDirectory(
nodeModulesIndex: number,
rootPathComponents: Readonly<PathPathComponents>,
lastNodeModulesIndex: number,
preferNonRecursiveWatch: boolean | undefined,
): DirectoryOfFailedLookupWatch | undefined {
// If directory path contains node module, get the most parent node_modules directory for watching
if (nodeModulesIndex !== -1) {
Expand All @@ -412,14 +416,17 @@ function getDirectoryToWatchFromFailedLookupLocationDirectory(
lastNodeModulesIndex,
);
}

// Use some ancestor of the root directory
let nonRecursive = true;
let length = dirPathComponentsLength;
for (let i = 0; i < dirPathComponentsLength; i++) {
if (dirPathComponents[i] !== rootPathComponents[i]) {
nonRecursive = false;
length = Math.max(i + 1, perceivedOsRootLength + 1);
break;
if (!preferNonRecursiveWatch) {
for (let i = 0; i < dirPathComponentsLength; i++) {
if (dirPathComponents[i] !== rootPathComponents[i]) {
nonRecursive = false;
length = Math.max(i + 1, perceivedOsRootLength + 1);
break;
}
}
}
return getDirectoryOfFailedLookupWatch(
Expand Down Expand Up @@ -463,6 +470,7 @@ export function getDirectoryToWatchFailedLookupLocationFromTypeRoot(
rootPath: Path,
rootPathComponents: Readonly<PathPathComponents>,
getCurrentDirectory: () => string | undefined,
preferNonRecursiveWatch: boolean | undefined,
filterCustomPath: (path: Path) => boolean, // Return true if this path can be used
): Path | undefined {
const typeRootPathComponents = getPathComponents(typeRootPath);
Expand All @@ -479,6 +487,7 @@ export function getDirectoryToWatchFailedLookupLocationFromTypeRoot(
typeRootPathComponents.indexOf("node_modules" as Path),
rootPathComponents,
typeRootPathComponents.lastIndexOf("node_modules" as Path),
preferNonRecursiveWatch,
);
return toWatch && filterCustomPath(toWatch.dirPath) ? toWatch.dirPath : undefined;
}
Expand Down Expand Up @@ -1140,6 +1149,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
rootPath,
rootPathComponents,
getCurrentDirectory,
resolutionHost.preferNonRecursiveWatch,
);
if (toWatch) {
const { dir, dirPath, nonRecursive, packageDir, packageDirPath } = toWatch;
Expand Down Expand Up @@ -1354,6 +1364,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
rootPath,
rootPathComponents,
getCurrentDirectory,
resolutionHost.preferNonRecursiveWatch,
);
if (toWatch) {
const { dirPath, packageDirPath } = toWatch;
Expand Down Expand Up @@ -1662,6 +1673,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
rootPath,
rootPathComponents,
getCurrentDirectory,
resolutionHost.preferNonRecursiveWatch,
dirPath => directoryWatchesOfFailedLookups.has(dirPath) || dirPathToSymlinkPackageRefCount.has(dirPath),
);
if (dirPath) {
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,7 @@ export interface System {
*/
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
/**@internal */ preferNonRecursiveWatch?: boolean;
resolvePath(path: string): string;
fileExists(path: string): boolean;
directoryExists(path: string): boolean;
Expand Down Expand Up @@ -1534,6 +1535,7 @@ export let sys: System = (() => {
writeFile,
watchFile,
watchDirectory,
preferNonRecursiveWatch: !fsSupportsRecursiveFsWatch,
resolvePath: path => _path.resolve(path),
fileExists,
directoryExists,
Expand Down
1 change: 1 addition & 0 deletions src/compiler/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusRep
watchDirectory: maybeBind(system, system.watchDirectory) || returnNoopFileWatcher,
setTimeout: maybeBind(system, system.setTimeout) || noop,
clearTimeout: maybeBind(system, system.clearTimeout) || noop,
preferNonRecursiveWatch: system.preferNonRecursiveWatch,
};
}

Expand Down
2 changes: 2 additions & 0 deletions src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export interface WatchHost {
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
preferNonRecursiveWatch?: boolean;
}
export interface ProgramHost<T extends BuilderProgram> {
/**
Expand Down Expand Up @@ -498,6 +499,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
compilerHost.toPath = toPath;
compilerHost.getCompilationSettings = () => compilerOptions!;
compilerHost.useSourceOfProjectReferenceRedirect = maybeBind(host, host.useSourceOfProjectReferenceRedirect);
compilerHost.preferNonRecursiveWatch = host.preferNonRecursiveWatch;
compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.FailedLookupLocations);
compilerHost.watchAffectingFileLocation = (file, cb) => watchFile(file, cb, PollingInterval.High, watchOptions, WatchType.AffectingFileLocation);
compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.TypeRoots);
Expand Down
1 change: 1 addition & 0 deletions src/harness/incrementalUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ function verifyProgram(service: ts.server.ProjectService, project: ts.server.Pro
fileIsOpen: project.fileIsOpen.bind(project),
getCurrentProgram: () => project.getCurrentProgram(),

preferNonRecursiveWatch: project.preferNonRecursiveWatch,
watchDirectoryOfFailedLookupLocation: ts.returnNoopFileWatcher,
watchAffectingFileLocation: ts.returnNoopFileWatcher,
onInvalidatedResolution: ts.noop,
Expand Down
10 changes: 8 additions & 2 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1075,13 +1075,17 @@ function getHostWatcherMap<T>(): HostWatcherMap<T> {
return { idToCallbacks: new Map(), pathToId: new Map() };
}

function getCanUseWatchEvents(service: ProjectService, canUseWatchEvents: boolean | undefined) {
return !!canUseWatchEvents && !!service.eventHandler && !!service.session;
}

function createWatchFactoryHostUsingWatchEvents(service: ProjectService, canUseWatchEvents: boolean | undefined): WatchFactoryHost | undefined {
if (!canUseWatchEvents || !service.eventHandler || !service.session) return undefined;
if (!getCanUseWatchEvents(service, canUseWatchEvents)) return undefined;
const watchedFiles = getHostWatcherMap<FileWatcherCallback>();
const watchedDirectories = getHostWatcherMap<DirectoryWatcherCallback>();
const watchedDirectoriesRecursive = getHostWatcherMap<DirectoryWatcherCallback>();
let ids = 1;
service.session.addProtocolHandler(protocol.CommandTypes.WatchChange, req => {
service.session!.addProtocolHandler(protocol.CommandTypes.WatchChange, req => {
onWatchChange((req as protocol.WatchChangeRequest).arguments);
return { responseRequired: false };
});
Expand Down Expand Up @@ -1326,6 +1330,7 @@ export class ProjectService {
/** @internal */ verifyDocumentRegistry = noop;
/** @internal */ verifyProgram: (project: Project) => void = noop;
/** @internal */ onProjectCreation: (project: Project) => void = noop;
/** @internal */ canUseWatchEvents: boolean;

readonly jsDocParsingMode: JSDocParsingMode | undefined;

Expand Down Expand Up @@ -1397,6 +1402,7 @@ export class ProjectService {
log,
getDetailWatchInfo,
);
this.canUseWatchEvents = getCanUseWatchEvents(this, opts.canUseWatchEvents);
opts.incrementalVerifier?.(this);
}

Expand Down
2 changes: 2 additions & 0 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
protected typeAcquisition: TypeAcquisition | undefined;
/** @internal */
createHash = maybeBind(this.projectService.host, this.projectService.host.createHash);
/** @internal*/ preferNonRecursiveWatch: boolean | undefined;

readonly jsDocParsingMode: JSDocParsingMode | undefined;

Expand Down Expand Up @@ -566,6 +567,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
this.trace = s => host.trace!(s);
}
this.realpath = maybeBind(host, host.realpath);
this.preferNonRecursiveWatch = this.projectService.canUseWatchEvents || host.preferNonRecursiveWatch;

// Use the current directory as resolution root only if the project created using current directory string
this.resolutionCache = createResolutionCache(
Expand Down
1 change: 1 addition & 0 deletions src/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type RequireResult = ModuleImportResult;
export interface ServerHost extends System {
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
preferNonRecursiveWatch?: boolean;
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
clearTimeout(timeoutId: any): void;
setImmediate(callback: (...args: any[]) => void, ...args: any[]): any;
Expand Down
2 changes: 2 additions & 0 deletions src/testRunner/unittests/canWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe("unittests:: canWatch::", () => {
root,
rootPathCompoments,
ts.returnUndefined,
preferNonRecursiveWatch,
);
pushRow(baseline, [testPath, result ? result.packageDir ?? result.dir : "", result ? `${!result.nonRecursive}` : "", result?.packageDir ? result.dir : ""], maxLengths);
});
Expand Down Expand Up @@ -116,6 +117,7 @@ describe("unittests:: canWatch::", () => {
root,
rootPathCompoments,
ts.returnUndefined,
preferNonRecursiveWatch,
ts.returnTrue,
);
pushRow(baseline, [path, result !== undefined ? result : ""], maxLengths);
Expand Down
1 change: 1 addition & 0 deletions src/testRunner/unittests/helpers/tscWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ function verifyProgramStructureAndResolutionCache(
getCompilationSettings: () => options,
fileIsOpen: ts.returnFalse,
getCurrentProgram: () => program,
preferNonRecursiveWatch: sys.preferNonRecursiveWatch,

watchDirectoryOfFailedLookupLocation: ts.returnNoopFileWatcher,
watchAffectingFileLocation: ts.returnNoopFileWatcher,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
watchDirectory: HostWatchDirectory;
service?: server.ProjectService;
osFlavor: TestServerHostOsFlavor;
preferNonRecursiveWatch: boolean;
constructor(
fileOrFolderorSymLinkList: FileOrFolderOrSymLinkMap | readonly FileOrFolderOrSymLink[],
{
Expand Down Expand Up @@ -389,6 +390,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.runWithFallbackPolling = !!runWithFallbackPolling;
const tscWatchFile = this.environmentVariables && this.environmentVariables.get("TSC_WATCHFILE");
const tscWatchDirectory = this.environmentVariables && this.environmentVariables.get("TSC_WATCHDIRECTORY");
this.preferNonRecursiveWatch = this.osFlavor === TestServerHostOsFlavor.Linux;
this.inodeWatching = this.osFlavor !== TestServerHostOsFlavor.Windows;
if (this.inodeWatching) this.inodes = new Map();

Expand All @@ -404,7 +406,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
fileSystemEntryExists: this.fileSystemEntryExists.bind(this),
useCaseSensitiveFileNames: this.useCaseSensitiveFileNames,
getCurrentDirectory: this.getCurrentDirectory.bind(this),
fsSupportsRecursiveFsWatch: this.osFlavor !== TestServerHostOsFlavor.Linux,
fsSupportsRecursiveFsWatch: !this.preferNonRecursiveWatch,
getAccessibleSortedChildDirectories: path => this.getDirectories(path),
realpath: this.realpath.bind(this),
tscWatchFile,
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2649,6 +2649,7 @@ declare namespace ts {
interface ServerHost extends System {
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
preferNonRecursiveWatch?: boolean;
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
clearTimeout(timeoutId: any): void;
setImmediate(callback: (...args: any[]) => void, ...args: any[]): any;
Expand Down Expand Up @@ -9634,6 +9635,7 @@ declare namespace ts {
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
preferNonRecursiveWatch?: boolean;
}
interface ProgramHost<T extends BuilderProgram> {
/**
Expand Down
Loading

0 comments on commit 560ac16

Please sign in to comment.