Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Port of #51626 and #51689 to release-4.9 (#51627)
* 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

* Test for empty string change

* Fix empty string for file versioning
  • Loading branch information
sheetalkamat committed Nov 30, 2022
1 parent 1727912 commit e7a02f4
Show file tree
Hide file tree
Showing 20 changed files with 359 additions and 932 deletions.
4 changes: 2 additions & 2 deletions src/compiler/tsbuildPublic.ts
Expand Up @@ -292,7 +292,7 @@ namespace ts {
// 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);
Expand Down Expand Up @@ -1655,7 +1655,7 @@ namespace ts {
if (!buildInfoVersionMap) buildInfoVersionMap = getBuildInfoFileVersionMap(buildInfoProgram, buildInfoPath!, host);
version = buildInfoVersionMap.get(toPath(state, inputFile));
const text = version ? state.readFileWithCache(inputFile) : undefined;
currentVersion = text && (host.createHash || generateDjb2Hash)(text);
currentVersion = text !== undefined ? (host.createHash || generateDjb2Hash)(text) : undefined;
if (version && version === currentVersion) pseudoInputUpToDate = true;
}

Expand Down
10 changes: 6 additions & 4 deletions src/compiler/watch.ts
Expand Up @@ -595,12 +595,13 @@ namespace ts {
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: (fileName, languageVersionOrOptions, onError) => {
let text: string | undefined;
try {
performance.mark("beforeIORead");
text = host.readFile(fileName, getCompilerOptions().charset);
const encoding = getCompilerOptions().charset;
text = !encoding ? compilerHost.readFile(fileName) : host.readFile(fileName, encoding);
performance.mark("afterIORead");
performance.measure("I/O Read", "beforeIORead", "afterIORead");
}
Expand Down Expand Up @@ -632,6 +633,7 @@ namespace ts {
disableUseFileVersionAsSignature: host.disableUseFileVersionAsSignature,
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
};
return compilerHost;

function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
try {
Expand Down Expand Up @@ -659,9 +661,9 @@ namespace ts {
}
}

export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) {
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost) {
const originalGetSourceFile = compilerHost.getSourceFile;
const computeHash = maybeBind(host, host.createHash) || generateDjb2Hash;
const computeHash = maybeBind(compilerHost, compilerHost.createHash) || generateDjb2Hash;
compilerHost.getSourceFile = (...args) => {
const result = originalGetSourceFile.call(compilerHost, ...args);
if (result) {
Expand Down
16 changes: 10 additions & 6 deletions src/compiler/watchPublic.ts
Expand Up @@ -28,7 +28,7 @@ namespace ts {
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;
}
Expand Down Expand Up @@ -330,7 +330,7 @@ namespace ts {
}

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);
Expand Down Expand Up @@ -452,9 +452,9 @@ namespace ts {
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);
Expand Down Expand Up @@ -609,9 +609,13 @@ namespace ts {
}
}

function getSourceVersion(path: Path): string | undefined {
function getSourceVersion(path: Path, readFileWithCache: (fileName: 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 !== undefined ? (compilerHost.createHash || generateDjb2Hash)(text) : undefined;
}

function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions, hasSourceFileByPath: boolean) {
Expand Down
2 changes: 1 addition & 1 deletion src/harness/virtualFileSystemWithWatch.ts
Expand Up @@ -730,7 +730,7 @@ interface Array<T> { length: number; [n: number]: T; }`
}
}

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);
Expand Down
32 changes: 32 additions & 0 deletions src/testRunner/unittests/tscWatch/watchEnvironment.ts
Expand Up @@ -687,5 +687,37 @@ namespace ts.tscWatch {
]
});
});

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 => replaceFileText(sys, "/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(),
},
{
caption: "change main.ts to empty text",
change: sys => sys.writeFile("/user/username/projects/project/main.ts", ""),
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(),
}
]
});
});
}
Expand Up @@ -132,25 +132,11 @@ Output::
>> Screen clear
[12:00:36 AM] File change detected. Starting incremental compilation...

[[90m12:00:37 AM[0m] Project 'tsconfig.json' is out of date because output 'tsconfig.tsbuildinfo' is older than input 'a.js'
[[90m12:00:37 AM[0m] Project 'tsconfig.json' is up to date but needs to update timestamps of output files that are older than input files

[[90m12:00:38 AM[0m] Building project '/user/username/projects/myproject/tsconfig.json'...
[[90m12:00:38 AM[0m] Found 0 errors. Watching for file changes.

[12:00:39 AM] Found 0 errors. Watching for file changes.



Program root files: ["/user/username/projects/myproject/a.js","/user/username/projects/myproject/b.ts"]
Program options: {"allowJs":true,"noEmit":true,"watch":true,"incremental":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/a.js
/user/username/projects/myproject/b.ts

Semantic diagnostics in builder refreshed for::

No shapes updated in the builder::

PolledWatches::

Expand Down Expand Up @@ -178,13 +164,13 @@ const x = 10;

Output::
>> Screen clear
[[90m12:00:43 AM[0m] File change detected. Starting incremental compilation...
[[90m12:00:42 AM[0m] File change detected. Starting incremental compilation...

[[90m12:00:44 AM[0m] Project 'tsconfig.json' is out of date because output 'tsconfig.tsbuildinfo' is older than input 'a.js'
[[90m12:00:43 AM[0m] Project 'tsconfig.json' is out of date because output 'tsconfig.tsbuildinfo' is older than input 'a.js'

[[90m12:00:45 AM[0m] Building project '/user/username/projects/myproject/tsconfig.json'...
[[90m12:00:44 AM[0m] Building project '/user/username/projects/myproject/tsconfig.json'...

[[90m12:00:53 AM[0m] Found 0 errors. Watching for file changes.
[[90m12:00:52 AM[0m] Found 0 errors. Watching for file changes.



Expand Down
Expand Up @@ -179,30 +179,6 @@ Input::
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents

Output::
>> Screen clear
[12:00:43 AM] File change detected. Starting incremental compilation...

src/main.ts:4:1 - error TS1005: ',' expected.
4 ;
  ~
[12:00:44 AM] Found 1 error. Watching for file changes.
Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"incremental":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
Program structureReused: Completely
Program files::
/a/lib/lib.d.ts
/user/username/projects/noEmitOnError/shared/types/db.ts
/user/username/projects/noEmitOnError/src/main.ts
/user/username/projects/noEmitOnError/src/other.ts
Semantic diagnostics in builder refreshed for::
No shapes updated in the builder::

PolledWatches::
/user/username/projects/noemitonerror/node_modules/@types:
Expand Down Expand Up @@ -239,9 +215,9 @@ const a = {

Output::
>> Screen clear
[[90m12:00:48 AM[0m] File change detected. Starting incremental compilation...
[[90m12:00:46 AM[0m] File change detected. Starting incremental compilation...

[[90m12:01:06 AM[0m] Found 0 errors. Watching for file changes.
[[90m12:01:04 AM[0m] Found 0 errors. Watching for file changes.



Expand Down Expand Up @@ -370,14 +346,14 @@ const a: string = 10;

Output::
>> Screen clear
[[90m12:01:13 AM[0m] File change detected. Starting incremental compilation...
[[90m12:01:11 AM[0m] File change detected. Starting incremental compilation...

src/main.ts:2:7 - error TS2322: Type 'number' is not assignable to type 'string'.

2 const a: string = 10;
   ~

[[90m12:01:17 AM[0m] Found 1 error. Watching for file changes.
[[90m12:01:15 AM[0m] Found 1 error. Watching for file changes.



Expand Down Expand Up @@ -501,30 +477,6 @@ Input::
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents

Output::
>> Screen clear
[12:01:25 AM] File change detected. Starting incremental compilation...
src/main.ts:2:7 - error TS2322: Type 'number' is not assignable to type 'string'.

2 const a: string = 10;
   ~

[12:01:26 AM] Found 1 error. Watching for file changes.



Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"incremental":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
Program structureReused: Completely
Program files::
/a/lib/lib.d.ts
/user/username/projects/noEmitOnError/shared/types/db.ts
/user/username/projects/noEmitOnError/src/main.ts
/user/username/projects/noEmitOnError/src/other.ts

Semantic diagnostics in builder refreshed for::

No shapes updated in the builder::

PolledWatches::
/user/username/projects/noemitonerror/node_modules/@types:
Expand Down Expand Up @@ -559,9 +511,9 @@ const a: string = "hello";

Output::
>> Screen clear
[[90m12:01:30 AM[0m] File change detected. Starting incremental compilation...
[[90m12:01:26 AM[0m] File change detected. Starting incremental compilation...

[[90m12:01:37 AM[0m] Found 0 errors. Watching for file changes.
[[90m12:01:33 AM[0m] Found 0 errors. Watching for file changes.



Expand Down Expand Up @@ -673,25 +625,6 @@ Input::
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents

Output::
>> Screen clear
[12:01:44 AM] File change detected. Starting incremental compilation...

[12:01:45 AM] Found 0 errors. Watching for file changes.



Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"incremental":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
Program structureReused: Completely
Program files::
/a/lib/lib.d.ts
/user/username/projects/noEmitOnError/shared/types/db.ts
/user/username/projects/noEmitOnError/src/main.ts
/user/username/projects/noEmitOnError/src/other.ts

Semantic diagnostics in builder refreshed for::

No shapes updated in the builder::

PolledWatches::
/user/username/projects/noemitonerror/node_modules/@types:
Expand Down

0 comments on commit e7a02f4

Please sign in to comment.