Skip to content

Commit 68c4a2a

Browse files
authored
Merge pull request #14728 from markjm/markjm/context-timestamps
perf: upgrade watchpack & split timestamp by file/dependency & only call collectTimeInfoEntries once per invalid
2 parents c930a76 + 23e7cb4 commit 68c4a2a

7 files changed

Lines changed: 171 additions & 54 deletions

File tree

lib/WatchIgnorePlugin.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,20 @@ class IgnoringWatchFileSystem {
8787
fileTimestamps.set(path, IGNORE_TIME_ENTRY);
8888
}
8989
return fileTimestamps;
90-
}
90+
},
91+
getInfo:
92+
watcher.getInfo &&
93+
(() => {
94+
const info = watcher.getInfo();
95+
const { fileTimeInfoEntries, contextTimeInfoEntries } = info;
96+
for (const path of ignoredFiles) {
97+
fileTimeInfoEntries.set(path, IGNORE_TIME_ENTRY);
98+
}
99+
for (const path of ignoredDirs) {
100+
contextTimeInfoEntries.set(path, IGNORE_TIME_ENTRY);
101+
}
102+
return info;
103+
})
91104
};
92105
}
93106
}

lib/Watching.js

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -109,30 +109,44 @@ class Watching {
109109
this.lastWatcherStartTime = Date.now();
110110
}
111111
this.compiler.fsStartTime = Date.now();
112-
this._mergeWithCollected(
113-
changedFiles ||
114-
(this.pausedWatcher &&
112+
if (
113+
changedFiles &&
114+
removedFiles &&
115+
fileTimeInfoEntries &&
116+
contextTimeInfoEntries
117+
) {
118+
this._mergeWithCollected(changedFiles, removedFiles);
119+
this.compiler.fileTimestamps = fileTimeInfoEntries;
120+
this.compiler.contextTimestamps = contextTimeInfoEntries;
121+
} else if (this.pausedWatcher) {
122+
if (this.pausedWatcher.getInfo) {
123+
const {
124+
changes,
125+
removals,
126+
fileTimeInfoEntries,
127+
contextTimeInfoEntries
128+
} = this.pausedWatcher.getInfo();
129+
this._mergeWithCollected(changes, removals);
130+
this.compiler.fileTimestamps = fileTimeInfoEntries;
131+
this.compiler.contextTimestamps = contextTimeInfoEntries;
132+
} else {
133+
this._mergeWithCollected(
115134
this.pausedWatcher.getAggregatedChanges &&
116-
this.pausedWatcher.getAggregatedChanges()),
117-
(this.compiler.removedFiles =
118-
removedFiles ||
119-
(this.pausedWatcher &&
135+
this.pausedWatcher.getAggregatedChanges(),
120136
this.pausedWatcher.getAggregatedRemovals &&
121-
this.pausedWatcher.getAggregatedRemovals()))
122-
);
123-
137+
this.pausedWatcher.getAggregatedRemovals()
138+
);
139+
this.compiler.fileTimestamps =
140+
this.pausedWatcher.getFileTimeInfoEntries();
141+
this.compiler.contextTimestamps =
142+
this.pausedWatcher.getContextTimeInfoEntries();
143+
}
144+
}
124145
this.compiler.modifiedFiles = this._collectedChangedFiles;
125146
this._collectedChangedFiles = undefined;
126147
this.compiler.removedFiles = this._collectedRemovedFiles;
127148
this._collectedRemovedFiles = undefined;
128149

129-
this.compiler.fileTimestamps =
130-
fileTimeInfoEntries ||
131-
(this.pausedWatcher && this.pausedWatcher.getFileTimeInfoEntries());
132-
this.compiler.contextTimestamps =
133-
contextTimeInfoEntries ||
134-
(this.pausedWatcher && this.pausedWatcher.getContextTimeInfoEntries());
135-
136150
const run = () => {
137151
if (this.compiler.idle) {
138152
return this.compiler.cache.endIdle(err => {

lib/node/NodeWatchFileSystem.js

Lines changed: 85 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
"use strict";
77

8+
const util = require("util");
89
const Watchpack = require("watchpack");
910

1011
/** @typedef {import("../../declarations/WebpackOptions").WatchOptions} WatchOptions */
@@ -68,7 +69,22 @@ class NodeWatchFileSystem {
6869
if (callbackUndelayed) {
6970
this.watcher.once("change", callbackUndelayed);
7071
}
72+
73+
const fetchTimeInfo = () => {
74+
const fileTimeInfoEntries = new Map();
75+
const contextTimeInfoEntries = new Map();
76+
if (this.watcher) {
77+
this.watcher.collectTimeInfoEntries(
78+
fileTimeInfoEntries,
79+
contextTimeInfoEntries
80+
);
81+
}
82+
return { fileTimeInfoEntries, contextTimeInfoEntries };
83+
};
7184
this.watcher.once("aggregated", (changes, removals) => {
85+
// pause emitting events (avoids clearing aggregated changes and removals on timeout)
86+
this.watcher.pause();
87+
7288
if (this.inputFileSystem && this.inputFileSystem.purge) {
7389
const fs = this.inputFileSystem;
7490
for (const item of changes) {
@@ -78,8 +94,14 @@ class NodeWatchFileSystem {
7894
fs.purge(item);
7995
}
8096
}
81-
const times = this.watcher.getTimeInfoEntries();
82-
callback(null, times, times, changes, removals);
97+
const { fileTimeInfoEntries, contextTimeInfoEntries } = fetchTimeInfo();
98+
callback(
99+
null,
100+
fileTimeInfoEntries,
101+
contextTimeInfoEntries,
102+
changes,
103+
removals
104+
);
83105
});
84106

85107
this.watcher.watch({ files, directories, missing, startTime });
@@ -99,39 +121,71 @@ class NodeWatchFileSystem {
99121
this.watcher.pause();
100122
}
101123
},
102-
getAggregatedRemovals: () => {
103-
const items = this.watcher && this.watcher.aggregatedRemovals;
104-
if (items && this.inputFileSystem && this.inputFileSystem.purge) {
105-
const fs = this.inputFileSystem;
106-
for (const item of items) {
107-
fs.purge(item);
124+
getAggregatedRemovals: util.deprecate(
125+
() => {
126+
const items = this.watcher && this.watcher.aggregatedRemovals;
127+
if (items && this.inputFileSystem && this.inputFileSystem.purge) {
128+
const fs = this.inputFileSystem;
129+
for (const item of items) {
130+
fs.purge(item);
131+
}
108132
}
109-
}
110-
return items;
111-
},
112-
getAggregatedChanges: () => {
113-
const items = this.watcher && this.watcher.aggregatedChanges;
114-
if (items && this.inputFileSystem && this.inputFileSystem.purge) {
133+
return items;
134+
},
135+
"Watcher.getAggregatedRemovals is deprecated in favor of Watcher.getInfo since that's more performant.",
136+
"DEP_WEBPACK_WATCHER_GET_AGGREGATED_REMOVALS"
137+
),
138+
getAggregatedChanges: util.deprecate(
139+
() => {
140+
const items = this.watcher && this.watcher.aggregatedChanges;
141+
if (items && this.inputFileSystem && this.inputFileSystem.purge) {
142+
const fs = this.inputFileSystem;
143+
for (const item of items) {
144+
fs.purge(item);
145+
}
146+
}
147+
return items;
148+
},
149+
"Watcher.getAggregatedChanges is deprecated in favor of Watcher.getInfo since that's more performant.",
150+
"DEP_WEBPACK_WATCHER_GET_AGGREGATED_CHANGES"
151+
),
152+
getFileTimeInfoEntries: util.deprecate(
153+
() => {
154+
return fetchTimeInfo().fileTimeInfoEntries;
155+
},
156+
"Watcher.getFileTimeInfoEntries is deprecated in favor of Watcher.getInfo since that's more performant.",
157+
"DEP_WEBPACK_WATCHER_FILE_TIME_INFO_ENTRIES"
158+
),
159+
getContextTimeInfoEntries: util.deprecate(
160+
() => {
161+
return fetchTimeInfo().contextTimeInfoEntries;
162+
},
163+
"Watcher.getContextTimeInfoEntries is deprecated in favor of Watcher.getInfo since that's more performant.",
164+
"DEP_WEBPACK_WATCHER_CONTEXT_TIME_INFO_ENTRIES"
165+
),
166+
getInfo: () => {
167+
const removals = this.watcher && this.watcher.aggregatedRemovals;
168+
const changes = this.watcher && this.watcher.aggregatedChanges;
169+
if (this.inputFileSystem && this.inputFileSystem.purge) {
115170
const fs = this.inputFileSystem;
116-
for (const item of items) {
117-
fs.purge(item);
171+
if (removals) {
172+
for (const item of removals) {
173+
fs.purge(item);
174+
}
175+
}
176+
if (changes) {
177+
for (const item of changes) {
178+
fs.purge(item);
179+
}
118180
}
119181
}
120-
return items;
121-
},
122-
getFileTimeInfoEntries: () => {
123-
if (this.watcher) {
124-
return this.watcher.getTimeInfoEntries();
125-
} else {
126-
return new Map();
127-
}
128-
},
129-
getContextTimeInfoEntries: () => {
130-
if (this.watcher) {
131-
return this.watcher.getTimeInfoEntries();
132-
} else {
133-
return new Map();
134-
}
182+
const { fileTimeInfoEntries, contextTimeInfoEntries } = fetchTimeInfo();
183+
return {
184+
changes,
185+
removals,
186+
fileTimeInfoEntries,
187+
contextTimeInfoEntries
188+
};
135189
}
136190
};
137191
}

lib/util/fs.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ const path = require("path");
6161
/** @typedef {function((NodeJS.ErrnoException | Error | null)=, any=): void} ReadJsonCallback */
6262
/** @typedef {function((NodeJS.ErrnoException | Error | null)=, IStats|string=): void} LstatReadlinkAbsoluteCallback */
6363

64+
/**
65+
* @typedef {Object} WatcherInfo
66+
* @property {Set<string>} changes get current aggregated changes that have not yet send to callback
67+
* @property {Set<string>} removals get current aggregated removals that have not yet send to callback
68+
* @property {Map<string, FileSystemInfoEntry | "ignore">} fileTimeInfoEntries get info about files
69+
* @property {Map<string, FileSystemInfoEntry | "ignore">} contextTimeInfoEntries get info about directories
70+
*/
71+
72+
// TODO webpack 6 deprecate missing getInfo
6473
/**
6574
* @typedef {Object} Watcher
6675
* @property {function(): void} close closes the watcher and all underlying file watchers
@@ -69,6 +78,7 @@ const path = require("path");
6978
* @property {function(): Set<string>=} getAggregatedRemovals get current aggregated removals that have not yet send to callback
7079
* @property {function(): Map<string, FileSystemInfoEntry | "ignore">} getFileTimeInfoEntries get info about files
7180
* @property {function(): Map<string, FileSystemInfoEntry | "ignore">} getContextTimeInfoEntries get info about directories
81+
* @property {function(): WatcherInfo=} getInfo get info about timestamps and changes
7282
*/
7383

7484
/**

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"schema-utils": "^3.1.0",
2828
"tapable": "^2.1.1",
2929
"terser-webpack-plugin": "^5.1.3",
30-
"watchpack": "^2.2.0",
30+
"watchpack": "^2.3.0",
3131
"webpack-sources": "^3.2.2"
3232
},
3333
"peerDependenciesMeta": {

types.d.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11741,6 +11741,32 @@ declare interface Watcher {
1174111741
* get info about directories
1174211742
*/
1174311743
getContextTimeInfoEntries: () => Map<string, FileSystemInfoEntry | "ignore">;
11744+
11745+
/**
11746+
* get info about timestamps and changes
11747+
*/
11748+
getInfo?: () => WatcherInfo;
11749+
}
11750+
declare interface WatcherInfo {
11751+
/**
11752+
* get current aggregated changes that have not yet send to callback
11753+
*/
11754+
changes: Set<string>;
11755+
11756+
/**
11757+
* get current aggregated removals that have not yet send to callback
11758+
*/
11759+
removals: Set<string>;
11760+
11761+
/**
11762+
* get info about files
11763+
*/
11764+
fileTimeInfoEntries: Map<string, FileSystemInfoEntry | "ignore">;
11765+
11766+
/**
11767+
* get info about directories
11768+
*/
11769+
contextTimeInfoEntries: Map<string, FileSystemInfoEntry | "ignore">;
1174411770
}
1174511771
declare abstract class Watching {
1174611772
startTime: null | number;

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6074,10 +6074,10 @@ wast-loader@^1.11.0:
60746074
dependencies:
60756075
wabt "1.0.0-nightly.20180421"
60766076

6077-
watchpack@^2.2.0:
6078-
version "2.2.0"
6079-
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce"
6080-
integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==
6077+
watchpack@^2.3.0:
6078+
version "2.3.0"
6079+
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.0.tgz#a41bca3da6afaff31e92a433f4c856a0c25ea0c4"
6080+
integrity sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==
60816081
dependencies:
60826082
glob-to-regexp "^0.4.1"
60836083
graceful-fs "^4.1.2"

0 commit comments

Comments
 (0)