-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Delay "File change detected" reporting until createProgram #47427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
590166a
a2c9d9c
7f5f56e
3c844cf
e59d8f5
582a8da
67e8cff
8b21e9b
d5d0bc6
e7fc25d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -273,6 +273,7 @@ namespace ts { | |
let sharedExtendedConfigFileWatchers: ESMap<Path, SharedExtendedConfigFileWatcher<Path>>; // Map of file watchers for extended files, shared between different referenced projects | ||
let extendedConfigCache = host.extendedConfigCache; // Cache for extended config evaluation | ||
let changesAffectResolution = false; // Flag for indicating non-config changes affect module resolution | ||
let reportFileChangeDetectedOnCreateProgram = false; // True if synchronizeProgram should report "File change detected..." when a new program is created | ||
|
||
const sourceFilesCache = new Map<string, HostFileInfo>(); // Cache that stores the source file and version info | ||
let missingFilePathsRequestedForRelease: Path[] | undefined; // These paths are held temporarily so that we can remove the entry from source file cache if the file is not tracked by missing files | ||
|
@@ -434,15 +435,22 @@ namespace ts { | |
const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution || changesAffectResolution); | ||
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) { | ||
if (hasChangedConfigFileParsingErrors) { | ||
if (reportFileChangeDetectedOnCreateProgram) { | ||
reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation); | ||
} | ||
builderProgram = createProgram(/*rootNames*/ undefined, /*options*/ undefined, compilerHost, builderProgram, configFileParsingDiagnostics, projectReferences); | ||
hasChangedConfigFileParsingErrors = false; | ||
} | ||
} | ||
else { | ||
if (reportFileChangeDetectedOnCreateProgram) { | ||
reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation); | ||
} | ||
createNewProgram(hasInvalidatedResolution); | ||
} | ||
|
||
changesAffectResolution = false; // reset for next sync | ||
reportFileChangeDetectedOnCreateProgram = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be cleared in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can clear it there instead, but I had been modelling My main concern was a case where it may be left over for another synchronize later, but I don't know why I thought that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally, I think it's clearer to set it and call it in the same place, but I'm also fine with following local conventions. If this is where things are cleared after sync, then go for it. |
||
|
||
if (host.afterProgramCreate && program !== builderProgram) { | ||
host.afterProgramCreate(builderProgram); | ||
|
@@ -665,7 +673,7 @@ namespace ts { | |
|
||
function updateProgramWithWatchStatus() { | ||
timerToUpdateProgram = undefined; | ||
reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation); | ||
reportFileChangeDetectedOnCreateProgram = true; | ||
updateProgram(); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,9 +84,6 @@ Input:: | |
//// [/a/b/tsconfig.json] deleted | ||
|
||
Output:: | ||
>> Screen clear | ||
[[90m12:00:24 AM[0m] File change detected. Starting incremental compilation... | ||
|
||
[91merror[0m[90m TS5083: [0mCannot read file '/a/b/tsconfig.json'. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's hard to tell in the diff here, but after this message is printed, the process exits (see exitCode below), so it's sort of pointless if this message is printed or not here other than to maybe indicate that we noticed because of a file watcher update. This same error is printed if we start with a missing config file in the first place. |
||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
Input:: | ||
//// [/a/b/f1.ts] | ||
let x = 1 | ||
|
||
//// [/a/b/f2.ts] | ||
let y = 1 | ||
|
||
//// [/a/lib/lib.d.ts] | ||
/// <reference no-default-lib="true"/> | ||
interface Boolean {} | ||
interface Function {} | ||
interface CallableFunction {} | ||
interface NewableFunction {} | ||
interface IArguments {} | ||
interface Number { toExponential: any; } | ||
interface Object {} | ||
interface RegExp {} | ||
interface String { charAt: any; } | ||
interface Array<T> { length: number; [n: number]: T; } | ||
|
||
//// [/a/b/tsconfig.json] | ||
{"compilerOptions":{},"files":["f1.ts","f2.ts"]} | ||
|
||
|
||
/a/lib/tsc.js -w -p /a/b/tsconfig.json | ||
Output:: | ||
>> Screen clear | ||
[[90m12:00:17 AM[0m] Starting compilation in watch mode... | ||
|
||
[[90m12:00:22 AM[0m] Found 0 errors. Watching for file changes. | ||
|
||
|
||
|
||
Program root files: ["/a/b/f1.ts","/a/b/f2.ts"] | ||
Program options: {"watch":true,"project":"/a/b/tsconfig.json","configFilePath":"/a/b/tsconfig.json"} | ||
Program structureReused: Not | ||
Program files:: | ||
/a/lib/lib.d.ts | ||
/a/b/f1.ts | ||
/a/b/f2.ts | ||
|
||
Semantic diagnostics in builder refreshed for:: | ||
/a/lib/lib.d.ts | ||
/a/b/f1.ts | ||
/a/b/f2.ts | ||
|
||
Shape signatures in builder refreshed for:: | ||
/a/lib/lib.d.ts (used version) | ||
/a/b/f1.ts (used version) | ||
/a/b/f2.ts (used version) | ||
|
||
WatchedFiles:: | ||
/a/b/tsconfig.json: | ||
{"fileName":"/a/b/tsconfig.json","pollingInterval":250} | ||
/a/b/f1.ts: | ||
{"fileName":"/a/b/f1.ts","pollingInterval":250} | ||
/a/b/f2.ts: | ||
{"fileName":"/a/b/f2.ts","pollingInterval":250} | ||
/a/lib/lib.d.ts: | ||
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250} | ||
|
||
FsWatches:: | ||
|
||
FsWatchesRecursive:: | ||
/a/b/node_modules/@types: | ||
{"directoryName":"/a/b/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} | ||
|
||
exitCode:: ExitStatus.undefined | ||
|
||
//// [/a/b/f1.js] | ||
var x = 1; | ||
|
||
|
||
//// [/a/b/f2.js] | ||
var y = 1; | ||
|
||
|
||
|
||
Change:: Delete f2 | ||
|
||
Input:: | ||
//// [/a/b/f2.ts] deleted | ||
|
||
Output:: | ||
>> Screen clear | ||
[[90m12:00:24 AM[0m] File change detected. Starting incremental compilation... | ||
|
||
[91merror[0m[90m TS6053: [0mFile '/a/b/f2.ts' not found. | ||
The file is in the program because: | ||
Part of 'files' list in tsconfig.json | ||
|
||
[96ma/b/tsconfig.json[0m:[93m1[0m:[93m40[0m | ||
[7m1[0m {"compilerOptions":{},"files":["f1.ts","f2.ts"]} | ||
[7m [0m [96m ~~~~~~~[0m | ||
File is matched by 'files' list specified here. | ||
|
||
[[90m12:00:28 AM[0m] Found 1 error. Watching for file changes. | ||
|
||
|
||
|
||
Program root files: ["/a/b/f1.ts","/a/b/f2.ts"] | ||
Program options: {"watch":true,"project":"/a/b/tsconfig.json","configFilePath":"/a/b/tsconfig.json"} | ||
Program structureReused: Not | ||
Program files:: | ||
/a/lib/lib.d.ts | ||
/a/b/f1.ts | ||
|
||
No cached semantic diagnostics in the builder:: | ||
|
||
Shape signatures in builder refreshed for:: | ||
/a/b/f1.ts (computed .d.ts) | ||
|
||
WatchedFiles:: | ||
/a/b/tsconfig.json: | ||
{"fileName":"/a/b/tsconfig.json","pollingInterval":250} | ||
/a/b/f1.ts: | ||
{"fileName":"/a/b/f1.ts","pollingInterval":250} | ||
/a/lib/lib.d.ts: | ||
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250} | ||
/a/b/f2.ts: | ||
{"fileName":"/a/b/f2.ts","pollingInterval":250} | ||
|
||
FsWatches:: | ||
|
||
FsWatchesRecursive:: | ||
/a/b/node_modules/@types: | ||
{"directoryName":"/a/b/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} | ||
|
||
exitCode:: ExitStatus.undefined | ||
|
||
//// [/a/b/f1.js] file written with same contents |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
Input:: | ||
//// [/user/username/projects/myproject/index.ts] | ||
|
||
|
||
//// [/user/username/projects/myproject/tsconfig.json] | ||
{} | ||
|
||
//// [/a/lib/lib.d.ts] | ||
/// <reference no-default-lib="true"/> | ||
interface Boolean {} | ||
interface Function {} | ||
interface CallableFunction {} | ||
interface NewableFunction {} | ||
interface IArguments {} | ||
interface Number { toExponential: any; } | ||
interface Object {} | ||
interface RegExp {} | ||
interface String { charAt: any; } | ||
interface Array<T> { length: number; [n: number]: T; } | ||
|
||
|
||
/a/lib/tsc.js -w -p . --extendedDiagnostics | ||
Output:: | ||
[[90m12:00:21 AM[0m] Starting compilation in watch mode... | ||
|
||
Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false | ||
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Config file | ||
Synchronizing program | ||
CreatingProgramWith:: | ||
roots: ["/user/username/projects/myproject/index.ts"] | ||
options: {"watch":true,"project":"/user/username/projects/myproject","extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} | ||
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/index.ts 250 undefined Source file | ||
FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 undefined Source file | ||
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots | ||
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots | ||
[[90m12:00:24 AM[0m] Found 0 errors. Watching for file changes. | ||
|
||
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory | ||
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory | ||
|
||
|
||
Program root files: ["/user/username/projects/myproject/index.ts"] | ||
Program options: {"watch":true,"project":"/user/username/projects/myproject","extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} | ||
Program structureReused: Not | ||
Program files:: | ||
/a/lib/lib.d.ts | ||
/user/username/projects/myproject/index.ts | ||
|
||
Semantic diagnostics in builder refreshed for:: | ||
/a/lib/lib.d.ts | ||
/user/username/projects/myproject/index.ts | ||
|
||
Shape signatures in builder refreshed for:: | ||
/a/lib/lib.d.ts (used version) | ||
/user/username/projects/myproject/index.ts (used version) | ||
|
||
WatchedFiles:: | ||
/user/username/projects/myproject/tsconfig.json: | ||
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250} | ||
/user/username/projects/myproject/index.ts: | ||
{"fileName":"/user/username/projects/myproject/index.ts","pollingInterval":250} | ||
/a/lib/lib.d.ts: | ||
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250} | ||
|
||
FsWatches:: | ||
|
||
FsWatchesRecursive:: | ||
/user/username/projects/myproject/node_modules/@types: | ||
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} | ||
/user/username/projects/myproject: | ||
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} | ||
|
||
exitCode:: ExitStatus.undefined | ||
|
||
//// [/user/username/projects/myproject/index.js] | ||
|
||
|
||
|
||
Change:: Create foo in project root | ||
|
||
Input:: | ||
//// [/user/username/projects/myproject/foo] | ||
|
||
|
||
|
||
Output:: | ||
DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory | ||
Scheduling update | ||
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory | ||
Reloading new file names and options | ||
Synchronizing program | ||
|
||
|
||
WatchedFiles:: | ||
/user/username/projects/myproject/tsconfig.json: | ||
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250} | ||
/user/username/projects/myproject/index.ts: | ||
{"fileName":"/user/username/projects/myproject/index.ts","pollingInterval":250} | ||
/a/lib/lib.d.ts: | ||
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250} | ||
|
||
FsWatches:: | ||
|
||
FsWatchesRecursive:: | ||
/user/username/projects/myproject/node_modules/@types: | ||
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} | ||
/user/username/projects/myproject: | ||
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} | ||
|
||
exitCode:: ExitStatus.undefined | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much work is done between where this used to be reported and here? I'm concerned that it will look like the compiler is frozen if it reacts "late" to a file change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question; my skimming says that
isProgramUptoDate
will:I'm not sure that it's a huge amount of work.
I could try and have it unconditionally run
afterProgramCreate
, but based on #37266, it seems like this was an intentional change (but, hard to tell entirely, given it's one part of a lot of other tweaks). I'll try it and see if anything breaks catastrophically, but I don't know exactly how to double check that I don't undo a perf fix.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we convinced ourselves that it should be cheap. I'm fine bugging this into shape if we get complaints.