When there is no change in file text for program, no need to update program (#51626)

* When fsEvent for change is repeated

* When trying to check if program is uptodate, read the files from disk to determine the version instead of delaying so that new program is not created if file contents have not changed
This commit is contained in:
Sheetal Nandi
2022-11-29 12:20:41 -08:00
committed by GitHub
parent af36a859b9
commit c2519bb301
19 changed files with 264 additions and 911 deletions
+1 -1
View File
@@ -436,7 +436,7 @@ function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, ho
// State of the solution
const baseCompilerOptions = getCompilerOptionsOfBuildOptions(options);
const compilerHost = createCompilerHostFromProgramHost(host, () => state.projectCompilerOptions) as CompilerHost & ReadBuildProgramHost;
setGetSourceFileAsHashVersioned(compilerHost, host);
setGetSourceFileAsHashVersioned(compilerHost);
compilerHost.getParsedCommandLine = fileName => parseConfigFile(state, fileName as ResolvedConfigFileName, toResolvedConfigFilePath(state, fileName as ResolvedConfigFileName));
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
+5 -4
View File
@@ -739,9 +739,9 @@ export function createWatchFactory<Y = undefined>(host: WatchFactoryHost & { tra
export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCompilerOptions: () => CompilerOptions, directoryStructureHost: DirectoryStructureHost = host): CompilerHost {
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
const hostGetNewLine = memoize(() => host.getNewLine());
return {
const compilerHost: CompilerHost = {
getSourceFile: createGetSourceFile(
(fileName, encoding) => host.readFile(fileName, encoding),
(fileName, encoding) => !encoding ? compilerHost.readFile(fileName) : host.readFile(fileName, encoding),
getCompilerOptions,
/*setParentNodes*/ undefined
),
@@ -768,6 +768,7 @@ export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCom
disableUseFileVersionAsSignature: host.disableUseFileVersionAsSignature,
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
};
return compilerHost;
}
/** @internal */
@@ -810,12 +811,12 @@ export function getSourceFileVersionAsHashFromText(host: Pick<CompilerHost, "cre
}
/** @internal */
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) {
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost) {
const originalGetSourceFile = compilerHost.getSourceFile;
compilerHost.getSourceFile = (...args) => {
const result = originalGetSourceFile.call(compilerHost, ...args);
if (result) {
result.version = getSourceFileVersionAsHashFromText(host, result.text);
result.version = getSourceFileVersionAsHashFromText(compilerHost, result.text);
}
return result;
};
+11 -6
View File
@@ -48,6 +48,7 @@ import {
getNewLineCharacter,
getNormalizedAbsolutePath,
getParsedCommandLineOfConfigFile,
getSourceFileVersionAsHashFromText,
getTsBuildInfoEmitOutputFilePath,
HasInvalidatedResolutions,
isArray,
@@ -121,7 +122,7 @@ export function createIncrementalCompilerHost(options: CompilerOptions, system =
host.createHash = maybeBind(system, system.createHash);
host.disableUseFileVersionAsSignature = system.disableUseFileVersionAsSignature;
host.storeFilesChangingSignatureDuringEmit = system.storeFilesChangingSignatureDuringEmit;
setGetSourceFileAsHashVersioned(host, system);
setGetSourceFileAsHashVersioned(host);
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
return host;
}
@@ -429,7 +430,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
}
const compilerHost = createCompilerHostFromProgramHost(host, () => compilerOptions, directoryStructureHost) as CompilerHost & ResolutionCacheHost;
setGetSourceFileAsHashVersioned(compilerHost, host);
setGetSourceFileAsHashVersioned(compilerHost);
// Members for CompilerHost
const getNewSourceFile = compilerHost.getSourceFile;
compilerHost.getSourceFile = (fileName, ...args) => getVersionedSourceFileByPath(fileName, toPath(fileName), ...args);
@@ -551,9 +552,9 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
const hasInvalidatedResolutions = resolutionCache.createHasInvalidatedResolutions(customHasInvalidatedResolutions);
const {
originalReadFile, originalFileExists, originalDirectoryExists,
originalCreateDirectory, originalWriteFile,
originalCreateDirectory, originalWriteFile, readFileWithCache
} = changeCompilerHostLikeToUseCache(compilerHost, toPath);
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, path => getSourceVersion(path, readFileWithCache), fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
if (hasChangedConfigFileParsingErrors) {
if (reportFileChangeDetectedOnCreateProgram) {
reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation);
@@ -708,9 +709,13 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
}
}
function getSourceVersion(path: Path): string | undefined {
function getSourceVersion(path: Path, readFileWithCache: (file: string) => string | undefined): string | undefined {
const hostSourceFile = sourceFilesCache.get(path);
return !hostSourceFile || !hostSourceFile.version ? undefined : hostSourceFile.version;
if (!hostSourceFile) return undefined;
if (hostSourceFile.version) return hostSourceFile.version;
// Read file and get new version
const text = readFileWithCache(path);
return text ? getSourceFileVersionAsHashFromText(compilerHost, text) : undefined;
}
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions, hasSourceFileByPath: boolean) {
@@ -702,4 +702,26 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
]
});
});
verifyTscWatch({
scenario,
subScenario: "fsEvent for change is repeated",
commandLineArgs: ["-w", "main.ts", "--extendedDiagnostics"],
sys: () => createWatchedSystem({
"/user/username/projects/project/main.ts": `let a: string = "Hello"`,
[libFile.path]: libFile.content,
}, { currentDirectory: "/user/username/projects/project" }),
changes: [
{
caption: "change main.ts",
change: sys => sys.replaceFileText("/user/username/projects/project/main.ts", "Hello", "Hello World"),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
},
{
caption: "receive another change event without modifying the file",
change: sys => sys.invokeFsWatches("/user/username/projects/project/main.ts", "change", /*modifiedTime*/ undefined, /*useTildeSuffix*/ undefined),
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
}
]
});
});
@@ -686,7 +686,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
}
}
private invokeFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime: Date | undefined, useTildeSuffix: boolean | undefined) {
invokeFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime: Date | undefined, useTildeSuffix: boolean | undefined) {
this.invokeFsWatchesCallbacks(fullPath, eventName, modifiedTime, fullPath, useTildeSuffix);
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, modifiedTime, fullPath, useTildeSuffix);
this.invokeRecursiveFsWatches(fullPath, eventName, modifiedTime, /*entryFullPath*/ undefined, useTildeSuffix);