From 6cbde7091233425e1e8ea384bc773021c4d5b8ab Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 13 Jun 2024 14:11:27 -0700 Subject: [PATCH] Temp --- src/compiler/tsbuildPublic.ts | 170 ++++++++++++++++++++------------- src/compiler/watchPublic.ts | 4 +- src/compiler/watchUtilities.ts | 51 +++++++--- src/server/editorServices.ts | 4 +- 4 files changed, 142 insertions(+), 87 deletions(-) diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 6213b9f63d0..18818b5a59d 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -1,6 +1,5 @@ import { AffectedFileResult, - arrayFrom, assertType, BuilderProgram, BuildInfo, @@ -10,7 +9,6 @@ import { clearMap, closeFileWatcher, closeFileWatcherOf, - combinePaths, commonOptionsWithBuild, CompilerHost, CompilerOptions, @@ -78,7 +76,6 @@ import { isIgnoredFileFromWildCardWatching, isIncrementalBuildInfo, isIncrementalCompilation, - isPackageJsonInfo, loadWithModeAwareCache, maybeBind, missingFileModifiedTime, @@ -105,7 +102,7 @@ import { returnUndefined, SemanticDiagnosticsBuilderProgram, setGetSourceFileAsHashVersioned, - SharedExtendedConfigFileWatcher, + SharedFileWatcher, SourceFile, Status, sys, @@ -115,6 +112,7 @@ import { unorderedRemoveItem, updateErrorForNoInputFiles, updateSharedExtendedConfigFileWatcher, + updateSharedFileWatcher, updateWatchingWildcardDirectories, UpToDateStatus, UpToDateStatusType, @@ -370,7 +368,7 @@ interface SolutionBuilderState extends WatchFactory; + readonly resolvedConfigFilePaths: Map; readonly configFileCache: Map; /** Map from config file name to up-to-date status */ readonly projectStatus: Map; @@ -401,13 +399,11 @@ interface SolutionBuilderState extends WatchFactory>; readonly allWatchedInputFiles: Map>; readonly allWatchedConfigFiles: Map; - readonly allWatchedExtendedConfigFiles: Map>; - readonly allWatchedPackageJsonFiles: Map>; + readonly allWatchedExtendedConfigFiles: Map>; + readonly allWatchedPackageJsonFiles: Map>; readonly filesWatched: Map; readonly outputTimeStamps: Map>; - readonly lastCachedPackageJsonLookups: Map | undefined>; - timerToBuildInvalidatedProject: any; reportFileChangeDetected: boolean; writeLog: (s: string) => void; @@ -527,8 +523,6 @@ function createSolutionBuilderState(watch: boolean, ho allWatchedPackageJsonFiles: new Map(), filesWatched: new Map(), - lastCachedPackageJsonLookups: new Map(), - timerToBuildInvalidatedProject: undefined, reportFileChangeDetected: false, watchFile, @@ -666,7 +660,6 @@ function createStateBuildOrder(state: SolutionBuilderS mutateMapSkippingNewValues(state.projectErrorsReported, currentProjects, noopOnDelete); mutateMapSkippingNewValues(state.buildInfoCache, currentProjects, noopOnDelete); mutateMapSkippingNewValues(state.outputTimeStamps, currentProjects, noopOnDelete); - mutateMapSkippingNewValues(state.lastCachedPackageJsonLookups, currentProjects, noopOnDelete); // Remove watches for the program no longer in the solution if (state.watch) { @@ -676,14 +669,7 @@ function createStateBuildOrder(state: SolutionBuilderS { onDeleteValue: closeFileWatcher }, ); - state.allWatchedExtendedConfigFiles.forEach(watcher => { - watcher.projects.forEach(project => { - if (!currentProjects.has(project)) { - watcher.projects.delete(project); - } - }); - watcher.close(); - }); + state.allWatchedExtendedConfigFiles.forEach(closeSharedWatcherOfUnknownProjects); mutateMapSkippingNewValues( state.allWatchedWildcardDirectories, @@ -697,13 +683,18 @@ function createStateBuildOrder(state: SolutionBuilderS { onDeleteValue: existingMap => existingMap.forEach(closeFileWatcher) }, ); - mutateMapSkippingNewValues( - state.allWatchedPackageJsonFiles, - currentProjects, - { onDeleteValue: existingMap => existingMap.forEach(closeFileWatcher) }, - ); + state.allWatchedPackageJsonFiles.forEach(closeSharedWatcherOfUnknownProjects); } return state.buildOrder = buildOrder; + + function closeSharedWatcherOfUnknownProjects(watcher: SharedFileWatcher) { + watcher.projects.forEach(project => { + if (!currentProjects.has(project)) { + watcher.projects.delete(project); + } + }); + watcher.close(); + } } function getBuildOrderFor(state: SolutionBuilderState, project: string | undefined, onlyReferences: boolean | undefined): AnyBuildOrder | undefined { @@ -1041,20 +1032,7 @@ function createBuildOrUpdateInvalidedProject( getConfigFileParsingDiagnostics(config), config.projectReferences, ); - if (state.watch) { - const internalMap = state.moduleResolutionCache?.getPackageJsonInfoCache().getInternalMap(); - state.lastCachedPackageJsonLookups.set( - projectPath, - internalMap && new Set(arrayFrom( - internalMap.values(), - data => - state.host.realpath && (isPackageJsonInfo(data) || data.directoryExists) ? - state.host.realpath(combinePaths(data.packageDirectory, "package.json")) : - combinePaths(data.packageDirectory, "package.json"), - )), - ); - state.builderPrograms.set(projectPath, program); - } + if (state.watch) state.builderPrograms.set(projectPath, program); step++; } @@ -1226,7 +1204,6 @@ function getNextInvalidatedProjectCreateInfo( config.fileNames = getFileNamesFromConfigSpecs(config.options.configFile!.configFileSpecs!, getDirectoryPath(project), config.options, state.parseConfigFileHost); updateErrorForNoInputFiles(config.fileNames, project, config.options.configFile!.configFileSpecs!, config.errors, canJsonReportNoInputFiles(config.raw)); watchInputFiles(state, project, projectPath, config); - watchPackageJsonFiles(state, project, projectPath, config); } const status = getUpToDateStatus(state, config, projectPath); @@ -1736,6 +1713,8 @@ function getUpToDateStatusWorker(state: SolutionBuilde if (extendedConfigStatus) return extendedConfigStatus; // Check package file time + buildInfoCacheEntry ??= state.buildInfoCache.get(resolvedPath)!; + // TODO: sheetal how to calculate packageJsons - do we store in buildInfo only for tsc -b or always? const packageJsonLookups = state.lastCachedPackageJsonLookups.get(resolvedPath); const dependentPackageFileStatus = packageJsonLookups && forEachKey( packageJsonLookups, @@ -1958,7 +1937,11 @@ function clean(state: SolutionBuilderState, project return result; } -function cleanWorker(state: SolutionBuilderState, project: string | undefined, onlyReferences: boolean | undefined) { +function cleanWorker( + state: SolutionBuilderState, + project: string | undefined, + onlyReferences: boolean | undefined, +) { const buildOrder = getBuildOrderFor(state, project, onlyReferences); if (!buildOrder) return ExitStatus.InvalidProject_OutputsSkipped; @@ -2002,7 +1985,11 @@ function cleanWorker(state: SolutionBuilderState, p return ExitStatus.Success; } -function invalidateProject(state: SolutionBuilderState, resolved: ResolvedConfigFilePath, updateLevel: ProgramUpdateLevel) { +function invalidateProject( + state: SolutionBuilderState, + resolved: ResolvedConfigFilePath, + updateLevel: ProgramUpdateLevel, +) { // If host implements getParsedCommandLine, we cant get list of files from parseConfigFileHost if (state.host.getParsedCommandLine && updateLevel === ProgramUpdateLevel.RootNamesAndUpdate) { updateLevel = ProgramUpdateLevel.Full; @@ -2017,13 +2004,35 @@ function invalidateProject(state: SolutionBuilderState enableCache(state); } -function invalidateProjectAndScheduleBuilds(state: SolutionBuilderState, resolvedPath: ResolvedConfigFilePath, updateLevel: ProgramUpdateLevel) { +function invalidateProjectAndScheduleBuilds( + state: SolutionBuilderState, + resolvedPath: ResolvedConfigFilePath, + updateLevel: ProgramUpdateLevel, +) { state.reportFileChangeDetected = true; invalidateProject(state, resolvedPath, updateLevel); scheduleBuildInvalidatedProject(state, 250, /*changeDetected*/ true); } -function scheduleBuildInvalidatedProject(state: SolutionBuilderState, time: number, changeDetected: boolean) { +function invalidateProjectAndScheduledBuildsOfSharedFileWacher( + state: SolutionBuilderState, + sharedWatcher: SharedFileWatcher | undefined, + reloadLevel: ProgramUpdateLevel, +) { + sharedWatcher?.projects.forEach(projectConfigFilePath => + invalidateProjectAndScheduleBuilds( + state, + projectConfigFilePath, + reloadLevel, + ) + ); +} + +function scheduleBuildInvalidatedProject( + state: SolutionBuilderState, + time: number, + changeDetected: boolean, +) { const { hostWithWatch } = state; if (!hostWithWatch.setTimeout || !hostWithWatch.clearTimeout) { return; @@ -2034,7 +2043,11 @@ function scheduleBuildInvalidatedProject(state: Soluti state.timerToBuildInvalidatedProject = hostWithWatch.setTimeout(buildNextInvalidatedProject, time, "timerToBuildInvalidatedProject", state, changeDetected); } -function buildNextInvalidatedProject(_timeoutType: string, state: SolutionBuilderState, changeDetected: boolean) { +function buildNextInvalidatedProject( + _timeoutType: string, + state: SolutionBuilderState, + changeDetected: boolean, +) { performance.mark("SolutionBuilder::beforeBuild"); const buildOrder = buildNextInvalidatedProjectWorker(state, changeDetected); performance.mark("SolutionBuilder::afterBuild"); @@ -2042,7 +2055,10 @@ function buildNextInvalidatedProject(_timeoutType: str if (buildOrder) reportErrorSummary(state, buildOrder); } -function buildNextInvalidatedProjectWorker(state: SolutionBuilderState, changeDetected: boolean) { +function buildNextInvalidatedProjectWorker( + state: SolutionBuilderState, + changeDetected: boolean, +) { state.timerToBuildInvalidatedProject = undefined; if (state.reportFileChangeDetected) { state.reportFileChangeDetected = false; @@ -2075,7 +2091,12 @@ function buildNextInvalidatedProjectWorker(state: Solu return buildOrder; } -function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) { +function watchConfigFile( + state: SolutionBuilderState, + resolved: ResolvedConfigFileName, + resolvedPath: ResolvedConfigFilePath, + parsed: ParsedCommandLine | undefined, +) { if (!state.watch || state.allWatchedConfigFiles.has(resolvedPath)) return; state.allWatchedConfigFiles.set( resolvedPath, @@ -2091,7 +2112,11 @@ function watchConfigFile(state: SolutionBuilderState(state: SolutionBuilderState, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) { +function watchExtendedConfigFiles( + state: SolutionBuilderState, + resolvedPath: ResolvedConfigFilePath, + parsed: ParsedCommandLine | undefined, +) { updateSharedExtendedConfigFileWatcher( resolvedPath, parsed?.options, @@ -2100,12 +2125,17 @@ function watchExtendedConfigFiles(state: SolutionBuild watchFile( state, extendedConfigFileName, - () => state.allWatchedExtendedConfigFiles.get(extendedConfigFilePath)?.projects.forEach(projectConfigFilePath => invalidateProjectAndScheduleBuilds(state, projectConfigFilePath, ProgramUpdateLevel.Full)), + () => + invalidateProjectAndScheduledBuildsOfSharedFileWacher( + state, + state.allWatchedExtendedConfigFiles.get(extendedConfigFilePath), + ProgramUpdateLevel.Full, + ), PollingInterval.High, parsed?.watchOptions, WatchType.ExtendedConfigFile, ), - fileName => toPath(state, fileName), + fileName => toResolvedConfigFilePath(state, fileName as ResolvedConfigFileName), ); } @@ -2165,23 +2195,27 @@ function watchInputFiles(state: SolutionBuilderState(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine) { - if (!state.watch || !state.lastCachedPackageJsonLookups) return; - mutateMap( - getOrCreateValueMapFromConfigFileMap(state.allWatchedPackageJsonFiles, resolvedPath), - state.lastCachedPackageJsonLookups.get(resolvedPath), - { - createNewValue: input => - watchFile( - state, - input, - () => invalidateProjectAndScheduleBuilds(state, resolvedPath, ProgramUpdateLevel.Update), - PollingInterval.High, - parsed?.watchOptions, - WatchType.PackageJson, - resolved, - ), - onDeleteValue: closeFileWatcher, - }, + if (!state.watch) return; + updateSharedFileWatcher( + resolvedPath, + getPackageJsonDependencies(state, resolvedPath, parsed), + state.allWatchedPackageJsonFiles, + (file, path) => + watchFile( + state, + file, + () => + invalidateProjectAndScheduledBuildsOfSharedFileWacher( + state, + state.allWatchedPackageJsonFiles.get(path), + ProgramUpdateLevel.Update, + ), + PollingInterval.High, + parsed?.watchOptions, + WatchType.PackageJson, + resolved, + ), + fileName => toResolvedConfigFilePath(state, fileName as ResolvedConfigFileName), ); } @@ -2215,7 +2249,7 @@ function stopWatching(state: SolutionBuilderState) clearMap(state.allWatchedExtendedConfigFiles, closeFileWatcherOf); clearMap(state.allWatchedWildcardDirectories, watchedWildcardDirectories => clearMap(watchedWildcardDirectories, closeFileWatcherOf)); clearMap(state.allWatchedInputFiles, watchedWildcardDirectories => clearMap(watchedWildcardDirectories, closeFileWatcher)); - clearMap(state.allWatchedPackageJsonFiles, watchedPacageJsonFiles => clearMap(watchedPacageJsonFiles, closeFileWatcher)); + clearMap(state.allWatchedPackageJsonFiles, closeFileWatcherOf); } /** diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index 95c14ed1580..2439fe394a1 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -77,7 +77,7 @@ import { returnTrue, ScriptTarget, setGetSourceFileAsHashVersioned, - SharedExtendedConfigFileWatcher, + SharedFileWatcher, SourceFile, StringLiteralLike, sys, @@ -437,7 +437,7 @@ export function createWatchProgram(host: WatchCompiler let timerToUpdateProgram: any; // timer callback to recompile the program let timerToInvalidateFailedLookupResolutions: any; // timer callback to invalidate resolutions for changes in failed lookup locations let parsedConfigs: Map | undefined; // Parsed commandline and watching cached for referenced projects - let sharedExtendedConfigFileWatchers: Map>; // Map of file watchers for extended files, shared between different referenced projects + let sharedExtendedConfigFileWatchers: Map>; // Map of file watchers for extended files, shared between different referenced projects let extendedConfigCache = host.extendedConfigCache; // Cache for extended config evaluation let reportFileChangeDetectedOnCreateProgram = false; // True if synchronizeProgram should report "File change detected..." when a new program is created diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 433c34ad96d..c8699f236ee 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -42,6 +42,7 @@ import { noop, normalizePath, Path, + Path as ts_Path, PollingInterval, Program, removeFileExtension, @@ -382,7 +383,7 @@ export enum ProgramUpdateLevel { } /** @internal */ -export interface SharedExtendedConfigFileWatcher extends FileWatcher { +export interface SharedFileWatcher extends FileWatcher { watcher: FileWatcher; projects: Set; } @@ -392,37 +393,57 @@ export interface SharedExtendedConfigFileWatcher extends FileWatcher { * * @internal */ -export function updateSharedExtendedConfigFileWatcher( +export function updateSharedExtendedConfigFileWatcher( projectPath: T, options: CompilerOptions | undefined, - extendedConfigFilesMap: Map>, + extendedConfigFilesMap: Map>, createExtendedConfigFileWatch: (extendedConfigPath: string, extendedConfigFilePath: Path) => FileWatcher, toPath: (fileName: string) => Path, ) { - const extendedConfigs = arrayToMap(options?.configFile?.extendedSourceFiles || emptyArray, toPath); + return updateSharedFileWatcher( + projectPath, + options?.configFile?.extendedSourceFiles || emptyArray, + extendedConfigFilesMap, + createExtendedConfigFileWatch, + toPath, + ); +} + +/** + * Updates the map of shared extended config file watches with a new set of extended config files from a base config file of the project + * @internal + */ +export function updateSharedFileWatcher( + projectPath: T, + filesToWatch: readonly string[], + fileWatchersMap: Map>, + createFileWatcher: (file: string, path: Path) => FileWatcher, + toPath: (fileName: string) => Path, +) { + const filesToWatchMap = arrayToMap(filesToWatch, toPath); // remove project from all unrelated watchers - extendedConfigFilesMap.forEach((watcher, extendedConfigFilePath) => { - if (!extendedConfigs.has(extendedConfigFilePath)) { + fileWatchersMap.forEach((watcher, fileToWatchPath) => { + if (!filesToWatchMap.has(fileToWatchPath)) { watcher.projects.delete(projectPath); watcher.close(); } }); - // Update the extended config files watcher - extendedConfigs.forEach((extendedConfigFileName, extendedConfigFilePath) => { - const existing = extendedConfigFilesMap.get(extendedConfigFilePath); + // Update the files watcher + filesToWatchMap.forEach((fileToWatch, fileToWatchPath) => { + const existing = fileWatchersMap.get(fileToWatchPath); if (existing) { existing.projects.add(projectPath); } else { - // start watching previously unseen extended config - extendedConfigFilesMap.set(extendedConfigFilePath, { + // start watching previously unseen file + fileWatchersMap.set(fileToWatchPath, { projects: new Set([projectPath]), - watcher: createExtendedConfigFileWatch(extendedConfigFileName, extendedConfigFilePath), + watcher: createFileWatcher(fileToWatch, fileToWatchPath), close: () => { - const existing = extendedConfigFilesMap.get(extendedConfigFilePath); + const existing = fileWatchersMap.get(fileToWatchPath); if (!existing || existing.projects.size !== 0) return; existing.watcher.close(); - extendedConfigFilesMap.delete(extendedConfigFilePath); + fileWatchersMap.delete(fileToWatchPath); }, }); } @@ -436,7 +457,7 @@ export function updateSharedExtendedConfigFileWatcher( */ export function clearSharedExtendedConfigFileWatcher( projectPath: T, - extendedConfigFilesMap: Map>, + extendedConfigFilesMap: Map>, ) { extendedConfigFilesMap.forEach(watcher => { if (watcher.projects.delete(projectPath)) watcher.close(); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index ae10458e5ad..f5199dd072e 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -107,7 +107,7 @@ import { returnFalse, returnNoopFileWatcher, ScriptKind, - SharedExtendedConfigFileWatcher, + SharedFileWatcher, some, SourceFile, SourceFileLike, @@ -1269,7 +1269,7 @@ export class ProjectService { readonly watchFactory: WatchFactory; /** @internal */ - private readonly sharedExtendedConfigFileWatchers = new Map>(); + private readonly sharedExtendedConfigFileWatchers = new Map>(); /** @internal */ private readonly extendedConfigCache = new Map();