@rspack/core 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiled/http-proxy-middleware/index.d.ts +1 -26
- package/compiled/http-proxy-middleware/package.json +1 -1
- package/compiled/watchpack/index.d.ts +2 -218
- package/compiled/watchpack/index.js +1387 -939
- package/compiled/watchpack/package.json +8 -1
- package/compiled/watchpack/types/DirectoryWatcher.d.ts +333 -0
- package/compiled/watchpack/types/LinkResolver.d.ts +10 -0
- package/compiled/watchpack/types/getWatcherManager.d.ts +62 -0
- package/compiled/watchpack/types/index.d.ts +261 -0
- package/compiled/watchpack/types/reducePlan.d.ts +34 -0
- package/compiled/watchpack/types/watchEventSource.d.ts +53 -0
- package/compiled/watchpack/types/watchpack.d.ts +2 -0
- package/dist/Compilation.d.ts +2 -2
- package/dist/builtin-plugin/rsc/RscServerPlugin.d.ts +1 -1
- package/dist/builtin-plugin/rsc/index.d.ts +4 -4
- package/dist/index.js +29 -20
- package/dist/stats/statsFactoryUtils.d.ts +3 -3
- package/package.json +5 -6
|
@@ -1111,7 +1111,7 @@ function patch (fs) {
|
|
|
1111
1111
|
|
|
1112
1112
|
/***/ }),
|
|
1113
1113
|
|
|
1114
|
-
/***/
|
|
1114
|
+
/***/ 600:
|
|
1115
1115
|
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
|
1116
1116
|
|
|
1117
1117
|
"use strict";
|
|
@@ -1121,12 +1121,23 @@ function patch (fs) {
|
|
|
1121
1121
|
*/
|
|
1122
1122
|
|
|
1123
1123
|
|
|
1124
|
-
const EventEmitter =
|
|
1125
|
-
const fs = __nccwpck_require__(692);
|
|
1124
|
+
const { EventEmitter } = __nccwpck_require__(434);
|
|
1126
1125
|
const path = __nccwpck_require__(928);
|
|
1126
|
+
const fs = __nccwpck_require__(692);
|
|
1127
|
+
|
|
1128
|
+
const watchEventSource = __nccwpck_require__(313);
|
|
1127
1129
|
|
|
1128
|
-
|
|
1130
|
+
/** @typedef {import("./index").IgnoredFunction} IgnoredFunction */
|
|
1131
|
+
/** @typedef {import("./index").EventType} EventType */
|
|
1132
|
+
/** @typedef {import("./index").TimeInfoEntries} TimeInfoEntries */
|
|
1133
|
+
/** @typedef {import("./index").Entry} Entry */
|
|
1134
|
+
/** @typedef {import("./index").ExistenceOnlyTimeEntry} ExistenceOnlyTimeEntry */
|
|
1135
|
+
/** @typedef {import("./index").OnlySafeTimeEntry} OnlySafeTimeEntry */
|
|
1136
|
+
/** @typedef {import("./index").EventMap} EventMap */
|
|
1137
|
+
/** @typedef {import("./getWatcherManager").WatcherManager} WatcherManager */
|
|
1138
|
+
/** @typedef {import("./watchEventSource").Watcher} EventSourceWatcher */
|
|
1129
1139
|
|
|
1140
|
+
/** @type {ExistenceOnlyTimeEntry} */
|
|
1130
1141
|
const EXISTANCE_ONLY_TIME_ENTRY = Object.freeze({});
|
|
1131
1142
|
|
|
1132
1143
|
let FS_ACCURACY = 2000;
|
|
@@ -1134,45 +1145,130 @@ let FS_ACCURACY = 2000;
|
|
|
1134
1145
|
const IS_OSX = (__nccwpck_require__(857).platform)() === "darwin";
|
|
1135
1146
|
const IS_WIN = (__nccwpck_require__(857).platform)() === "win32";
|
|
1136
1147
|
|
|
1137
|
-
const WATCHPACK_POLLING = process.env
|
|
1148
|
+
const { WATCHPACK_POLLING } = process.env;
|
|
1138
1149
|
const FORCE_POLLING =
|
|
1150
|
+
// @ts-expect-error avoid additional checks
|
|
1139
1151
|
`${+WATCHPACK_POLLING}` === WATCHPACK_POLLING
|
|
1140
1152
|
? +WATCHPACK_POLLING
|
|
1141
|
-
:
|
|
1153
|
+
: Boolean(WATCHPACK_POLLING) && WATCHPACK_POLLING !== "false";
|
|
1142
1154
|
|
|
1155
|
+
/**
|
|
1156
|
+
* @param {string} str string
|
|
1157
|
+
* @returns {string} lower cased string
|
|
1158
|
+
*/
|
|
1143
1159
|
function withoutCase(str) {
|
|
1144
1160
|
return str.toLowerCase();
|
|
1145
1161
|
}
|
|
1146
1162
|
|
|
1163
|
+
/**
|
|
1164
|
+
* @param {number} times times
|
|
1165
|
+
* @param {() => void} callback callback
|
|
1166
|
+
* @returns {() => void} result
|
|
1167
|
+
*/
|
|
1147
1168
|
function needCalls(times, callback) {
|
|
1148
|
-
return function() {
|
|
1169
|
+
return function needCallsCallback() {
|
|
1149
1170
|
if (--times === 0) {
|
|
1150
1171
|
return callback();
|
|
1151
1172
|
}
|
|
1152
1173
|
};
|
|
1153
1174
|
}
|
|
1154
1175
|
|
|
1176
|
+
/**
|
|
1177
|
+
* @param {Entry} entry entry
|
|
1178
|
+
*/
|
|
1179
|
+
function fixupEntryAccuracy(entry) {
|
|
1180
|
+
if (entry.accuracy > FS_ACCURACY) {
|
|
1181
|
+
entry.safeTime = entry.safeTime - entry.accuracy + FS_ACCURACY;
|
|
1182
|
+
entry.accuracy = FS_ACCURACY;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* @param {number=} mtime mtime
|
|
1188
|
+
*/
|
|
1189
|
+
function ensureFsAccuracy(mtime) {
|
|
1190
|
+
if (!mtime) return;
|
|
1191
|
+
if (FS_ACCURACY > 1 && mtime % 1 !== 0) FS_ACCURACY = 1;
|
|
1192
|
+
else if (FS_ACCURACY > 10 && mtime % 10 !== 0) FS_ACCURACY = 10;
|
|
1193
|
+
else if (FS_ACCURACY > 100 && mtime % 100 !== 0) FS_ACCURACY = 100;
|
|
1194
|
+
else if (FS_ACCURACY > 1000 && mtime % 1000 !== 0) FS_ACCURACY = 1000;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
/**
|
|
1198
|
+
* @typedef {object} FileWatcherEvents
|
|
1199
|
+
* @property {(type: EventType) => void} initial-missing initial missing event
|
|
1200
|
+
* @property {(mtime: number, type: EventType, initial: boolean) => void} change change event
|
|
1201
|
+
* @property {(type: EventType) => void} remove remove event
|
|
1202
|
+
* @property {() => void} closed closed event
|
|
1203
|
+
*/
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* @typedef {object} DirectoryWatcherEvents
|
|
1207
|
+
* @property {(type: EventType) => void} initial-missing initial missing event
|
|
1208
|
+
* @property {((file: string, mtime: number, type: EventType, initial: boolean) => void)} change change event
|
|
1209
|
+
* @property {(type: EventType) => void} remove remove event
|
|
1210
|
+
* @property {() => void} closed closed event
|
|
1211
|
+
*/
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* @template {EventMap} T
|
|
1215
|
+
* @extends {EventEmitter<{ [K in keyof T]: Parameters<T[K]> }>}
|
|
1216
|
+
*/
|
|
1155
1217
|
class Watcher extends EventEmitter {
|
|
1156
|
-
|
|
1218
|
+
/**
|
|
1219
|
+
* @param {DirectoryWatcher} directoryWatcher a directory watcher
|
|
1220
|
+
* @param {string} target a target to watch
|
|
1221
|
+
* @param {number=} startTime start time
|
|
1222
|
+
*/
|
|
1223
|
+
constructor(directoryWatcher, target, startTime) {
|
|
1157
1224
|
super();
|
|
1158
1225
|
this.directoryWatcher = directoryWatcher;
|
|
1159
|
-
this.path =
|
|
1226
|
+
this.path = target;
|
|
1160
1227
|
this.startTime = startTime && +startTime;
|
|
1161
1228
|
}
|
|
1162
1229
|
|
|
1230
|
+
/**
|
|
1231
|
+
* @param {number} mtime mtime
|
|
1232
|
+
* @param {boolean} initial true when initial, otherwise false
|
|
1233
|
+
* @returns {boolean} true of start time less than mtile, otherwise false
|
|
1234
|
+
*/
|
|
1163
1235
|
checkStartTime(mtime, initial) {
|
|
1164
|
-
const startTime = this
|
|
1236
|
+
const { startTime } = this;
|
|
1165
1237
|
if (typeof startTime !== "number") return !initial;
|
|
1166
1238
|
return startTime <= mtime;
|
|
1167
1239
|
}
|
|
1168
1240
|
|
|
1169
1241
|
close() {
|
|
1242
|
+
// @ts-expect-error bad typing in EventEmitter
|
|
1170
1243
|
this.emit("closed");
|
|
1171
1244
|
}
|
|
1172
1245
|
}
|
|
1173
1246
|
|
|
1247
|
+
/** @typedef {Set<string>} InitialScanRemoved */
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* @typedef {object} WatchpackEvents
|
|
1251
|
+
* @property {(target: string, mtime: string, type: EventType, initial: boolean) => void} change change event
|
|
1252
|
+
* @property {() => void} closed closed event
|
|
1253
|
+
*/
|
|
1254
|
+
|
|
1255
|
+
/**
|
|
1256
|
+
* @typedef {object} DirectoryWatcherOptions
|
|
1257
|
+
* @property {boolean=} followSymlinks true when need to resolve symlinks and watch symlink and real file, otherwise false
|
|
1258
|
+
* @property {IgnoredFunction=} ignored ignore some files from watching (glob pattern or regexp)
|
|
1259
|
+
* @property {number | boolean=} poll true when need to enable polling mode for watching, otherwise false
|
|
1260
|
+
*/
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* @extends {EventEmitter<{ [K in keyof WatchpackEvents]: Parameters<WatchpackEvents[K]> }>}
|
|
1264
|
+
*/
|
|
1174
1265
|
class DirectoryWatcher extends EventEmitter {
|
|
1175
|
-
|
|
1266
|
+
/**
|
|
1267
|
+
* @param {WatcherManager} watcherManager a watcher manager
|
|
1268
|
+
* @param {string} directoryPath directory path
|
|
1269
|
+
* @param {DirectoryWatcherOptions=} options options
|
|
1270
|
+
*/
|
|
1271
|
+
constructor(watcherManager, directoryPath, options = {}) {
|
|
1176
1272
|
super();
|
|
1177
1273
|
if (FORCE_POLLING) {
|
|
1178
1274
|
options.poll = FORCE_POLLING;
|
|
@@ -1182,28 +1278,35 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1182
1278
|
this.path = directoryPath;
|
|
1183
1279
|
// safeTime is the point in time after which reading is safe to be unchanged
|
|
1184
1280
|
// timestamp is a value that should be compared with another timestamp (mtime)
|
|
1185
|
-
/** @type {Map<string,
|
|
1281
|
+
/** @type {Map<string, Entry>} */
|
|
1186
1282
|
this.files = new Map();
|
|
1187
1283
|
/** @type {Map<string, number>} */
|
|
1188
1284
|
this.filesWithoutCase = new Map();
|
|
1285
|
+
/** @type {Map<string, Watcher<DirectoryWatcherEvents> | boolean>} */
|
|
1189
1286
|
this.directories = new Map();
|
|
1190
1287
|
this.lastWatchEvent = 0;
|
|
1191
1288
|
this.initialScan = true;
|
|
1192
1289
|
this.ignored = options.ignored || (() => false);
|
|
1193
1290
|
this.nestedWatching = false;
|
|
1291
|
+
/** @type {number | false} */
|
|
1194
1292
|
this.polledWatching =
|
|
1195
1293
|
typeof options.poll === "number"
|
|
1196
1294
|
? options.poll
|
|
1197
1295
|
: options.poll
|
|
1198
|
-
|
|
1199
|
-
|
|
1296
|
+
? 5007
|
|
1297
|
+
: false;
|
|
1298
|
+
/** @type {undefined | NodeJS.Timeout} */
|
|
1200
1299
|
this.timeout = undefined;
|
|
1300
|
+
/** @type {null | InitialScanRemoved} */
|
|
1201
1301
|
this.initialScanRemoved = new Set();
|
|
1302
|
+
/** @type {undefined | number} */
|
|
1202
1303
|
this.initialScanFinished = undefined;
|
|
1203
|
-
/** @type {Map<string, Set<Watcher
|
|
1304
|
+
/** @type {Map<string, Set<Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>>>} */
|
|
1204
1305
|
this.watchers = new Map();
|
|
1306
|
+
/** @type {Watcher<FileWatcherEvents> | null} */
|
|
1205
1307
|
this.parentWatcher = null;
|
|
1206
1308
|
this.refs = 0;
|
|
1309
|
+
/** @type {Map<string, boolean>} */
|
|
1207
1310
|
this._activeEvents = new Map();
|
|
1208
1311
|
this.closed = false;
|
|
1209
1312
|
this.scanning = false;
|
|
@@ -1217,19 +1320,22 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1217
1320
|
createWatcher() {
|
|
1218
1321
|
try {
|
|
1219
1322
|
if (this.polledWatching) {
|
|
1220
|
-
|
|
1323
|
+
/** @type {EventSourceWatcher} */
|
|
1324
|
+
(this.watcher) = /** @type {EventSourceWatcher} */ ({
|
|
1221
1325
|
close: () => {
|
|
1222
1326
|
if (this.timeout) {
|
|
1223
1327
|
clearTimeout(this.timeout);
|
|
1224
1328
|
this.timeout = undefined;
|
|
1225
1329
|
}
|
|
1226
|
-
}
|
|
1227
|
-
};
|
|
1330
|
+
},
|
|
1331
|
+
});
|
|
1228
1332
|
} else {
|
|
1229
1333
|
if (IS_OSX) {
|
|
1230
1334
|
this.watchInParentDirectory();
|
|
1231
1335
|
}
|
|
1232
|
-
this.watcher =
|
|
1336
|
+
this.watcher =
|
|
1337
|
+
/** @type {EventSourceWatcher} */
|
|
1338
|
+
(watchEventSource.watch(this.path));
|
|
1233
1339
|
this.watcher.on("change", this.onWatchEvent.bind(this));
|
|
1234
1340
|
this.watcher.on("error", this.onWatcherError.bind(this));
|
|
1235
1341
|
}
|
|
@@ -1238,6 +1344,11 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1238
1344
|
}
|
|
1239
1345
|
}
|
|
1240
1346
|
|
|
1347
|
+
/**
|
|
1348
|
+
* @template {(watcher: Watcher<EventMap>) => void} T
|
|
1349
|
+
* @param {string} path path
|
|
1350
|
+
* @param {T} fn function
|
|
1351
|
+
*/
|
|
1241
1352
|
forEachWatcher(path, fn) {
|
|
1242
1353
|
const watchers = this.watchers.get(withoutCase(path));
|
|
1243
1354
|
if (watchers !== undefined) {
|
|
@@ -1247,20 +1358,28 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1247
1358
|
}
|
|
1248
1359
|
}
|
|
1249
1360
|
|
|
1361
|
+
/**
|
|
1362
|
+
* @param {string} itemPath an item path
|
|
1363
|
+
* @param {boolean} initial true when initial, otherwise false
|
|
1364
|
+
* @param {EventType} type even type
|
|
1365
|
+
*/
|
|
1250
1366
|
setMissing(itemPath, initial, type) {
|
|
1251
1367
|
if (this.initialScan) {
|
|
1252
|
-
|
|
1368
|
+
/** @type {InitialScanRemoved} */
|
|
1369
|
+
(this.initialScanRemoved).add(itemPath);
|
|
1253
1370
|
}
|
|
1254
1371
|
|
|
1255
1372
|
const oldDirectory = this.directories.get(itemPath);
|
|
1256
1373
|
if (oldDirectory) {
|
|
1257
|
-
if (this.nestedWatching)
|
|
1374
|
+
if (this.nestedWatching) {
|
|
1375
|
+
/** @type {Watcher<DirectoryWatcherEvents>} */
|
|
1376
|
+
(oldDirectory).close();
|
|
1377
|
+
}
|
|
1258
1378
|
this.directories.delete(itemPath);
|
|
1259
|
-
|
|
1260
|
-
this.forEachWatcher(itemPath, w => w.emit("remove", type));
|
|
1379
|
+
this.forEachWatcher(itemPath, (w) => w.emit("remove", type));
|
|
1261
1380
|
if (!initial) {
|
|
1262
|
-
this.forEachWatcher(this.path, w =>
|
|
1263
|
-
w.emit("change", itemPath, null, type, initial)
|
|
1381
|
+
this.forEachWatcher(this.path, (w) =>
|
|
1382
|
+
w.emit("change", itemPath, null, type, initial),
|
|
1264
1383
|
);
|
|
1265
1384
|
}
|
|
1266
1385
|
}
|
|
@@ -1269,30 +1388,38 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1269
1388
|
if (oldFile) {
|
|
1270
1389
|
this.files.delete(itemPath);
|
|
1271
1390
|
const key = withoutCase(itemPath);
|
|
1272
|
-
const count = this.filesWithoutCase.get(key) - 1;
|
|
1391
|
+
const count = /** @type {number} */ (this.filesWithoutCase.get(key)) - 1;
|
|
1273
1392
|
if (count <= 0) {
|
|
1274
1393
|
this.filesWithoutCase.delete(key);
|
|
1275
|
-
this.forEachWatcher(itemPath, w => w.emit("remove", type));
|
|
1394
|
+
this.forEachWatcher(itemPath, (w) => w.emit("remove", type));
|
|
1276
1395
|
} else {
|
|
1277
1396
|
this.filesWithoutCase.set(key, count);
|
|
1278
1397
|
}
|
|
1279
1398
|
|
|
1280
1399
|
if (!initial) {
|
|
1281
|
-
this.forEachWatcher(this.path, w =>
|
|
1282
|
-
w.emit("change", itemPath, null, type, initial)
|
|
1400
|
+
this.forEachWatcher(this.path, (w) =>
|
|
1401
|
+
w.emit("change", itemPath, null, type, initial),
|
|
1283
1402
|
);
|
|
1284
1403
|
}
|
|
1285
1404
|
}
|
|
1286
1405
|
}
|
|
1287
1406
|
|
|
1288
|
-
|
|
1407
|
+
/**
|
|
1408
|
+
* @param {string} target a target to set file time
|
|
1409
|
+
* @param {number} mtime mtime
|
|
1410
|
+
* @param {boolean} initial true when initial, otherwise false
|
|
1411
|
+
* @param {boolean} ignoreWhenEqual true to ignore when equal, otherwise false
|
|
1412
|
+
* @param {EventType} type type
|
|
1413
|
+
*/
|
|
1414
|
+
setFileTime(target, mtime, initial, ignoreWhenEqual, type) {
|
|
1289
1415
|
const now = Date.now();
|
|
1290
1416
|
|
|
1291
|
-
if (this.ignored(
|
|
1417
|
+
if (this.ignored(target)) return;
|
|
1292
1418
|
|
|
1293
|
-
const old = this.files.get(
|
|
1419
|
+
const old = this.files.get(target);
|
|
1294
1420
|
|
|
1295
|
-
let safeTime
|
|
1421
|
+
let safeTime;
|
|
1422
|
+
let accuracy;
|
|
1296
1423
|
if (initial) {
|
|
1297
1424
|
safeTime = Math.min(now, mtime) + FS_ACCURACY;
|
|
1298
1425
|
accuracy = FS_ACCURACY;
|
|
@@ -1311,14 +1438,14 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1311
1438
|
|
|
1312
1439
|
if (ignoreWhenEqual && old && old.timestamp === mtime) return;
|
|
1313
1440
|
|
|
1314
|
-
this.files.set(
|
|
1441
|
+
this.files.set(target, {
|
|
1315
1442
|
safeTime,
|
|
1316
1443
|
accuracy,
|
|
1317
|
-
timestamp: mtime
|
|
1444
|
+
timestamp: mtime,
|
|
1318
1445
|
});
|
|
1319
1446
|
|
|
1320
1447
|
if (!old) {
|
|
1321
|
-
const key = withoutCase(
|
|
1448
|
+
const key = withoutCase(target);
|
|
1322
1449
|
const count = this.filesWithoutCase.get(key);
|
|
1323
1450
|
this.filesWithoutCase.set(key, (count || 0) + 1);
|
|
1324
1451
|
if (count !== undefined) {
|
|
@@ -1330,27 +1457,33 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1330
1457
|
this.doScan(false);
|
|
1331
1458
|
}
|
|
1332
1459
|
|
|
1333
|
-
this.forEachWatcher(
|
|
1460
|
+
this.forEachWatcher(target, (w) => {
|
|
1334
1461
|
if (!initial || w.checkStartTime(safeTime, initial)) {
|
|
1335
1462
|
w.emit("change", mtime, type);
|
|
1336
1463
|
}
|
|
1337
1464
|
});
|
|
1338
1465
|
} else if (!initial) {
|
|
1339
|
-
this.forEachWatcher(
|
|
1466
|
+
this.forEachWatcher(target, (w) => w.emit("change", mtime, type));
|
|
1340
1467
|
}
|
|
1341
|
-
this.forEachWatcher(this.path, w => {
|
|
1468
|
+
this.forEachWatcher(this.path, (w) => {
|
|
1342
1469
|
if (!initial || w.checkStartTime(safeTime, initial)) {
|
|
1343
|
-
w.emit("change",
|
|
1470
|
+
w.emit("change", target, safeTime, type, initial);
|
|
1344
1471
|
}
|
|
1345
1472
|
});
|
|
1346
1473
|
}
|
|
1347
1474
|
|
|
1475
|
+
/**
|
|
1476
|
+
* @param {string} directoryPath directory path
|
|
1477
|
+
* @param {number} birthtime birthtime
|
|
1478
|
+
* @param {boolean} initial true when initial, otherwise false
|
|
1479
|
+
* @param {EventType} type even type
|
|
1480
|
+
*/
|
|
1348
1481
|
setDirectory(directoryPath, birthtime, initial, type) {
|
|
1349
1482
|
if (this.ignored(directoryPath)) return;
|
|
1350
1483
|
if (directoryPath === this.path) {
|
|
1351
1484
|
if (!initial) {
|
|
1352
|
-
this.forEachWatcher(this.path, w =>
|
|
1353
|
-
w.emit("change", directoryPath, birthtime, type, initial)
|
|
1485
|
+
this.forEachWatcher(this.path, (w) =>
|
|
1486
|
+
w.emit("change", directoryPath, birthtime, type, initial),
|
|
1354
1487
|
);
|
|
1355
1488
|
}
|
|
1356
1489
|
} else {
|
|
@@ -1364,19 +1497,14 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1364
1497
|
this.directories.set(directoryPath, true);
|
|
1365
1498
|
}
|
|
1366
1499
|
|
|
1367
|
-
|
|
1368
|
-
if (initial) {
|
|
1369
|
-
safeTime = Math.min(now, birthtime) + FS_ACCURACY;
|
|
1370
|
-
} else {
|
|
1371
|
-
safeTime = now;
|
|
1372
|
-
}
|
|
1500
|
+
const safeTime = initial ? Math.min(now, birthtime) + FS_ACCURACY : now;
|
|
1373
1501
|
|
|
1374
|
-
this.forEachWatcher(directoryPath, w => {
|
|
1502
|
+
this.forEachWatcher(directoryPath, (w) => {
|
|
1375
1503
|
if (!initial || w.checkStartTime(safeTime, false)) {
|
|
1376
1504
|
w.emit("change", birthtime, type);
|
|
1377
1505
|
}
|
|
1378
1506
|
});
|
|
1379
|
-
this.forEachWatcher(this.path, w => {
|
|
1507
|
+
this.forEachWatcher(this.path, (w) => {
|
|
1380
1508
|
if (!initial || w.checkStartTime(safeTime, initial)) {
|
|
1381
1509
|
w.emit("change", directoryPath, safeTime, type, initial);
|
|
1382
1510
|
}
|
|
@@ -1385,43 +1513,57 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1385
1513
|
}
|
|
1386
1514
|
}
|
|
1387
1515
|
|
|
1516
|
+
/**
|
|
1517
|
+
* @param {string} directoryPath directory path
|
|
1518
|
+
*/
|
|
1388
1519
|
createNestedWatcher(directoryPath) {
|
|
1389
1520
|
const watcher = this.watcherManager.watchDirectory(directoryPath, 1);
|
|
1390
|
-
watcher.on("change", (
|
|
1391
|
-
this.forEachWatcher(this.path, w => {
|
|
1521
|
+
watcher.on("change", (target, mtime, type, initial) => {
|
|
1522
|
+
this.forEachWatcher(this.path, (w) => {
|
|
1392
1523
|
if (!initial || w.checkStartTime(mtime, initial)) {
|
|
1393
|
-
w.emit("change",
|
|
1524
|
+
w.emit("change", target, mtime, type, initial);
|
|
1394
1525
|
}
|
|
1395
1526
|
});
|
|
1396
1527
|
});
|
|
1397
1528
|
this.directories.set(directoryPath, watcher);
|
|
1398
1529
|
}
|
|
1399
1530
|
|
|
1531
|
+
/**
|
|
1532
|
+
* @param {boolean} flag true when nested, otherwise false
|
|
1533
|
+
*/
|
|
1400
1534
|
setNestedWatching(flag) {
|
|
1401
|
-
if (this.nestedWatching !==
|
|
1402
|
-
this.nestedWatching =
|
|
1535
|
+
if (this.nestedWatching !== Boolean(flag)) {
|
|
1536
|
+
this.nestedWatching = Boolean(flag);
|
|
1403
1537
|
if (this.nestedWatching) {
|
|
1404
1538
|
for (const directory of this.directories.keys()) {
|
|
1405
1539
|
this.createNestedWatcher(directory);
|
|
1406
1540
|
}
|
|
1407
1541
|
} else {
|
|
1408
1542
|
for (const [directory, watcher] of this.directories) {
|
|
1409
|
-
|
|
1543
|
+
/** @type {Watcher<DirectoryWatcherEvents>} */
|
|
1544
|
+
(watcher).close();
|
|
1410
1545
|
this.directories.set(directory, true);
|
|
1411
1546
|
}
|
|
1412
1547
|
}
|
|
1413
1548
|
}
|
|
1414
1549
|
}
|
|
1415
1550
|
|
|
1416
|
-
|
|
1417
|
-
|
|
1551
|
+
/**
|
|
1552
|
+
* @param {string} target a target to watch
|
|
1553
|
+
* @param {number=} startTime start time
|
|
1554
|
+
* @returns {Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>} watcher
|
|
1555
|
+
*/
|
|
1556
|
+
watch(target, startTime) {
|
|
1557
|
+
const key = withoutCase(target);
|
|
1418
1558
|
let watchers = this.watchers.get(key);
|
|
1419
1559
|
if (watchers === undefined) {
|
|
1420
1560
|
watchers = new Set();
|
|
1421
1561
|
this.watchers.set(key, watchers);
|
|
1422
1562
|
}
|
|
1423
1563
|
this.refs++;
|
|
1424
|
-
const watcher =
|
|
1564
|
+
const watcher =
|
|
1565
|
+
/** @type {Watcher<DirectoryWatcherEvents> | Watcher<FileWatcherEvents>} */
|
|
1566
|
+
(new Watcher(this, target, startTime));
|
|
1425
1567
|
watcher.on("closed", () => {
|
|
1426
1568
|
if (--this.refs <= 0) {
|
|
1427
1569
|
this.close();
|
|
@@ -1430,12 +1572,12 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1430
1572
|
watchers.delete(watcher);
|
|
1431
1573
|
if (watchers.size === 0) {
|
|
1432
1574
|
this.watchers.delete(key);
|
|
1433
|
-
if (this.path ===
|
|
1575
|
+
if (this.path === target) this.setNestedWatching(false);
|
|
1434
1576
|
}
|
|
1435
1577
|
});
|
|
1436
1578
|
watchers.add(watcher);
|
|
1437
1579
|
let safeTime;
|
|
1438
|
-
if (
|
|
1580
|
+
if (target === this.path) {
|
|
1439
1581
|
this.setNestedWatching(true);
|
|
1440
1582
|
safeTime = this.lastWatchEvent;
|
|
1441
1583
|
for (const entry of this.files.values()) {
|
|
@@ -1443,7 +1585,7 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1443
1585
|
safeTime = Math.max(safeTime, entry.safeTime);
|
|
1444
1586
|
}
|
|
1445
1587
|
} else {
|
|
1446
|
-
const entry = this.files.get(
|
|
1588
|
+
const entry = this.files.get(target);
|
|
1447
1589
|
if (entry) {
|
|
1448
1590
|
fixupEntryAccuracy(entry);
|
|
1449
1591
|
safeTime = entry.safeTime;
|
|
@@ -1452,38 +1594,47 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1452
1594
|
}
|
|
1453
1595
|
}
|
|
1454
1596
|
if (safeTime) {
|
|
1455
|
-
if (safeTime >= startTime) {
|
|
1597
|
+
if (startTime && safeTime >= startTime) {
|
|
1456
1598
|
process.nextTick(() => {
|
|
1457
1599
|
if (this.closed) return;
|
|
1458
|
-
if (
|
|
1459
|
-
|
|
1600
|
+
if (target === this.path) {
|
|
1601
|
+
/** @type {Watcher<DirectoryWatcherEvents>} */
|
|
1602
|
+
(watcher).emit(
|
|
1460
1603
|
"change",
|
|
1461
|
-
|
|
1604
|
+
target,
|
|
1462
1605
|
safeTime,
|
|
1463
1606
|
"watch (outdated on attach)",
|
|
1464
|
-
true
|
|
1607
|
+
true,
|
|
1465
1608
|
);
|
|
1466
1609
|
} else {
|
|
1467
|
-
|
|
1610
|
+
/** @type {Watcher<FileWatcherEvents>} */
|
|
1611
|
+
(watcher).emit(
|
|
1468
1612
|
"change",
|
|
1469
1613
|
safeTime,
|
|
1470
1614
|
"watch (outdated on attach)",
|
|
1471
|
-
true
|
|
1615
|
+
true,
|
|
1472
1616
|
);
|
|
1473
1617
|
}
|
|
1474
1618
|
});
|
|
1475
1619
|
}
|
|
1476
1620
|
} else if (this.initialScan) {
|
|
1477
|
-
if (
|
|
1621
|
+
if (
|
|
1622
|
+
/** @type {InitialScanRemoved} */
|
|
1623
|
+
(this.initialScanRemoved).has(target)
|
|
1624
|
+
) {
|
|
1478
1625
|
process.nextTick(() => {
|
|
1479
1626
|
if (this.closed) return;
|
|
1480
1627
|
watcher.emit("remove");
|
|
1481
1628
|
});
|
|
1482
1629
|
}
|
|
1483
1630
|
} else if (
|
|
1484
|
-
|
|
1485
|
-
!this.directories.has(
|
|
1486
|
-
watcher.checkStartTime(
|
|
1631
|
+
target !== this.path &&
|
|
1632
|
+
!this.directories.has(target) &&
|
|
1633
|
+
watcher.checkStartTime(
|
|
1634
|
+
/** @type {number} */
|
|
1635
|
+
(this.initialScanFinished),
|
|
1636
|
+
false,
|
|
1637
|
+
)
|
|
1487
1638
|
) {
|
|
1488
1639
|
process.nextTick(() => {
|
|
1489
1640
|
if (this.closed) return;
|
|
@@ -1493,6 +1644,10 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1493
1644
|
return watcher;
|
|
1494
1645
|
}
|
|
1495
1646
|
|
|
1647
|
+
/**
|
|
1648
|
+
* @param {EventType} eventType event type
|
|
1649
|
+
* @param {string=} filename filename
|
|
1650
|
+
*/
|
|
1496
1651
|
onWatchEvent(eventType, filename) {
|
|
1497
1652
|
if (this.closed) return;
|
|
1498
1653
|
if (!filename) {
|
|
@@ -1504,15 +1659,15 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1504
1659
|
return;
|
|
1505
1660
|
}
|
|
1506
1661
|
|
|
1507
|
-
const
|
|
1508
|
-
if (this.ignored(
|
|
1662
|
+
const target = path.join(this.path, filename);
|
|
1663
|
+
if (this.ignored(target)) return;
|
|
1509
1664
|
|
|
1510
1665
|
if (this._activeEvents.get(filename) === undefined) {
|
|
1511
1666
|
this._activeEvents.set(filename, false);
|
|
1512
1667
|
const checkStats = () => {
|
|
1513
1668
|
if (this.closed) return;
|
|
1514
1669
|
this._activeEvents.set(filename, false);
|
|
1515
|
-
fs.lstat(
|
|
1670
|
+
fs.lstat(target, (err, stats) => {
|
|
1516
1671
|
if (this.closed) return;
|
|
1517
1672
|
if (this._activeEvents.get(filename) === true) {
|
|
1518
1673
|
process.nextTick(checkStats);
|
|
@@ -1528,35 +1683,28 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1528
1683
|
err.code !== "EBUSY"
|
|
1529
1684
|
) {
|
|
1530
1685
|
this.onStatsError(err);
|
|
1531
|
-
} else
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1686
|
+
} else if (
|
|
1687
|
+
filename === path.basename(this.path) && // This may indicate that the directory itself was removed
|
|
1688
|
+
!fs.existsSync(this.path)
|
|
1689
|
+
) {
|
|
1690
|
+
this.onDirectoryRemoved("stat failed");
|
|
1538
1691
|
}
|
|
1539
1692
|
}
|
|
1540
1693
|
this.lastWatchEvent = Date.now();
|
|
1541
1694
|
if (!stats) {
|
|
1542
|
-
this.setMissing(
|
|
1695
|
+
this.setMissing(target, false, eventType);
|
|
1543
1696
|
} else if (stats.isDirectory()) {
|
|
1544
|
-
this.setDirectory(
|
|
1545
|
-
filePath,
|
|
1546
|
-
+stats.birthtime || 1,
|
|
1547
|
-
false,
|
|
1548
|
-
eventType
|
|
1549
|
-
);
|
|
1697
|
+
this.setDirectory(target, +stats.birthtime || 1, false, eventType);
|
|
1550
1698
|
} else if (stats.isFile() || stats.isSymbolicLink()) {
|
|
1551
1699
|
if (stats.mtime) {
|
|
1552
|
-
ensureFsAccuracy(stats.mtime);
|
|
1700
|
+
ensureFsAccuracy(+stats.mtime);
|
|
1553
1701
|
}
|
|
1554
1702
|
this.setFileTime(
|
|
1555
|
-
|
|
1703
|
+
target,
|
|
1556
1704
|
+stats.mtime || +stats.ctime || 1,
|
|
1557
1705
|
false,
|
|
1558
1706
|
false,
|
|
1559
|
-
eventType
|
|
1707
|
+
eventType,
|
|
1560
1708
|
);
|
|
1561
1709
|
}
|
|
1562
1710
|
});
|
|
@@ -1567,25 +1715,42 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1567
1715
|
}
|
|
1568
1716
|
}
|
|
1569
1717
|
|
|
1718
|
+
/**
|
|
1719
|
+
* @param {unknown=} err error
|
|
1720
|
+
*/
|
|
1570
1721
|
onWatcherError(err) {
|
|
1571
1722
|
if (this.closed) return;
|
|
1572
1723
|
if (err) {
|
|
1573
|
-
if (
|
|
1574
|
-
|
|
1724
|
+
if (
|
|
1725
|
+
/** @type {NodeJS.ErrnoException} */
|
|
1726
|
+
(err).code !== "EPERM" &&
|
|
1727
|
+
/** @type {NodeJS.ErrnoException} */
|
|
1728
|
+
(err).code !== "ENOENT"
|
|
1729
|
+
) {
|
|
1730
|
+
// eslint-disable-next-line no-console
|
|
1731
|
+
console.error(`Watchpack Error (watcher): ${err}`);
|
|
1575
1732
|
}
|
|
1576
1733
|
this.onDirectoryRemoved("watch error");
|
|
1577
1734
|
}
|
|
1578
1735
|
}
|
|
1579
1736
|
|
|
1737
|
+
/**
|
|
1738
|
+
* @param {Error | NodeJS.ErrnoException=} err error
|
|
1739
|
+
*/
|
|
1580
1740
|
onStatsError(err) {
|
|
1581
1741
|
if (err) {
|
|
1582
|
-
|
|
1742
|
+
// eslint-disable-next-line no-console
|
|
1743
|
+
console.error(`Watchpack Error (stats): ${err}`);
|
|
1583
1744
|
}
|
|
1584
1745
|
}
|
|
1585
1746
|
|
|
1747
|
+
/**
|
|
1748
|
+
* @param {Error | NodeJS.ErrnoException=} err error
|
|
1749
|
+
*/
|
|
1586
1750
|
onScanError(err) {
|
|
1587
1751
|
if (err) {
|
|
1588
|
-
|
|
1752
|
+
// eslint-disable-next-line no-console
|
|
1753
|
+
console.error(`Watchpack Error (initial scan): ${err}`);
|
|
1589
1754
|
}
|
|
1590
1755
|
this.onScanFinished();
|
|
1591
1756
|
}
|
|
@@ -1599,18 +1764,21 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1599
1764
|
}
|
|
1600
1765
|
}
|
|
1601
1766
|
|
|
1767
|
+
/**
|
|
1768
|
+
* @param {string} reason a reason
|
|
1769
|
+
*/
|
|
1602
1770
|
onDirectoryRemoved(reason) {
|
|
1603
1771
|
if (this.watcher) {
|
|
1604
1772
|
this.watcher.close();
|
|
1605
1773
|
this.watcher = null;
|
|
1606
1774
|
}
|
|
1607
1775
|
this.watchInParentDirectory();
|
|
1608
|
-
const type = `directory-removed (${reason})
|
|
1776
|
+
const type = /** @type {EventType} */ (`directory-removed (${reason})`);
|
|
1609
1777
|
for (const directory of this.directories.keys()) {
|
|
1610
|
-
this.setMissing(directory,
|
|
1778
|
+
this.setMissing(directory, false, type);
|
|
1611
1779
|
}
|
|
1612
1780
|
for (const file of this.files.keys()) {
|
|
1613
|
-
this.setMissing(file,
|
|
1781
|
+
this.setMissing(file, false, type);
|
|
1614
1782
|
}
|
|
1615
1783
|
}
|
|
1616
1784
|
|
|
@@ -1622,7 +1790,8 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1622
1790
|
if (path.dirname(parentDir) === parentDir) return;
|
|
1623
1791
|
|
|
1624
1792
|
this.parentWatcher = this.watcherManager.watchFile(this.path, 1);
|
|
1625
|
-
|
|
1793
|
+
/** @type {Watcher<FileWatcherEvents>} */
|
|
1794
|
+
(this.parentWatcher).on("change", (mtime, type) => {
|
|
1626
1795
|
if (this.closed) return;
|
|
1627
1796
|
|
|
1628
1797
|
// On non-osx platforms we don't need this watcher to detect
|
|
@@ -1637,17 +1806,21 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1637
1806
|
this.doScan(false);
|
|
1638
1807
|
|
|
1639
1808
|
// directory was created so we emit an event
|
|
1640
|
-
this.forEachWatcher(this.path, w =>
|
|
1641
|
-
w.emit("change", this.path, mtime, type, false)
|
|
1809
|
+
this.forEachWatcher(this.path, (w) =>
|
|
1810
|
+
w.emit("change", this.path, mtime, type, false),
|
|
1642
1811
|
);
|
|
1643
1812
|
}
|
|
1644
1813
|
});
|
|
1645
|
-
|
|
1814
|
+
/** @type {Watcher<FileWatcherEvents>} */
|
|
1815
|
+
(this.parentWatcher).on("remove", () => {
|
|
1646
1816
|
this.onDirectoryRemoved("parent directory removed");
|
|
1647
1817
|
});
|
|
1648
1818
|
}
|
|
1649
1819
|
}
|
|
1650
1820
|
|
|
1821
|
+
/**
|
|
1822
|
+
* @param {boolean} initial true when initial, otherwise false
|
|
1823
|
+
*/
|
|
1651
1824
|
doScan(initial) {
|
|
1652
1825
|
if (this.scanning) {
|
|
1653
1826
|
if (this.scanAgain) {
|
|
@@ -1681,7 +1854,7 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1681
1854
|
if (watcher.checkStartTime(this.initialScanFinished, false)) {
|
|
1682
1855
|
watcher.emit(
|
|
1683
1856
|
"initial-missing",
|
|
1684
|
-
"scan (parent directory missing in initial scan)"
|
|
1857
|
+
"scan (parent directory missing in initial scan)",
|
|
1685
1858
|
);
|
|
1686
1859
|
}
|
|
1687
1860
|
}
|
|
@@ -1696,7 +1869,7 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1696
1869
|
return;
|
|
1697
1870
|
}
|
|
1698
1871
|
const itemPaths = new Set(
|
|
1699
|
-
items.map(item => path.join(this.path, item.normalize("NFC")))
|
|
1872
|
+
items.map((item) => path.join(this.path, item.normalize("NFC"))),
|
|
1700
1873
|
);
|
|
1701
1874
|
for (const file of this.files.keys()) {
|
|
1702
1875
|
if (!itemPaths.has(file)) {
|
|
@@ -1730,7 +1903,7 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1730
1903
|
if (watcher.checkStartTime(this.initialScanFinished, false)) {
|
|
1731
1904
|
watcher.emit(
|
|
1732
1905
|
"initial-missing",
|
|
1733
|
-
"scan (missing in initial scan)"
|
|
1906
|
+
"scan (missing in initial scan)",
|
|
1734
1907
|
);
|
|
1735
1908
|
}
|
|
1736
1909
|
}
|
|
@@ -1756,7 +1929,7 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1756
1929
|
// TODO https://github.com/libuv/libuv/pull/4566
|
|
1757
1930
|
(err2.code === "EINVAL" && IS_WIN)
|
|
1758
1931
|
) {
|
|
1759
|
-
this.setMissing(itemPath, initial,
|
|
1932
|
+
this.setMissing(itemPath, initial, `scan (${err2.code})`);
|
|
1760
1933
|
} else {
|
|
1761
1934
|
this.onScanError(err2);
|
|
1762
1935
|
}
|
|
@@ -1765,23 +1938,25 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1765
1938
|
}
|
|
1766
1939
|
if (stats.isFile() || stats.isSymbolicLink()) {
|
|
1767
1940
|
if (stats.mtime) {
|
|
1768
|
-
ensureFsAccuracy(stats.mtime);
|
|
1941
|
+
ensureFsAccuracy(+stats.mtime);
|
|
1769
1942
|
}
|
|
1770
1943
|
this.setFileTime(
|
|
1771
1944
|
itemPath,
|
|
1772
1945
|
+stats.mtime || +stats.ctime || 1,
|
|
1773
1946
|
initial,
|
|
1774
1947
|
true,
|
|
1775
|
-
"scan (file)"
|
|
1948
|
+
"scan (file)",
|
|
1949
|
+
);
|
|
1950
|
+
} else if (
|
|
1951
|
+
stats.isDirectory() &&
|
|
1952
|
+
(!initial || !this.directories.has(itemPath))
|
|
1953
|
+
) {
|
|
1954
|
+
this.setDirectory(
|
|
1955
|
+
itemPath,
|
|
1956
|
+
+stats.birthtime || 1,
|
|
1957
|
+
initial,
|
|
1958
|
+
"scan (dir)",
|
|
1776
1959
|
);
|
|
1777
|
-
} else if (stats.isDirectory()) {
|
|
1778
|
-
if (!initial || !this.directories.has(itemPath))
|
|
1779
|
-
this.setDirectory(
|
|
1780
|
-
itemPath,
|
|
1781
|
-
+stats.birthtime || 1,
|
|
1782
|
-
initial,
|
|
1783
|
-
"scan (dir)"
|
|
1784
|
-
);
|
|
1785
1960
|
}
|
|
1786
1961
|
itemFinished();
|
|
1787
1962
|
});
|
|
@@ -1791,6 +1966,9 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1791
1966
|
});
|
|
1792
1967
|
}
|
|
1793
1968
|
|
|
1969
|
+
/**
|
|
1970
|
+
* @returns {Record<string, number>} times
|
|
1971
|
+
*/
|
|
1794
1972
|
getTimes() {
|
|
1795
1973
|
const obj = Object.create(null);
|
|
1796
1974
|
let safeTime = this.lastWatchEvent;
|
|
@@ -1801,7 +1979,9 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1801
1979
|
}
|
|
1802
1980
|
if (this.nestedWatching) {
|
|
1803
1981
|
for (const w of this.directories.values()) {
|
|
1804
|
-
const times =
|
|
1982
|
+
const times =
|
|
1983
|
+
/** @type {Watcher<DirectoryWatcherEvents>} */
|
|
1984
|
+
(w).directoryWatcher.getTimes();
|
|
1805
1985
|
for (const file of Object.keys(times)) {
|
|
1806
1986
|
const time = times[file];
|
|
1807
1987
|
safeTime = Math.max(safeTime, time);
|
|
@@ -1813,7 +1993,7 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1813
1993
|
if (!this.initialScan) {
|
|
1814
1994
|
for (const watchers of this.watchers.values()) {
|
|
1815
1995
|
for (const watcher of watchers) {
|
|
1816
|
-
const path = watcher
|
|
1996
|
+
const { path } = watcher;
|
|
1817
1997
|
if (!Object.prototype.hasOwnProperty.call(obj, path)) {
|
|
1818
1998
|
obj[path] = null;
|
|
1819
1999
|
}
|
|
@@ -1823,6 +2003,11 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1823
2003
|
return obj;
|
|
1824
2004
|
}
|
|
1825
2005
|
|
|
2006
|
+
/**
|
|
2007
|
+
* @param {TimeInfoEntries} fileTimestamps file timestamps
|
|
2008
|
+
* @param {TimeInfoEntries} directoryTimestamps directory timestamps
|
|
2009
|
+
* @returns {number} safe time
|
|
2010
|
+
*/
|
|
1826
2011
|
collectTimeInfoEntries(fileTimestamps, directoryTimestamps) {
|
|
1827
2012
|
let safeTime = this.lastWatchEvent;
|
|
1828
2013
|
for (const [file, entry] of this.files) {
|
|
@@ -1834,23 +2019,25 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1834
2019
|
for (const w of this.directories.values()) {
|
|
1835
2020
|
safeTime = Math.max(
|
|
1836
2021
|
safeTime,
|
|
1837
|
-
|
|
2022
|
+
/** @type {Watcher<DirectoryWatcherEvents>} */
|
|
2023
|
+
(w).directoryWatcher.collectTimeInfoEntries(
|
|
1838
2024
|
fileTimestamps,
|
|
1839
|
-
directoryTimestamps
|
|
1840
|
-
)
|
|
2025
|
+
directoryTimestamps,
|
|
2026
|
+
),
|
|
1841
2027
|
);
|
|
1842
2028
|
}
|
|
1843
2029
|
fileTimestamps.set(this.path, EXISTANCE_ONLY_TIME_ENTRY);
|
|
1844
2030
|
directoryTimestamps.set(this.path, {
|
|
1845
|
-
safeTime
|
|
2031
|
+
safeTime,
|
|
1846
2032
|
});
|
|
1847
2033
|
} else {
|
|
1848
2034
|
for (const dir of this.directories.keys()) {
|
|
1849
2035
|
// No additional info about this directory
|
|
1850
2036
|
// but maybe another DirectoryWatcher has info
|
|
1851
2037
|
fileTimestamps.set(dir, EXISTANCE_ONLY_TIME_ENTRY);
|
|
1852
|
-
if (!directoryTimestamps.has(dir))
|
|
2038
|
+
if (!directoryTimestamps.has(dir)) {
|
|
1853
2039
|
directoryTimestamps.set(dir, EXISTANCE_ONLY_TIME_ENTRY);
|
|
2040
|
+
}
|
|
1854
2041
|
}
|
|
1855
2042
|
fileTimestamps.set(this.path, EXISTANCE_ONLY_TIME_ENTRY);
|
|
1856
2043
|
directoryTimestamps.set(this.path, EXISTANCE_ONLY_TIME_ENTRY);
|
|
@@ -1858,7 +2045,7 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1858
2045
|
if (!this.initialScan) {
|
|
1859
2046
|
for (const watchers of this.watchers.values()) {
|
|
1860
2047
|
for (const watcher of watchers) {
|
|
1861
|
-
const path = watcher
|
|
2048
|
+
const { path } = watcher;
|
|
1862
2049
|
if (!fileTimestamps.has(path)) {
|
|
1863
2050
|
fileTimestamps.set(path, null);
|
|
1864
2051
|
}
|
|
@@ -1877,7 +2064,8 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1877
2064
|
}
|
|
1878
2065
|
if (this.nestedWatching) {
|
|
1879
2066
|
for (const w of this.directories.values()) {
|
|
1880
|
-
|
|
2067
|
+
/** @type {Watcher<DirectoryWatcherEvents>} */
|
|
2068
|
+
(w).close();
|
|
1881
2069
|
}
|
|
1882
2070
|
this.directories.clear();
|
|
1883
2071
|
}
|
|
@@ -1891,26 +2079,12 @@ class DirectoryWatcher extends EventEmitter {
|
|
|
1891
2079
|
|
|
1892
2080
|
module.exports = DirectoryWatcher;
|
|
1893
2081
|
module.exports.EXISTANCE_ONLY_TIME_ENTRY = EXISTANCE_ONLY_TIME_ENTRY;
|
|
1894
|
-
|
|
1895
|
-
function fixupEntryAccuracy(entry) {
|
|
1896
|
-
if (entry.accuracy > FS_ACCURACY) {
|
|
1897
|
-
entry.safeTime = entry.safeTime - entry.accuracy + FS_ACCURACY;
|
|
1898
|
-
entry.accuracy = FS_ACCURACY;
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
function ensureFsAccuracy(mtime) {
|
|
1903
|
-
if (!mtime) return;
|
|
1904
|
-
if (FS_ACCURACY > 1 && mtime % 1 !== 0) FS_ACCURACY = 1;
|
|
1905
|
-
else if (FS_ACCURACY > 10 && mtime % 10 !== 0) FS_ACCURACY = 10;
|
|
1906
|
-
else if (FS_ACCURACY > 100 && mtime % 100 !== 0) FS_ACCURACY = 100;
|
|
1907
|
-
else if (FS_ACCURACY > 1000 && mtime % 1000 !== 0) FS_ACCURACY = 1000;
|
|
1908
|
-
}
|
|
2082
|
+
module.exports.Watcher = Watcher;
|
|
1909
2083
|
|
|
1910
2084
|
|
|
1911
2085
|
/***/ }),
|
|
1912
2086
|
|
|
1913
|
-
/***/
|
|
2087
|
+
/***/ 703:
|
|
1914
2088
|
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
|
1915
2089
|
|
|
1916
2090
|
"use strict";
|
|
@@ -1931,12 +2105,13 @@ if (process.platform === "win32") EXPECTED_ERRORS.add("UNKNOWN");
|
|
|
1931
2105
|
|
|
1932
2106
|
class LinkResolver {
|
|
1933
2107
|
constructor() {
|
|
2108
|
+
/** @type {Map<string, readonly string[]>} */
|
|
1934
2109
|
this.cache = new Map();
|
|
1935
2110
|
}
|
|
1936
2111
|
|
|
1937
2112
|
/**
|
|
1938
2113
|
* @param {string} file path to file or directory
|
|
1939
|
-
* @returns {string[]} array of file and all symlinks contributed in the resolving process (first item is the resolved file)
|
|
2114
|
+
* @returns {readonly string[]} array of file and all symlinks contributed in the resolving process (first item is the resolved file)
|
|
1940
2115
|
*/
|
|
1941
2116
|
resolve(file) {
|
|
1942
2117
|
const cacheEntry = this.cache.get(file);
|
|
@@ -1981,17 +2156,18 @@ class LinkResolver {
|
|
|
1981
2156
|
for (let i = 1; i < parentResolved.length; i++) {
|
|
1982
2157
|
resultSet.add(parentResolved[i]);
|
|
1983
2158
|
}
|
|
1984
|
-
result = Object.freeze(
|
|
2159
|
+
result = Object.freeze([...resultSet]);
|
|
1985
2160
|
} else if (parentResolved.length > 1) {
|
|
1986
2161
|
// we have links in the parent but not for the link content location
|
|
1987
|
-
result = parentResolved
|
|
2162
|
+
result = [...parentResolved];
|
|
2163
|
+
// eslint-disable-next-line prefer-destructuring
|
|
1988
2164
|
result[0] = linkResolved[0];
|
|
1989
2165
|
// add the link
|
|
1990
2166
|
result.push(realFile);
|
|
1991
2167
|
Object.freeze(result);
|
|
1992
2168
|
} else if (linkResolved.length > 1) {
|
|
1993
2169
|
// we can return the link content location result
|
|
1994
|
-
result = linkResolved
|
|
2170
|
+
result = [...linkResolved];
|
|
1995
2171
|
// add the link
|
|
1996
2172
|
result.push(realFile);
|
|
1997
2173
|
Object.freeze(result);
|
|
@@ -2002,17 +2178,21 @@ class LinkResolver {
|
|
|
2002
2178
|
// the resolve real location
|
|
2003
2179
|
linkResolved[0],
|
|
2004
2180
|
// add the link
|
|
2005
|
-
realFile
|
|
2181
|
+
realFile,
|
|
2006
2182
|
]);
|
|
2007
2183
|
}
|
|
2008
2184
|
this.cache.set(file, result);
|
|
2009
2185
|
return result;
|
|
2010
|
-
} catch (
|
|
2011
|
-
if (
|
|
2012
|
-
|
|
2186
|
+
} catch (err) {
|
|
2187
|
+
if (
|
|
2188
|
+
/** @type {NodeJS.ErrnoException} */
|
|
2189
|
+
(err).code &&
|
|
2190
|
+
!EXPECTED_ERRORS.has(/** @type {NodeJS.ErrnoException} */ (err).code)
|
|
2191
|
+
) {
|
|
2192
|
+
throw err;
|
|
2013
2193
|
}
|
|
2014
2194
|
// no link
|
|
2015
|
-
const result = parentResolved
|
|
2195
|
+
const result = [...parentResolved];
|
|
2016
2196
|
result[0] = realFile;
|
|
2017
2197
|
Object.freeze(result);
|
|
2018
2198
|
this.cache.set(file, result);
|
|
@@ -2020,12 +2200,13 @@ class LinkResolver {
|
|
|
2020
2200
|
}
|
|
2021
2201
|
}
|
|
2022
2202
|
}
|
|
2203
|
+
|
|
2023
2204
|
module.exports = LinkResolver;
|
|
2024
2205
|
|
|
2025
2206
|
|
|
2026
2207
|
/***/ }),
|
|
2027
2208
|
|
|
2028
|
-
/***/
|
|
2209
|
+
/***/ 254:
|
|
2029
2210
|
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
|
2030
2211
|
|
|
2031
2212
|
"use strict";
|
|
@@ -2036,14 +2217,31 @@ module.exports = LinkResolver;
|
|
|
2036
2217
|
|
|
2037
2218
|
|
|
2038
2219
|
const path = __nccwpck_require__(928);
|
|
2039
|
-
const DirectoryWatcher = __nccwpck_require__(
|
|
2220
|
+
const DirectoryWatcher = __nccwpck_require__(600);
|
|
2221
|
+
|
|
2222
|
+
/** @typedef {import("./index").EventMap} EventMap */
|
|
2223
|
+
/** @typedef {import("./DirectoryWatcher").DirectoryWatcherOptions} DirectoryWatcherOptions */
|
|
2224
|
+
/** @typedef {import("./DirectoryWatcher").DirectoryWatcherEvents} DirectoryWatcherEvents */
|
|
2225
|
+
/** @typedef {import("./DirectoryWatcher").FileWatcherEvents} FileWatcherEvents */
|
|
2226
|
+
/**
|
|
2227
|
+
* @template {EventMap} T
|
|
2228
|
+
* @typedef {import("./DirectoryWatcher").Watcher<T>} Watcher
|
|
2229
|
+
*/
|
|
2040
2230
|
|
|
2041
2231
|
class WatcherManager {
|
|
2042
|
-
|
|
2232
|
+
/**
|
|
2233
|
+
* @param {DirectoryWatcherOptions=} options options
|
|
2234
|
+
*/
|
|
2235
|
+
constructor(options = {}) {
|
|
2043
2236
|
this.options = options;
|
|
2237
|
+
/** @type {Map<string, DirectoryWatcher>} */
|
|
2044
2238
|
this.directoryWatchers = new Map();
|
|
2045
2239
|
}
|
|
2046
2240
|
|
|
2241
|
+
/**
|
|
2242
|
+
* @param {string} directory a directory
|
|
2243
|
+
* @returns {DirectoryWatcher} a directory watcher
|
|
2244
|
+
*/
|
|
2047
2245
|
getDirectoryWatcher(directory) {
|
|
2048
2246
|
const watcher = this.directoryWatchers.get(directory);
|
|
2049
2247
|
if (watcher === undefined) {
|
|
@@ -2057,23 +2255,34 @@ class WatcherManager {
|
|
|
2057
2255
|
return watcher;
|
|
2058
2256
|
}
|
|
2059
2257
|
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2258
|
+
/**
|
|
2259
|
+
* @param {string} file file
|
|
2260
|
+
* @param {number=} startTime start time
|
|
2261
|
+
* @returns {Watcher<FileWatcherEvents> | null} watcher or null if file has no directory
|
|
2262
|
+
*/
|
|
2263
|
+
watchFile(file, startTime) {
|
|
2264
|
+
const directory = path.dirname(file);
|
|
2265
|
+
if (directory === file) return null;
|
|
2266
|
+
return this.getDirectoryWatcher(directory).watch(file, startTime);
|
|
2064
2267
|
}
|
|
2065
2268
|
|
|
2269
|
+
/**
|
|
2270
|
+
* @param {string} directory directory
|
|
2271
|
+
* @param {number=} startTime start time
|
|
2272
|
+
* @returns {Watcher<DirectoryWatcherEvents>} watcher
|
|
2273
|
+
*/
|
|
2066
2274
|
watchDirectory(directory, startTime) {
|
|
2067
2275
|
return this.getDirectoryWatcher(directory).watch(directory, startTime);
|
|
2068
2276
|
}
|
|
2069
2277
|
}
|
|
2070
2278
|
|
|
2071
2279
|
const watcherManagers = new WeakMap();
|
|
2280
|
+
|
|
2072
2281
|
/**
|
|
2073
|
-
* @param {
|
|
2282
|
+
* @param {DirectoryWatcherOptions} options options
|
|
2074
2283
|
* @returns {WatcherManager} the watcher manager
|
|
2075
2284
|
*/
|
|
2076
|
-
module.exports = options => {
|
|
2285
|
+
module.exports = (options) => {
|
|
2077
2286
|
const watcherManager = watcherManagers.get(options);
|
|
2078
2287
|
if (watcherManager !== undefined) return watcherManager;
|
|
2079
2288
|
const newWatcherManager = new WatcherManager(options);
|
|
@@ -2085,7 +2294,7 @@ module.exports.WatcherManager = WatcherManager;
|
|
|
2085
2294
|
|
|
2086
2295
|
/***/ }),
|
|
2087
2296
|
|
|
2088
|
-
/***/
|
|
2297
|
+
/***/ 17:
|
|
2089
2298
|
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
|
2090
2299
|
|
|
2091
2300
|
"use strict";
|
|
@@ -2095,522 +2304,705 @@ module.exports.WatcherManager = WatcherManager;
|
|
|
2095
2304
|
*/
|
|
2096
2305
|
|
|
2097
2306
|
|
|
2098
|
-
const
|
|
2307
|
+
const { EventEmitter } = __nccwpck_require__(434);
|
|
2308
|
+
const globToRegExp = __nccwpck_require__(428);
|
|
2309
|
+
const LinkResolver = __nccwpck_require__(703);
|
|
2310
|
+
const getWatcherManager = __nccwpck_require__(254);
|
|
2311
|
+
const watchEventSource = __nccwpck_require__(313);
|
|
2312
|
+
|
|
2313
|
+
/** @typedef {import("./getWatcherManager").WatcherManager} WatcherManager */
|
|
2314
|
+
/** @typedef {import("./DirectoryWatcher")} DirectoryWatcher */
|
|
2315
|
+
/** @typedef {import("./DirectoryWatcher").DirectoryWatcherEvents} DirectoryWatcherEvents */
|
|
2316
|
+
/** @typedef {import("./DirectoryWatcher").FileWatcherEvents} FileWatcherEvents */
|
|
2317
|
+
|
|
2318
|
+
// eslint-disable-next-line jsdoc/reject-any-type
|
|
2319
|
+
/** @typedef {Record<string, (...args: any[]) => any>} EventMap */
|
|
2099
2320
|
|
|
2100
2321
|
/**
|
|
2101
|
-
* @template T
|
|
2102
|
-
* @typedef {
|
|
2103
|
-
* @property {string} filePath
|
|
2104
|
-
* @property {TreeNode} parent
|
|
2105
|
-
* @property {TreeNode[]} children
|
|
2106
|
-
* @property {number} entries
|
|
2107
|
-
* @property {boolean} active
|
|
2108
|
-
* @property {T[] | T | undefined} value
|
|
2322
|
+
* @template {EventMap} T
|
|
2323
|
+
* @typedef {import("./DirectoryWatcher").Watcher<T>} Watcher
|
|
2109
2324
|
*/
|
|
2110
2325
|
|
|
2326
|
+
/** @typedef {(item: string) => boolean} IgnoredFunction */
|
|
2327
|
+
/** @typedef {string[] | RegExp | string | IgnoredFunction} Ignored */
|
|
2328
|
+
|
|
2111
2329
|
/**
|
|
2112
|
-
* @
|
|
2113
|
-
* @
|
|
2114
|
-
* @
|
|
2115
|
-
* @
|
|
2330
|
+
* @typedef {object} WatcherOptions
|
|
2331
|
+
* @property {boolean=} followSymlinks true when need to resolve symlinks and watch symlink and real file, otherwise false
|
|
2332
|
+
* @property {Ignored=} ignored ignore some files from watching (glob pattern or regexp)
|
|
2333
|
+
* @property {number | boolean=} poll true when need to enable polling mode for watching, otherwise false
|
|
2116
2334
|
*/
|
|
2117
|
-
module.exports = (plan, limit) => {
|
|
2118
|
-
const treeMap = new Map();
|
|
2119
|
-
// Convert to tree
|
|
2120
|
-
for (const [filePath, value] of plan) {
|
|
2121
|
-
treeMap.set(filePath, {
|
|
2122
|
-
filePath,
|
|
2123
|
-
parent: undefined,
|
|
2124
|
-
children: undefined,
|
|
2125
|
-
entries: 1,
|
|
2126
|
-
active: true,
|
|
2127
|
-
value
|
|
2128
|
-
});
|
|
2129
|
-
}
|
|
2130
|
-
let currentCount = treeMap.size;
|
|
2131
|
-
// Create parents and calculate sum of entries
|
|
2132
|
-
for (const node of treeMap.values()) {
|
|
2133
|
-
const parentPath = path.dirname(node.filePath);
|
|
2134
|
-
if (parentPath !== node.filePath) {
|
|
2135
|
-
let parent = treeMap.get(parentPath);
|
|
2136
|
-
if (parent === undefined) {
|
|
2137
|
-
parent = {
|
|
2138
|
-
filePath: parentPath,
|
|
2139
|
-
parent: undefined,
|
|
2140
|
-
children: [node],
|
|
2141
|
-
entries: node.entries,
|
|
2142
|
-
active: false,
|
|
2143
|
-
value: undefined
|
|
2144
|
-
};
|
|
2145
|
-
treeMap.set(parentPath, parent);
|
|
2146
|
-
node.parent = parent;
|
|
2147
|
-
} else {
|
|
2148
|
-
node.parent = parent;
|
|
2149
|
-
if (parent.children === undefined) {
|
|
2150
|
-
parent.children = [node];
|
|
2151
|
-
} else {
|
|
2152
|
-
parent.children.push(node);
|
|
2153
|
-
}
|
|
2154
|
-
do {
|
|
2155
|
-
parent.entries += node.entries;
|
|
2156
|
-
parent = parent.parent;
|
|
2157
|
-
} while (parent);
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2161
|
-
// Reduce until limit reached
|
|
2162
|
-
while (currentCount > limit) {
|
|
2163
|
-
// Select node that helps reaching the limit most effectively without overmerging
|
|
2164
|
-
const overLimit = currentCount - limit;
|
|
2165
|
-
let bestNode = undefined;
|
|
2166
|
-
let bestCost = Infinity;
|
|
2167
|
-
for (const node of treeMap.values()) {
|
|
2168
|
-
if (node.entries <= 1 || !node.children || !node.parent) continue;
|
|
2169
|
-
if (node.children.length === 0) continue;
|
|
2170
|
-
if (node.children.length === 1 && !node.value) continue;
|
|
2171
|
-
// Try to select the node with has just a bit more entries than we need to reduce
|
|
2172
|
-
// When just a bit more is over 30% over the limit,
|
|
2173
|
-
// also consider just a bit less entries then we need to reduce
|
|
2174
|
-
const cost =
|
|
2175
|
-
node.entries - 1 >= overLimit
|
|
2176
|
-
? node.entries - 1 - overLimit
|
|
2177
|
-
: overLimit - node.entries + 1 + limit * 0.3;
|
|
2178
|
-
if (cost < bestCost) {
|
|
2179
|
-
bestNode = node;
|
|
2180
|
-
bestCost = cost;
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
if (!bestNode) break;
|
|
2184
|
-
// Merge all children
|
|
2185
|
-
const reduction = bestNode.entries - 1;
|
|
2186
|
-
bestNode.active = true;
|
|
2187
|
-
bestNode.entries = 1;
|
|
2188
|
-
currentCount -= reduction;
|
|
2189
|
-
let parent = bestNode.parent;
|
|
2190
|
-
while (parent) {
|
|
2191
|
-
parent.entries -= reduction;
|
|
2192
|
-
parent = parent.parent;
|
|
2193
|
-
}
|
|
2194
|
-
const queue = new Set(bestNode.children);
|
|
2195
|
-
for (const node of queue) {
|
|
2196
|
-
node.active = false;
|
|
2197
|
-
node.entries = 0;
|
|
2198
|
-
if (node.children) {
|
|
2199
|
-
for (const child of node.children) queue.add(child);
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
// Write down new plan
|
|
2204
|
-
const newPlan = new Map();
|
|
2205
|
-
for (const rootNode of treeMap.values()) {
|
|
2206
|
-
if (!rootNode.active) continue;
|
|
2207
|
-
const map = new Map();
|
|
2208
|
-
const queue = new Set([rootNode]);
|
|
2209
|
-
for (const node of queue) {
|
|
2210
|
-
if (node.active && node !== rootNode) continue;
|
|
2211
|
-
if (node.value) {
|
|
2212
|
-
if (Array.isArray(node.value)) {
|
|
2213
|
-
for (const item of node.value) {
|
|
2214
|
-
map.set(item, node.filePath);
|
|
2215
|
-
}
|
|
2216
|
-
} else {
|
|
2217
|
-
map.set(node.value, node.filePath);
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
if (node.children) {
|
|
2221
|
-
for (const child of node.children) {
|
|
2222
|
-
queue.add(child);
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
}
|
|
2226
|
-
newPlan.set(rootNode.filePath, map);
|
|
2227
|
-
}
|
|
2228
|
-
return newPlan;
|
|
2229
|
-
};
|
|
2230
2335
|
|
|
2336
|
+
/** @typedef {WatcherOptions & { aggregateTimeout?: number }} WatchOptions */
|
|
2231
2337
|
|
|
2232
|
-
|
|
2338
|
+
/**
|
|
2339
|
+
* @typedef {object} NormalizedWatchOptions
|
|
2340
|
+
* @property {boolean} followSymlinks true when need to resolve symlinks and watch symlink and real file, otherwise false
|
|
2341
|
+
* @property {IgnoredFunction} ignored ignore some files from watching (glob pattern or regexp)
|
|
2342
|
+
* @property {number | boolean=} poll true when need to enable polling mode for watching, otherwise false
|
|
2343
|
+
*/
|
|
2233
2344
|
|
|
2234
|
-
|
|
2235
|
-
|
|
2345
|
+
/** @typedef {`scan (${string})` | "change" | "rename" | `watch ${string}` | `directory-removed ${string}`} EventType */
|
|
2346
|
+
/** @typedef {{ safeTime: number, timestamp: number, accuracy: number }} Entry */
|
|
2347
|
+
/** @typedef {{ safeTime: number }} OnlySafeTimeEntry */
|
|
2348
|
+
// eslint-disable-next-line jsdoc/ts-no-empty-object-type
|
|
2349
|
+
/** @typedef {{}} ExistenceOnlyTimeEntry */
|
|
2350
|
+
/** @typedef {Map<string, Entry | OnlySafeTimeEntry | ExistenceOnlyTimeEntry | null>} TimeInfoEntries */
|
|
2351
|
+
/** @typedef {Set<string>} Changes */
|
|
2352
|
+
/** @typedef {Set<string>} Removals */
|
|
2353
|
+
/** @typedef {{ changes: Changes, removals: Removals }} Aggregated */
|
|
2354
|
+
/** @typedef {{ files?: Iterable<string>, directories?: Iterable<string>, missing?: Iterable<string>, startTime?: number }} WatchMethodOptions */
|
|
2355
|
+
/** @typedef {Record<string, number>} Times */
|
|
2236
2356
|
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2357
|
+
/**
|
|
2358
|
+
* @param {MapIterator<WatchpackFileWatcher> | MapIterator<WatchpackDirectoryWatcher>} watchers watchers
|
|
2359
|
+
* @param {Set<DirectoryWatcher>} set set
|
|
2360
|
+
*/
|
|
2361
|
+
function addWatchersToSet(watchers, set) {
|
|
2362
|
+
for (const ww of watchers) {
|
|
2363
|
+
const w = ww.watcher;
|
|
2364
|
+
if (!set.has(w.directoryWatcher)) {
|
|
2365
|
+
set.add(w.directoryWatcher);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2242
2369
|
|
|
2370
|
+
/**
|
|
2371
|
+
* @param {string} ignored ignored
|
|
2372
|
+
* @returns {string | undefined} resolved global to regexp
|
|
2373
|
+
*/
|
|
2374
|
+
const stringToRegexp = (ignored) => {
|
|
2375
|
+
if (ignored.length === 0) {
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
const { source } = globToRegExp(ignored, { globstar: true, extended: true });
|
|
2379
|
+
return `${source.slice(0, -1)}(?:$|\\/)`;
|
|
2380
|
+
};
|
|
2243
2381
|
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
const
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2382
|
+
/**
|
|
2383
|
+
* @param {Ignored=} ignored ignored
|
|
2384
|
+
* @returns {(item: string) => boolean} ignored to function
|
|
2385
|
+
*/
|
|
2386
|
+
const ignoredToFunction = (ignored) => {
|
|
2387
|
+
if (Array.isArray(ignored)) {
|
|
2388
|
+
const stringRegexps = ignored.map((i) => stringToRegexp(i)).filter(Boolean);
|
|
2389
|
+
if (stringRegexps.length === 0) {
|
|
2390
|
+
return () => false;
|
|
2391
|
+
}
|
|
2392
|
+
const regexp = new RegExp(stringRegexps.join("|"));
|
|
2393
|
+
return (item) => regexp.test(item.replace(/\\/g, "/"));
|
|
2394
|
+
} else if (typeof ignored === "string") {
|
|
2395
|
+
const stringRegexp = stringToRegexp(ignored);
|
|
2396
|
+
if (!stringRegexp) {
|
|
2397
|
+
return () => false;
|
|
2398
|
+
}
|
|
2399
|
+
const regexp = new RegExp(stringRegexp);
|
|
2400
|
+
return (item) => regexp.test(item.replace(/\\/g, "/"));
|
|
2401
|
+
} else if (ignored instanceof RegExp) {
|
|
2402
|
+
return (item) => ignored.test(item.replace(/\\/g, "/"));
|
|
2403
|
+
} else if (typeof ignored === "function") {
|
|
2404
|
+
return ignored;
|
|
2405
|
+
} else if (ignored) {
|
|
2406
|
+
throw new Error(`Invalid option for 'ignored': ${ignored}`);
|
|
2407
|
+
} else {
|
|
2408
|
+
return () => false;
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2263
2411
|
|
|
2264
|
-
/**
|
|
2265
|
-
|
|
2412
|
+
/**
|
|
2413
|
+
* @param {WatchOptions} options options
|
|
2414
|
+
* @returns {NormalizedWatchOptions} normalized options
|
|
2415
|
+
*/
|
|
2416
|
+
const normalizeOptions = (options) => ({
|
|
2417
|
+
followSymlinks: Boolean(options.followSymlinks),
|
|
2418
|
+
ignored: ignoredToFunction(options.ignored),
|
|
2419
|
+
poll: options.poll,
|
|
2420
|
+
});
|
|
2266
2421
|
|
|
2267
|
-
|
|
2268
|
-
|
|
2422
|
+
const normalizeCache = new WeakMap();
|
|
2423
|
+
/**
|
|
2424
|
+
* @param {WatchOptions} options options
|
|
2425
|
+
* @returns {NormalizedWatchOptions} normalized options
|
|
2426
|
+
*/
|
|
2427
|
+
const cachedNormalizeOptions = (options) => {
|
|
2428
|
+
const cacheEntry = normalizeCache.get(options);
|
|
2429
|
+
if (cacheEntry !== undefined) return cacheEntry;
|
|
2430
|
+
const normalized = normalizeOptions(options);
|
|
2431
|
+
normalizeCache.set(options, normalized);
|
|
2432
|
+
return normalized;
|
|
2433
|
+
};
|
|
2269
2434
|
|
|
2270
|
-
|
|
2271
|
-
|
|
2435
|
+
class WatchpackFileWatcher {
|
|
2436
|
+
/**
|
|
2437
|
+
* @param {Watchpack} watchpack watchpack
|
|
2438
|
+
* @param {Watcher<FileWatcherEvents>} watcher watcher
|
|
2439
|
+
* @param {string | string[]} files files
|
|
2440
|
+
*/
|
|
2441
|
+
constructor(watchpack, watcher, files) {
|
|
2442
|
+
/** @type {string[]} */
|
|
2443
|
+
this.files = Array.isArray(files) ? files : [files];
|
|
2444
|
+
this.watcher = watcher;
|
|
2445
|
+
watcher.on("initial-missing", (type) => {
|
|
2446
|
+
for (const file of this.files) {
|
|
2447
|
+
if (!watchpack._missing.has(file)) {
|
|
2448
|
+
watchpack._onRemove(file, file, type);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
});
|
|
2452
|
+
watcher.on("change", (mtime, type, _initial) => {
|
|
2453
|
+
for (const file of this.files) {
|
|
2454
|
+
watchpack._onChange(file, mtime, file, type);
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2457
|
+
watcher.on("remove", (type) => {
|
|
2458
|
+
for (const file of this.files) {
|
|
2459
|
+
watchpack._onRemove(file, file, type);
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2272
2463
|
|
|
2273
|
-
/**
|
|
2274
|
-
|
|
2464
|
+
/**
|
|
2465
|
+
* @param {string | string[]} files files
|
|
2466
|
+
*/
|
|
2467
|
+
update(files) {
|
|
2468
|
+
if (!Array.isArray(files)) {
|
|
2469
|
+
if (this.files.length !== 1) {
|
|
2470
|
+
this.files = [files];
|
|
2471
|
+
} else if (this.files[0] !== files) {
|
|
2472
|
+
this.files[0] = files;
|
|
2473
|
+
}
|
|
2474
|
+
} else {
|
|
2475
|
+
this.files = files;
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2275
2478
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
return error;
|
|
2479
|
+
close() {
|
|
2480
|
+
this.watcher.close();
|
|
2481
|
+
}
|
|
2280
2482
|
}
|
|
2281
2483
|
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2484
|
+
class WatchpackDirectoryWatcher {
|
|
2485
|
+
/**
|
|
2486
|
+
* @param {Watchpack} watchpack watchpack
|
|
2487
|
+
* @param {Watcher<DirectoryWatcherEvents>} watcher watcher
|
|
2488
|
+
* @param {string} directories directories
|
|
2489
|
+
*/
|
|
2490
|
+
constructor(watchpack, watcher, directories) {
|
|
2491
|
+
/** @type {string[]} */
|
|
2492
|
+
this.directories = Array.isArray(directories) ? directories : [directories];
|
|
2493
|
+
this.watcher = watcher;
|
|
2494
|
+
watcher.on("initial-missing", (type) => {
|
|
2495
|
+
for (const item of this.directories) {
|
|
2496
|
+
watchpack._onRemove(item, item, type);
|
|
2295
2497
|
}
|
|
2296
|
-
|
|
2297
|
-
|
|
2498
|
+
});
|
|
2499
|
+
watcher.on("change", (file, mtime, type, _initial) => {
|
|
2500
|
+
for (const item of this.directories) {
|
|
2501
|
+
watchpack._onChange(item, mtime, file, type);
|
|
2502
|
+
}
|
|
2503
|
+
});
|
|
2504
|
+
watcher.on("remove", (type) => {
|
|
2505
|
+
for (const item of this.directories) {
|
|
2506
|
+
watchpack._onRemove(item, item, type);
|
|
2507
|
+
}
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
/**
|
|
2512
|
+
* @param {string | string[]} directories directories
|
|
2513
|
+
*/
|
|
2514
|
+
update(directories) {
|
|
2515
|
+
if (!Array.isArray(directories)) {
|
|
2516
|
+
if (this.directories.length !== 1) {
|
|
2517
|
+
this.directories = [directories];
|
|
2518
|
+
} else if (this.directories[0] !== directories) {
|
|
2519
|
+
this.directories[0] = directories;
|
|
2520
|
+
}
|
|
2521
|
+
} else {
|
|
2522
|
+
this.directories = directories;
|
|
2298
2523
|
}
|
|
2299
|
-
|
|
2300
|
-
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
close() {
|
|
2527
|
+
this.watcher.close();
|
|
2528
|
+
}
|
|
2301
2529
|
}
|
|
2302
2530
|
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
const watcher = fs.watch(filePath);
|
|
2531
|
+
/**
|
|
2532
|
+
* @typedef {object} WatchpackEvents
|
|
2533
|
+
* @property {(file: string, mtime: number, type: EventType) => void} change change event
|
|
2534
|
+
* @property {(file: string, type: EventType) => void} remove remove event
|
|
2535
|
+
* @property {(changes: Changes, removals: Removals) => void} aggregated aggregated event
|
|
2536
|
+
*/
|
|
2310
2537
|
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2538
|
+
/**
|
|
2539
|
+
* @extends {EventEmitter<{ [K in keyof WatchpackEvents]: Parameters<WatchpackEvents[K]> }>}
|
|
2540
|
+
*/
|
|
2541
|
+
class Watchpack extends EventEmitter {
|
|
2542
|
+
/**
|
|
2543
|
+
* @param {WatchOptions=} options options
|
|
2544
|
+
*/
|
|
2545
|
+
constructor(options = {}) {
|
|
2546
|
+
super();
|
|
2547
|
+
if (!options) options = {};
|
|
2548
|
+
/** @type {WatchOptions} */
|
|
2549
|
+
this.options = options;
|
|
2550
|
+
this.aggregateTimeout =
|
|
2551
|
+
typeof options.aggregateTimeout === "number"
|
|
2552
|
+
? options.aggregateTimeout
|
|
2553
|
+
: 200;
|
|
2554
|
+
/** @type {NormalizedWatchOptions} */
|
|
2555
|
+
this.watcherOptions = cachedNormalizeOptions(options);
|
|
2556
|
+
/** @type {WatcherManager} */
|
|
2557
|
+
this.watcherManager = getWatcherManager(this.watcherOptions);
|
|
2558
|
+
/** @type {Map<string, WatchpackFileWatcher>} */
|
|
2559
|
+
this.fileWatchers = new Map();
|
|
2560
|
+
/** @type {Map<string, WatchpackDirectoryWatcher>} */
|
|
2561
|
+
this.directoryWatchers = new Map();
|
|
2562
|
+
/** @type {Set<string>} */
|
|
2563
|
+
this._missing = new Set();
|
|
2564
|
+
this.startTime = undefined;
|
|
2565
|
+
this.paused = false;
|
|
2566
|
+
/** @type {Changes} */
|
|
2567
|
+
this.aggregatedChanges = new Set();
|
|
2568
|
+
/** @type {Removals} */
|
|
2569
|
+
this.aggregatedRemovals = new Set();
|
|
2570
|
+
/** @type {undefined | NodeJS.Timeout} */
|
|
2571
|
+
this.aggregateTimer = undefined;
|
|
2572
|
+
this._onTimeout = this._onTimeout.bind(this);
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
/**
|
|
2576
|
+
* @overload
|
|
2577
|
+
* @param {Iterable<string>} arg1 files
|
|
2578
|
+
* @param {Iterable<string>} arg2 directories
|
|
2579
|
+
* @param {number=} arg3 startTime
|
|
2580
|
+
* @returns {void}
|
|
2581
|
+
*/
|
|
2582
|
+
/**
|
|
2583
|
+
* @overload
|
|
2584
|
+
* @param {WatchMethodOptions} arg1 watch options
|
|
2585
|
+
* @returns {void}
|
|
2586
|
+
*/
|
|
2587
|
+
/**
|
|
2588
|
+
* @param {Iterable<string> | WatchMethodOptions} arg1 files
|
|
2589
|
+
* @param {Iterable<string>=} arg2 directories
|
|
2590
|
+
* @param {number=} arg3 startTime
|
|
2591
|
+
* @returns {void}
|
|
2592
|
+
*/
|
|
2593
|
+
watch(arg1, arg2, arg3) {
|
|
2594
|
+
/** @type {Iterable<string> | undefined} */
|
|
2595
|
+
let files;
|
|
2596
|
+
/** @type {Iterable<string> | undefined} */
|
|
2597
|
+
let directories;
|
|
2598
|
+
/** @type {Iterable<string> | undefined} */
|
|
2599
|
+
let missing;
|
|
2600
|
+
/** @type {number | undefined} */
|
|
2601
|
+
let startTime;
|
|
2602
|
+
if (!arg2) {
|
|
2603
|
+
({
|
|
2604
|
+
files = [],
|
|
2605
|
+
directories = [],
|
|
2606
|
+
missing = [],
|
|
2607
|
+
startTime,
|
|
2608
|
+
} = /** @type {WatchMethodOptions} */ (arg1));
|
|
2609
|
+
} else {
|
|
2610
|
+
files = /** @type {Iterable<string>} */ (arg1);
|
|
2611
|
+
directories = /** @type {Iterable<string>} */ (arg2);
|
|
2612
|
+
missing = [];
|
|
2613
|
+
startTime = /** @type {number} */ (arg3);
|
|
2614
|
+
}
|
|
2615
|
+
this.paused = false;
|
|
2616
|
+
const { fileWatchers, directoryWatchers } = this;
|
|
2617
|
+
const { ignored } = this.watcherOptions;
|
|
2618
|
+
/**
|
|
2619
|
+
* @param {string} path path
|
|
2620
|
+
* @returns {boolean} true when need to filter, otherwise false
|
|
2621
|
+
*/
|
|
2622
|
+
const filter = (path) => !ignored(path);
|
|
2623
|
+
/**
|
|
2624
|
+
* @template K, V
|
|
2625
|
+
* @param {Map<K, V | V[]>} map map
|
|
2626
|
+
* @param {K} key key
|
|
2627
|
+
* @param {V} item item
|
|
2628
|
+
*/
|
|
2629
|
+
const addToMap = (map, key, item) => {
|
|
2630
|
+
const list = map.get(key);
|
|
2631
|
+
if (list === undefined) {
|
|
2632
|
+
map.set(key, item);
|
|
2633
|
+
} else if (Array.isArray(list)) {
|
|
2634
|
+
list.push(item);
|
|
2635
|
+
} else {
|
|
2636
|
+
map.set(key, [list, item]);
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
const fileWatchersNeeded = new Map();
|
|
2640
|
+
const directoryWatchersNeeded = new Map();
|
|
2641
|
+
/** @type {Set<string>} */
|
|
2642
|
+
const missingFiles = new Set();
|
|
2643
|
+
if (this.watcherOptions.followSymlinks) {
|
|
2644
|
+
const resolver = new LinkResolver();
|
|
2645
|
+
for (const file of files) {
|
|
2646
|
+
if (filter(file)) {
|
|
2647
|
+
for (const innerFile of resolver.resolve(file)) {
|
|
2648
|
+
if (file === innerFile || filter(innerFile)) {
|
|
2649
|
+
addToMap(fileWatchersNeeded, innerFile, file);
|
|
2650
|
+
}
|
|
2318
2651
|
}
|
|
2319
2652
|
}
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2653
|
+
}
|
|
2654
|
+
for (const file of missing) {
|
|
2655
|
+
if (filter(file)) {
|
|
2656
|
+
for (const innerFile of resolver.resolve(file)) {
|
|
2657
|
+
if (file === innerFile || filter(innerFile)) {
|
|
2658
|
+
missingFiles.add(file);
|
|
2659
|
+
addToMap(fileWatchersNeeded, innerFile, file);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
for (const dir of directories) {
|
|
2665
|
+
if (filter(dir)) {
|
|
2666
|
+
let first = true;
|
|
2667
|
+
for (const innerItem of resolver.resolve(dir)) {
|
|
2668
|
+
if (filter(innerItem)) {
|
|
2669
|
+
addToMap(
|
|
2670
|
+
first ? directoryWatchersNeeded : fileWatchersNeeded,
|
|
2671
|
+
innerItem,
|
|
2672
|
+
dir,
|
|
2673
|
+
);
|
|
2674
|
+
}
|
|
2675
|
+
first = false;
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
} else {
|
|
2680
|
+
for (const file of files) {
|
|
2681
|
+
if (filter(file)) {
|
|
2682
|
+
addToMap(fileWatchersNeeded, file, file);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
for (const file of missing) {
|
|
2686
|
+
if (filter(file)) {
|
|
2687
|
+
missingFiles.add(file);
|
|
2688
|
+
addToMap(fileWatchersNeeded, file, file);
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
for (const dir of directories) {
|
|
2692
|
+
if (filter(dir)) {
|
|
2693
|
+
addToMap(directoryWatchersNeeded, dir, dir);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
// Close unneeded old watchers
|
|
2698
|
+
// and update existing watchers
|
|
2699
|
+
for (const [key, w] of fileWatchers) {
|
|
2700
|
+
const needed = fileWatchersNeeded.get(key);
|
|
2701
|
+
if (needed === undefined) {
|
|
2702
|
+
w.close();
|
|
2703
|
+
fileWatchers.delete(key);
|
|
2704
|
+
} else {
|
|
2705
|
+
w.update(needed);
|
|
2706
|
+
fileWatchersNeeded.delete(key);
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
for (const [key, w] of directoryWatchers) {
|
|
2710
|
+
const needed = directoryWatchersNeeded.get(key);
|
|
2711
|
+
if (needed === undefined) {
|
|
2712
|
+
w.close();
|
|
2713
|
+
directoryWatchers.delete(key);
|
|
2714
|
+
} else {
|
|
2715
|
+
w.update(needed);
|
|
2716
|
+
directoryWatchersNeeded.delete(key);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
// Create new watchers and install handlers on these watchers
|
|
2720
|
+
watchEventSource.batch(() => {
|
|
2721
|
+
for (const [key, files] of fileWatchersNeeded) {
|
|
2722
|
+
const watcher = this.watcherManager.watchFile(key, startTime);
|
|
2723
|
+
if (watcher) {
|
|
2724
|
+
fileWatchers.set(key, new WatchpackFileWatcher(this, watcher, files));
|
|
2325
2725
|
}
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2726
|
+
}
|
|
2727
|
+
for (const [key, directories] of directoryWatchersNeeded) {
|
|
2728
|
+
const watcher = this.watcherManager.watchDirectory(key, startTime);
|
|
2729
|
+
if (watcher) {
|
|
2730
|
+
directoryWatchers.set(
|
|
2731
|
+
key,
|
|
2732
|
+
new WatchpackDirectoryWatcher(this, watcher, directories),
|
|
2733
|
+
);
|
|
2331
2734
|
}
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2735
|
+
}
|
|
2736
|
+
});
|
|
2737
|
+
this._missing = missingFiles;
|
|
2738
|
+
this.startTime = startTime;
|
|
2335
2739
|
}
|
|
2336
2740
|
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
this.
|
|
2741
|
+
close() {
|
|
2742
|
+
this.paused = true;
|
|
2743
|
+
if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
|
|
2744
|
+
for (const w of this.fileWatchers.values()) w.close();
|
|
2745
|
+
for (const w of this.directoryWatchers.values()) w.close();
|
|
2746
|
+
this.fileWatchers.clear();
|
|
2747
|
+
this.directoryWatchers.clear();
|
|
2340
2748
|
}
|
|
2341
2749
|
|
|
2342
|
-
|
|
2343
|
-
this.
|
|
2344
|
-
if (this.
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2750
|
+
pause() {
|
|
2751
|
+
this.paused = true;
|
|
2752
|
+
if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
/**
|
|
2756
|
+
* @returns {Record<string, number>} times
|
|
2757
|
+
*/
|
|
2758
|
+
getTimes() {
|
|
2759
|
+
/** @type {Set<DirectoryWatcher>} */
|
|
2760
|
+
const directoryWatchers = new Set();
|
|
2761
|
+
addWatchersToSet(this.fileWatchers.values(), directoryWatchers);
|
|
2762
|
+
addWatchersToSet(this.directoryWatchers.values(), directoryWatchers);
|
|
2763
|
+
/** @type {Record<string, number>} */
|
|
2764
|
+
const obj = Object.create(null);
|
|
2765
|
+
for (const w of directoryWatchers) {
|
|
2766
|
+
const times = w.getTimes();
|
|
2767
|
+
for (const file of Object.keys(times)) obj[file] = times[file];
|
|
2348
2768
|
}
|
|
2769
|
+
return obj;
|
|
2349
2770
|
}
|
|
2350
2771
|
|
|
2351
|
-
|
|
2352
|
-
|
|
2772
|
+
/**
|
|
2773
|
+
* @returns {TimeInfoEntries} time info entries
|
|
2774
|
+
*/
|
|
2775
|
+
getTimeInfoEntries() {
|
|
2776
|
+
/** @type {TimeInfoEntries} */
|
|
2777
|
+
const map = new Map();
|
|
2778
|
+
this.collectTimeInfoEntries(map, map);
|
|
2779
|
+
return map;
|
|
2353
2780
|
}
|
|
2354
|
-
}
|
|
2355
2781
|
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
/** @type {
|
|
2362
|
-
|
|
2363
|
-
this.
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
});
|
|
2368
|
-
this.watcher = watcher;
|
|
2369
|
-
watcher.on("change", (type, filename) => {
|
|
2370
|
-
if (!filename) {
|
|
2371
|
-
if (recursiveWatcherLogging) {
|
|
2372
|
-
process.stderr.write(
|
|
2373
|
-
`[watchpack] dispatch ${type} event in recursive watcher (${this.rootPath}) to all watchers\n`
|
|
2374
|
-
);
|
|
2375
|
-
}
|
|
2376
|
-
for (const w of this.mapWatcherToPath.keys()) {
|
|
2377
|
-
w.emit("change", type);
|
|
2378
|
-
}
|
|
2379
|
-
} else {
|
|
2380
|
-
const dir = path.dirname(filename);
|
|
2381
|
-
const watchers = this.mapPathToWatchers.get(dir);
|
|
2382
|
-
if (recursiveWatcherLogging) {
|
|
2383
|
-
process.stderr.write(
|
|
2384
|
-
`[watchpack] dispatch ${type} event in recursive watcher (${
|
|
2385
|
-
this.rootPath
|
|
2386
|
-
}) for '${filename}' to ${
|
|
2387
|
-
watchers ? watchers.size : 0
|
|
2388
|
-
} watchers\n`
|
|
2389
|
-
);
|
|
2390
|
-
}
|
|
2391
|
-
if (watchers === undefined) return;
|
|
2392
|
-
for (const w of watchers) {
|
|
2393
|
-
w.emit("change", type, path.basename(filename));
|
|
2394
|
-
}
|
|
2395
|
-
}
|
|
2396
|
-
});
|
|
2397
|
-
watcher.on("error", error => {
|
|
2398
|
-
for (const w of this.mapWatcherToPath.keys()) {
|
|
2399
|
-
w.emit("error", error);
|
|
2400
|
-
}
|
|
2401
|
-
});
|
|
2402
|
-
} catch (err) {
|
|
2403
|
-
process.nextTick(() => {
|
|
2404
|
-
for (const w of this.mapWatcherToPath.keys()) {
|
|
2405
|
-
w.emit("error", err);
|
|
2406
|
-
}
|
|
2407
|
-
});
|
|
2408
|
-
}
|
|
2409
|
-
watcherCount++;
|
|
2410
|
-
if (recursiveWatcherLogging) {
|
|
2411
|
-
process.stderr.write(
|
|
2412
|
-
`[watchpack] created recursive watcher at ${rootPath}\n`
|
|
2413
|
-
);
|
|
2782
|
+
/**
|
|
2783
|
+
* @param {TimeInfoEntries} fileTimestamps file timestamps
|
|
2784
|
+
* @param {TimeInfoEntries} directoryTimestamps directory timestamps
|
|
2785
|
+
*/
|
|
2786
|
+
collectTimeInfoEntries(fileTimestamps, directoryTimestamps) {
|
|
2787
|
+
/** @type {Set<DirectoryWatcher>} */
|
|
2788
|
+
const allWatchers = new Set();
|
|
2789
|
+
addWatchersToSet(this.fileWatchers.values(), allWatchers);
|
|
2790
|
+
addWatchersToSet(this.directoryWatchers.values(), allWatchers);
|
|
2791
|
+
for (const w of allWatchers) {
|
|
2792
|
+
w.collectTimeInfoEntries(fileTimestamps, directoryTimestamps);
|
|
2414
2793
|
}
|
|
2415
2794
|
}
|
|
2416
2795
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
newSet.add(watcher);
|
|
2425
|
-
this.mapPathToWatchers.set(subpath, newSet);
|
|
2426
|
-
} else {
|
|
2427
|
-
set.add(watcher);
|
|
2796
|
+
/**
|
|
2797
|
+
* @returns {Aggregated} aggregated info
|
|
2798
|
+
*/
|
|
2799
|
+
getAggregated() {
|
|
2800
|
+
if (this.aggregateTimer) {
|
|
2801
|
+
clearTimeout(this.aggregateTimer);
|
|
2802
|
+
this.aggregateTimer = undefined;
|
|
2428
2803
|
}
|
|
2804
|
+
const changes = this.aggregatedChanges;
|
|
2805
|
+
const removals = this.aggregatedRemovals;
|
|
2806
|
+
this.aggregatedChanges = new Set();
|
|
2807
|
+
this.aggregatedRemovals = new Set();
|
|
2808
|
+
return { changes, removals };
|
|
2429
2809
|
}
|
|
2430
2810
|
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
if (this.watcher) this.watcher.close();
|
|
2444
|
-
if (recursiveWatcherLogging) {
|
|
2445
|
-
process.stderr.write(
|
|
2446
|
-
`[watchpack] closed recursive watcher at ${this.rootPath}\n`
|
|
2447
|
-
);
|
|
2448
|
-
}
|
|
2811
|
+
/**
|
|
2812
|
+
* @param {string} item item
|
|
2813
|
+
* @param {number} mtime mtime
|
|
2814
|
+
* @param {string} file file
|
|
2815
|
+
* @param {EventType} type type
|
|
2816
|
+
*/
|
|
2817
|
+
_onChange(item, mtime, file, type) {
|
|
2818
|
+
file = file || item;
|
|
2819
|
+
if (!this.paused) {
|
|
2820
|
+
this.emit("change", file, mtime, type);
|
|
2821
|
+
if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
|
|
2822
|
+
this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout);
|
|
2449
2823
|
}
|
|
2824
|
+
this.aggregatedRemovals.delete(item);
|
|
2825
|
+
this.aggregatedChanges.add(item);
|
|
2450
2826
|
}
|
|
2451
2827
|
|
|
2452
|
-
|
|
2453
|
-
|
|
2828
|
+
/**
|
|
2829
|
+
* @param {string} item item
|
|
2830
|
+
* @param {string} file file
|
|
2831
|
+
* @param {EventType} type type
|
|
2832
|
+
*/
|
|
2833
|
+
_onRemove(item, file, type) {
|
|
2834
|
+
file = file || item;
|
|
2835
|
+
if (!this.paused) {
|
|
2836
|
+
this.emit("remove", file, type);
|
|
2837
|
+
if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
|
|
2838
|
+
this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout);
|
|
2839
|
+
}
|
|
2840
|
+
this.aggregatedChanges.delete(item);
|
|
2841
|
+
this.aggregatedRemovals.add(item);
|
|
2454
2842
|
}
|
|
2455
|
-
}
|
|
2456
2843
|
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
watcher.remove(this);
|
|
2465
|
-
underlyingWatcher.delete(this);
|
|
2844
|
+
_onTimeout() {
|
|
2845
|
+
this.aggregateTimer = undefined;
|
|
2846
|
+
const changes = this.aggregatedChanges;
|
|
2847
|
+
const removals = this.aggregatedRemovals;
|
|
2848
|
+
this.aggregatedChanges = new Set();
|
|
2849
|
+
this.aggregatedRemovals = new Set();
|
|
2850
|
+
this.emit("aggregated", changes, removals);
|
|
2466
2851
|
}
|
|
2467
2852
|
}
|
|
2468
2853
|
|
|
2469
|
-
|
|
2470
|
-
const existing = directWatchers.get(filePath);
|
|
2471
|
-
if (existing !== undefined) return existing;
|
|
2472
|
-
const w = new DirectWatcher(filePath);
|
|
2473
|
-
directWatchers.set(filePath, w);
|
|
2474
|
-
return w;
|
|
2475
|
-
};
|
|
2854
|
+
module.exports = Watchpack;
|
|
2476
2855
|
|
|
2477
|
-
const createRecursiveWatcher = rootPath => {
|
|
2478
|
-
const existing = recursiveWatchers.get(rootPath);
|
|
2479
|
-
if (existing !== undefined) return existing;
|
|
2480
|
-
const w = new RecursiveWatcher(rootPath);
|
|
2481
|
-
recursiveWatchers.set(rootPath, w);
|
|
2482
|
-
return w;
|
|
2483
|
-
};
|
|
2484
2856
|
|
|
2485
|
-
|
|
2486
|
-
/** @type {Map<string, Watcher[] | Watcher>} */
|
|
2487
|
-
const map = new Map();
|
|
2488
|
-
const addWatcher = (watcher, filePath) => {
|
|
2489
|
-
const entry = map.get(filePath);
|
|
2490
|
-
if (entry === undefined) {
|
|
2491
|
-
map.set(filePath, watcher);
|
|
2492
|
-
} else if (Array.isArray(entry)) {
|
|
2493
|
-
entry.push(watcher);
|
|
2494
|
-
} else {
|
|
2495
|
-
map.set(filePath, [entry, watcher]);
|
|
2496
|
-
}
|
|
2497
|
-
};
|
|
2498
|
-
for (const [watcher, filePath] of pendingWatchers) {
|
|
2499
|
-
addWatcher(watcher, filePath);
|
|
2500
|
-
}
|
|
2501
|
-
pendingWatchers.clear();
|
|
2857
|
+
/***/ }),
|
|
2502
2858
|
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2859
|
+
/***/ 240:
|
|
2860
|
+
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
|
2861
|
+
|
|
2862
|
+
"use strict";
|
|
2863
|
+
/*
|
|
2864
|
+
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
2865
|
+
Author Tobias Koppers @sokra
|
|
2866
|
+
*/
|
|
2867
|
+
|
|
2868
|
+
|
|
2869
|
+
const path = __nccwpck_require__(928);
|
|
2870
|
+
|
|
2871
|
+
/**
|
|
2872
|
+
* @template T
|
|
2873
|
+
* @typedef {object} TreeNode
|
|
2874
|
+
* @property {string} target target
|
|
2875
|
+
* @property {TreeNode<T>} parent parent
|
|
2876
|
+
* @property {TreeNode<T>[]} children children
|
|
2877
|
+
* @property {number} entries number of entries
|
|
2878
|
+
* @property {boolean} active true when active, otherwise false
|
|
2879
|
+
* @property {T[] | T | undefined} value value
|
|
2880
|
+
*/
|
|
2881
|
+
|
|
2882
|
+
/**
|
|
2883
|
+
* @template T
|
|
2884
|
+
* @param {Map<string, T[] | T>} plan plan
|
|
2885
|
+
* @param {number} limit limit
|
|
2886
|
+
* @returns {Map<string, Map<T, string>>} the new plan
|
|
2887
|
+
*/
|
|
2888
|
+
module.exports = (plan, limit) => {
|
|
2889
|
+
const treeMap = new Map();
|
|
2890
|
+
// Convert to tree
|
|
2891
|
+
for (const [target, value] of plan) {
|
|
2892
|
+
treeMap.set(target, {
|
|
2893
|
+
target,
|
|
2894
|
+
parent: undefined,
|
|
2895
|
+
children: undefined,
|
|
2896
|
+
entries: 1,
|
|
2897
|
+
active: true,
|
|
2898
|
+
value,
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
let currentCount = treeMap.size;
|
|
2902
|
+
// Create parents and calculate sum of entries
|
|
2903
|
+
for (const node of treeMap.values()) {
|
|
2904
|
+
const parentPath = path.dirname(node.target);
|
|
2905
|
+
if (parentPath !== node.target) {
|
|
2906
|
+
let parent = treeMap.get(parentPath);
|
|
2907
|
+
if (parent === undefined) {
|
|
2908
|
+
parent = {
|
|
2909
|
+
target: parentPath,
|
|
2910
|
+
parent: undefined,
|
|
2911
|
+
children: [node],
|
|
2912
|
+
entries: node.entries,
|
|
2913
|
+
active: false,
|
|
2914
|
+
value: undefined,
|
|
2915
|
+
};
|
|
2916
|
+
treeMap.set(parentPath, parent);
|
|
2917
|
+
node.parent = parent;
|
|
2510
2918
|
} else {
|
|
2511
|
-
|
|
2919
|
+
node.parent = parent;
|
|
2920
|
+
if (parent.children === undefined) {
|
|
2921
|
+
parent.children = [node];
|
|
2922
|
+
} else {
|
|
2923
|
+
parent.children.push(node);
|
|
2924
|
+
}
|
|
2925
|
+
do {
|
|
2926
|
+
parent.entries += node.entries;
|
|
2927
|
+
parent = parent.parent;
|
|
2928
|
+
} while (parent);
|
|
2512
2929
|
}
|
|
2513
2930
|
}
|
|
2514
|
-
return;
|
|
2515
2931
|
}
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2932
|
+
// Reduce until limit reached
|
|
2933
|
+
while (currentCount > limit) {
|
|
2934
|
+
// Select node that helps reaching the limit most effectively without overmerging
|
|
2935
|
+
const overLimit = currentCount - limit;
|
|
2936
|
+
let bestNode;
|
|
2937
|
+
let bestCost = Infinity;
|
|
2938
|
+
for (const node of treeMap.values()) {
|
|
2939
|
+
if (node.entries <= 1 || !node.children || !node.parent) continue;
|
|
2940
|
+
if (node.children.length === 0) continue;
|
|
2941
|
+
if (node.children.length === 1 && !node.value) continue;
|
|
2942
|
+
// Try to select the node with has just a bit more entries than we need to reduce
|
|
2943
|
+
// When just a bit more is over 30% over the limit,
|
|
2944
|
+
// also consider just a bit less entries then we need to reduce
|
|
2945
|
+
const cost =
|
|
2946
|
+
node.entries - 1 >= overLimit
|
|
2947
|
+
? node.entries - 1 - overLimit
|
|
2948
|
+
: overLimit - node.entries + 1 + limit * 0.3;
|
|
2949
|
+
if (cost < bestCost) {
|
|
2950
|
+
bestNode = node;
|
|
2951
|
+
bestCost = cost;
|
|
2952
|
+
}
|
|
2521
2953
|
}
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2954
|
+
if (!bestNode) break;
|
|
2955
|
+
// Merge all children
|
|
2956
|
+
const reduction = bestNode.entries - 1;
|
|
2957
|
+
bestNode.active = true;
|
|
2958
|
+
bestNode.entries = 1;
|
|
2959
|
+
currentCount -= reduction;
|
|
2960
|
+
let { parent } = bestNode;
|
|
2961
|
+
while (parent) {
|
|
2962
|
+
parent.entries -= reduction;
|
|
2963
|
+
parent = parent.parent;
|
|
2526
2964
|
}
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
// Update watchers for all entries in the map
|
|
2534
|
-
for (const [filePath, entry] of plan) {
|
|
2535
|
-
if (entry.size === 1) {
|
|
2536
|
-
for (const [watcher, filePath] of entry) {
|
|
2537
|
-
const w = createDirectWatcher(filePath);
|
|
2538
|
-
const old = underlyingWatcher.get(watcher);
|
|
2539
|
-
if (old === w) continue;
|
|
2540
|
-
w.add(watcher);
|
|
2541
|
-
if (old !== undefined) old.remove(watcher);
|
|
2965
|
+
const queue = new Set(bestNode.children);
|
|
2966
|
+
for (const node of queue) {
|
|
2967
|
+
node.active = false;
|
|
2968
|
+
node.entries = 0;
|
|
2969
|
+
if (node.children) {
|
|
2970
|
+
for (const child of node.children) queue.add(child);
|
|
2542
2971
|
}
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
const
|
|
2556
|
-
|
|
2557
|
-
const old = underlyingWatcher.get(watcher);
|
|
2558
|
-
if (old === w) continue;
|
|
2559
|
-
w.add(watcher);
|
|
2560
|
-
if (old !== undefined) old.remove(watcher);
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
// Write down new plan
|
|
2975
|
+
const newPlan = new Map();
|
|
2976
|
+
for (const rootNode of treeMap.values()) {
|
|
2977
|
+
if (!rootNode.active) continue;
|
|
2978
|
+
const map = new Map();
|
|
2979
|
+
const queue = new Set([rootNode]);
|
|
2980
|
+
for (const node of queue) {
|
|
2981
|
+
if (node.active && node !== rootNode) continue;
|
|
2982
|
+
if (node.value) {
|
|
2983
|
+
if (Array.isArray(node.value)) {
|
|
2984
|
+
for (const item of node.value) {
|
|
2985
|
+
map.set(item, node.target);
|
|
2561
2986
|
}
|
|
2987
|
+
} else {
|
|
2988
|
+
map.set(node.value, node.target);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
if (node.children) {
|
|
2992
|
+
for (const child of node.children) {
|
|
2993
|
+
queue.add(child);
|
|
2562
2994
|
}
|
|
2563
2995
|
}
|
|
2564
2996
|
}
|
|
2997
|
+
newPlan.set(rootNode.target, map);
|
|
2565
2998
|
}
|
|
2999
|
+
return newPlan;
|
|
2566
3000
|
};
|
|
2567
3001
|
|
|
2568
|
-
exports.watch = filePath => {
|
|
2569
|
-
const watcher = new Watcher();
|
|
2570
|
-
// Find an existing watcher
|
|
2571
|
-
const directWatcher = directWatchers.get(filePath);
|
|
2572
|
-
if (directWatcher !== undefined) {
|
|
2573
|
-
directWatcher.add(watcher);
|
|
2574
|
-
return watcher;
|
|
2575
|
-
}
|
|
2576
|
-
let current = filePath;
|
|
2577
|
-
for (;;) {
|
|
2578
|
-
const recursiveWatcher = recursiveWatchers.get(current);
|
|
2579
|
-
if (recursiveWatcher !== undefined) {
|
|
2580
|
-
recursiveWatcher.add(filePath, watcher);
|
|
2581
|
-
return watcher;
|
|
2582
|
-
}
|
|
2583
|
-
const parent = path.dirname(current);
|
|
2584
|
-
if (parent === current) break;
|
|
2585
|
-
current = parent;
|
|
2586
|
-
}
|
|
2587
|
-
// Queue up watcher for creation
|
|
2588
|
-
pendingWatchers.set(watcher, filePath);
|
|
2589
|
-
if (!isBatch) execute();
|
|
2590
|
-
return watcher;
|
|
2591
|
-
};
|
|
2592
|
-
|
|
2593
|
-
exports.batch = fn => {
|
|
2594
|
-
isBatch = true;
|
|
2595
|
-
try {
|
|
2596
|
-
fn();
|
|
2597
|
-
} finally {
|
|
2598
|
-
isBatch = false;
|
|
2599
|
-
execute();
|
|
2600
|
-
}
|
|
2601
|
-
};
|
|
2602
|
-
|
|
2603
|
-
exports.getNumberOfWatchers = () => {
|
|
2604
|
-
return watcherCount;
|
|
2605
|
-
};
|
|
2606
|
-
|
|
2607
|
-
exports.createHandleChangeEvent = createHandleChangeEvent;
|
|
2608
|
-
exports.watcherLimit = watcherLimit;
|
|
2609
|
-
|
|
2610
3002
|
|
|
2611
3003
|
/***/ }),
|
|
2612
3004
|
|
|
2613
|
-
/***/
|
|
3005
|
+
/***/ 313:
|
|
2614
3006
|
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
|
2615
3007
|
|
|
2616
3008
|
"use strict";
|
|
@@ -2620,393 +3012,449 @@ exports.watcherLimit = watcherLimit;
|
|
|
2620
3012
|
*/
|
|
2621
3013
|
|
|
2622
3014
|
|
|
2623
|
-
const
|
|
2624
|
-
const
|
|
2625
|
-
const
|
|
2626
|
-
const
|
|
2627
|
-
const watchEventSource = __nccwpck_require__(729);
|
|
3015
|
+
const { EventEmitter } = __nccwpck_require__(434);
|
|
3016
|
+
const fs = __nccwpck_require__(896);
|
|
3017
|
+
const path = __nccwpck_require__(928);
|
|
3018
|
+
const reducePlan = __nccwpck_require__(240);
|
|
2628
3019
|
|
|
2629
|
-
|
|
2630
|
-
|
|
3020
|
+
/** @typedef {import("fs").FSWatcher} FSWatcher */
|
|
3021
|
+
/** @typedef {import("./index").EventType} EventType */
|
|
2631
3022
|
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
const w = ww.watcher;
|
|
2635
|
-
if (!set.has(w.directoryWatcher)) {
|
|
2636
|
-
set.add(w.directoryWatcher);
|
|
2637
|
-
}
|
|
2638
|
-
}
|
|
2639
|
-
}
|
|
3023
|
+
const IS_OSX = (__nccwpck_require__(857).platform)() === "darwin";
|
|
3024
|
+
const IS_WIN = (__nccwpck_require__(857).platform)() === "win32";
|
|
2640
3025
|
|
|
2641
|
-
const
|
|
2642
|
-
if (ignored.length === 0) {
|
|
2643
|
-
return;
|
|
2644
|
-
}
|
|
2645
|
-
const source = globToRegExp(ignored, { globstar: true, extended: true })
|
|
2646
|
-
.source;
|
|
2647
|
-
return source.slice(0, source.length - 1) + "(?:$|\\/)";
|
|
2648
|
-
};
|
|
3026
|
+
const SUPPORTS_RECURSIVE_WATCHING = IS_OSX || IS_WIN;
|
|
2649
3027
|
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
}
|
|
2656
|
-
const regexp = new RegExp(stringRegexps.join("|"));
|
|
2657
|
-
return x => regexp.test(x.replace(/\\/g, "/"));
|
|
2658
|
-
} else if (typeof ignored === "string") {
|
|
2659
|
-
const stringRegexp = stringToRegexp(ignored);
|
|
2660
|
-
if (!stringRegexp) {
|
|
2661
|
-
return () => false;
|
|
2662
|
-
}
|
|
2663
|
-
const regexp = new RegExp(stringRegexp);
|
|
2664
|
-
return x => regexp.test(x.replace(/\\/g, "/"));
|
|
2665
|
-
} else if (ignored instanceof RegExp) {
|
|
2666
|
-
return x => ignored.test(x.replace(/\\/g, "/"));
|
|
2667
|
-
} else if (ignored instanceof Function) {
|
|
2668
|
-
return ignored;
|
|
2669
|
-
} else if (ignored) {
|
|
2670
|
-
throw new Error(`Invalid option for 'ignored': ${ignored}`);
|
|
2671
|
-
} else {
|
|
2672
|
-
return () => false;
|
|
2673
|
-
}
|
|
2674
|
-
};
|
|
3028
|
+
// Use 20 for OSX to make `FSWatcher.close` faster
|
|
3029
|
+
// https://github.com/nodejs/node/issues/29949
|
|
3030
|
+
const watcherLimit =
|
|
3031
|
+
// @ts-expect-error avoid additional checks
|
|
3032
|
+
+process.env.WATCHPACK_WATCHER_LIMIT || (IS_OSX ? 20 : 10000);
|
|
2675
3033
|
|
|
2676
|
-
const
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
ignored: ignoredToFunction(options.ignored),
|
|
2680
|
-
poll: options.poll
|
|
2681
|
-
};
|
|
2682
|
-
};
|
|
3034
|
+
const recursiveWatcherLogging = Boolean(
|
|
3035
|
+
process.env.WATCHPACK_RECURSIVE_WATCHER_LOGGING,
|
|
3036
|
+
);
|
|
2683
3037
|
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
const cacheEntry = normalizeCache.get(options);
|
|
2687
|
-
if (cacheEntry !== undefined) return cacheEntry;
|
|
2688
|
-
const normalized = normalizeOptions(options);
|
|
2689
|
-
normalizeCache.set(options, normalized);
|
|
2690
|
-
return normalized;
|
|
2691
|
-
};
|
|
3038
|
+
let isBatch = false;
|
|
3039
|
+
let watcherCount = 0;
|
|
2692
3040
|
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
this.files = Array.isArray(files) ? files : [files];
|
|
2696
|
-
this.watcher = watcher;
|
|
2697
|
-
watcher.on("initial-missing", type => {
|
|
2698
|
-
for (const file of this.files) {
|
|
2699
|
-
if (!watchpack._missing.has(file))
|
|
2700
|
-
watchpack._onRemove(file, file, type);
|
|
2701
|
-
}
|
|
2702
|
-
});
|
|
2703
|
-
watcher.on("change", (mtime, type) => {
|
|
2704
|
-
for (const file of this.files) {
|
|
2705
|
-
watchpack._onChange(file, mtime, file, type);
|
|
2706
|
-
}
|
|
2707
|
-
});
|
|
2708
|
-
watcher.on("remove", type => {
|
|
2709
|
-
for (const file of this.files) {
|
|
2710
|
-
watchpack._onRemove(file, file, type);
|
|
2711
|
-
}
|
|
2712
|
-
});
|
|
2713
|
-
}
|
|
3041
|
+
/** @type {Map<Watcher, string>} */
|
|
3042
|
+
const pendingWatchers = new Map();
|
|
2714
3043
|
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
3044
|
+
/** @type {Map<string, RecursiveWatcher>} */
|
|
3045
|
+
const recursiveWatchers = new Map();
|
|
3046
|
+
|
|
3047
|
+
/** @type {Map<string, DirectWatcher>} */
|
|
3048
|
+
const directWatchers = new Map();
|
|
3049
|
+
|
|
3050
|
+
/** @type {Map<Watcher, RecursiveWatcher | DirectWatcher>} */
|
|
3051
|
+
const underlyingWatcher = new Map();
|
|
3052
|
+
|
|
3053
|
+
/**
|
|
3054
|
+
* @param {string} filePath file path
|
|
3055
|
+
* @returns {NodeJS.ErrnoException} new error with file path in the message
|
|
3056
|
+
*/
|
|
3057
|
+
function createEPERMError(filePath) {
|
|
3058
|
+
const error =
|
|
3059
|
+
/** @type {NodeJS.ErrnoException} */
|
|
3060
|
+
(new Error(`Operation not permitted: ${filePath}`));
|
|
3061
|
+
error.code = "EPERM";
|
|
3062
|
+
return error;
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
/**
|
|
3066
|
+
* @param {FSWatcher} watcher watcher
|
|
3067
|
+
* @param {string} filePath a file path
|
|
3068
|
+
* @param {(type: "rename" | "change", filename: string) => void} handleChangeEvent function to handle change
|
|
3069
|
+
* @returns {(type: "rename" | "change", filename: string) => void} handler of change event
|
|
3070
|
+
*/
|
|
3071
|
+
function createHandleChangeEvent(watcher, filePath, handleChangeEvent) {
|
|
3072
|
+
return (type, filename) => {
|
|
3073
|
+
// TODO: After Node.js v22, fs.watch(dir) and deleting a dir will trigger the rename change event.
|
|
3074
|
+
// Here we just ignore it and keep the same behavior as before v22
|
|
3075
|
+
// https://github.com/libuv/libuv/pull/4376
|
|
3076
|
+
if (
|
|
3077
|
+
type === "rename" &&
|
|
3078
|
+
path.isAbsolute(filename) &&
|
|
3079
|
+
path.basename(filename) === path.basename(filePath)
|
|
3080
|
+
) {
|
|
3081
|
+
if (!IS_OSX) {
|
|
3082
|
+
// Before v22, windows will throw EPERM error
|
|
3083
|
+
watcher.emit("error", createEPERMError(filename));
|
|
2721
3084
|
}
|
|
2722
|
-
|
|
2723
|
-
|
|
3085
|
+
// Before v22, macos nothing to do
|
|
3086
|
+
return;
|
|
2724
3087
|
}
|
|
2725
|
-
|
|
3088
|
+
handleChangeEvent(type, filename);
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
2726
3091
|
|
|
2727
|
-
|
|
2728
|
-
|
|
3092
|
+
class DirectWatcher {
|
|
3093
|
+
/**
|
|
3094
|
+
* @param {string} filePath file path
|
|
3095
|
+
*/
|
|
3096
|
+
constructor(filePath) {
|
|
3097
|
+
this.filePath = filePath;
|
|
3098
|
+
this.watchers = new Set();
|
|
3099
|
+
/** @type {FSWatcher | undefined} */
|
|
3100
|
+
this.watcher = undefined;
|
|
3101
|
+
try {
|
|
3102
|
+
const watcher = fs.watch(filePath);
|
|
3103
|
+
|
|
3104
|
+
this.watcher = watcher;
|
|
3105
|
+
const handleChangeEvent = createHandleChangeEvent(
|
|
3106
|
+
watcher,
|
|
3107
|
+
filePath,
|
|
3108
|
+
(type, filename) => {
|
|
3109
|
+
for (const w of this.watchers) {
|
|
3110
|
+
w.emit("change", type, filename);
|
|
3111
|
+
}
|
|
3112
|
+
},
|
|
3113
|
+
);
|
|
3114
|
+
watcher.on("change", handleChangeEvent);
|
|
3115
|
+
watcher.on("error", (error) => {
|
|
3116
|
+
for (const w of this.watchers) {
|
|
3117
|
+
w.emit("error", error);
|
|
3118
|
+
}
|
|
3119
|
+
});
|
|
3120
|
+
} catch (err) {
|
|
3121
|
+
process.nextTick(() => {
|
|
3122
|
+
for (const w of this.watchers) {
|
|
3123
|
+
w.emit("error", err);
|
|
3124
|
+
}
|
|
3125
|
+
});
|
|
3126
|
+
}
|
|
3127
|
+
watcherCount++;
|
|
2729
3128
|
}
|
|
2730
|
-
}
|
|
2731
3129
|
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
watchpack._onRemove(item, item, type);
|
|
2739
|
-
}
|
|
2740
|
-
});
|
|
2741
|
-
watcher.on("change", (file, mtime, type) => {
|
|
2742
|
-
for (const item of this.directories) {
|
|
2743
|
-
watchpack._onChange(item, mtime, file, type);
|
|
2744
|
-
}
|
|
2745
|
-
});
|
|
2746
|
-
watcher.on("remove", type => {
|
|
2747
|
-
for (const item of this.directories) {
|
|
2748
|
-
watchpack._onRemove(item, item, type);
|
|
2749
|
-
}
|
|
2750
|
-
});
|
|
3130
|
+
/**
|
|
3131
|
+
* @param {Watcher} watcher a watcher
|
|
3132
|
+
*/
|
|
3133
|
+
add(watcher) {
|
|
3134
|
+
underlyingWatcher.set(watcher, this);
|
|
3135
|
+
this.watchers.add(watcher);
|
|
2751
3136
|
}
|
|
2752
3137
|
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
this.
|
|
3138
|
+
/**
|
|
3139
|
+
* @param {Watcher} watcher a watcher
|
|
3140
|
+
*/
|
|
3141
|
+
remove(watcher) {
|
|
3142
|
+
this.watchers.delete(watcher);
|
|
3143
|
+
if (this.watchers.size === 0) {
|
|
3144
|
+
directWatchers.delete(this.filePath);
|
|
3145
|
+
watcherCount--;
|
|
3146
|
+
if (this.watcher) this.watcher.close();
|
|
2762
3147
|
}
|
|
2763
3148
|
}
|
|
2764
3149
|
|
|
2765
|
-
|
|
2766
|
-
this.
|
|
3150
|
+
getWatchers() {
|
|
3151
|
+
return this.watchers;
|
|
2767
3152
|
}
|
|
2768
3153
|
}
|
|
2769
3154
|
|
|
2770
|
-
|
|
2771
|
-
constructor(options) {
|
|
2772
|
-
super();
|
|
2773
|
-
if (!options) options = EMPTY_OPTIONS;
|
|
2774
|
-
this.options = options;
|
|
2775
|
-
this.aggregateTimeout =
|
|
2776
|
-
typeof options.aggregateTimeout === "number"
|
|
2777
|
-
? options.aggregateTimeout
|
|
2778
|
-
: 200;
|
|
2779
|
-
this.watcherOptions = cachedNormalizeOptions(options);
|
|
2780
|
-
this.watcherManager = getWatcherManager(this.watcherOptions);
|
|
2781
|
-
this.fileWatchers = new Map();
|
|
2782
|
-
this.directoryWatchers = new Map();
|
|
2783
|
-
this._missing = new Set();
|
|
2784
|
-
this.startTime = undefined;
|
|
2785
|
-
this.paused = false;
|
|
2786
|
-
this.aggregatedChanges = new Set();
|
|
2787
|
-
this.aggregatedRemovals = new Set();
|
|
2788
|
-
this.aggregateTimer = undefined;
|
|
2789
|
-
this._onTimeout = this._onTimeout.bind(this);
|
|
2790
|
-
}
|
|
3155
|
+
/** @typedef {Set<Watcher>} WatcherSet */
|
|
2791
3156
|
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
if (list === undefined) {
|
|
2815
|
-
map.set(key, item);
|
|
2816
|
-
} else if (Array.isArray(list)) {
|
|
2817
|
-
list.push(item);
|
|
2818
|
-
} else {
|
|
2819
|
-
map.set(key, [list, item]);
|
|
2820
|
-
}
|
|
2821
|
-
};
|
|
2822
|
-
const fileWatchersNeeded = new Map();
|
|
2823
|
-
const directoryWatchersNeeded = new Map();
|
|
2824
|
-
const missingFiles = new Set();
|
|
2825
|
-
if (this.watcherOptions.followSymlinks) {
|
|
2826
|
-
const resolver = new LinkResolver();
|
|
2827
|
-
for (const file of files) {
|
|
2828
|
-
if (filter(file)) {
|
|
2829
|
-
for (const innerFile of resolver.resolve(file)) {
|
|
2830
|
-
if (file === innerFile || filter(innerFile)) {
|
|
2831
|
-
addToMap(fileWatchersNeeded, innerFile, file);
|
|
2832
|
-
}
|
|
3157
|
+
class RecursiveWatcher {
|
|
3158
|
+
/**
|
|
3159
|
+
* @param {string} rootPath a root path
|
|
3160
|
+
*/
|
|
3161
|
+
constructor(rootPath) {
|
|
3162
|
+
this.rootPath = rootPath;
|
|
3163
|
+
/** @type {Map<Watcher, string>} */
|
|
3164
|
+
this.mapWatcherToPath = new Map();
|
|
3165
|
+
/** @type {Map<string, WatcherSet>} */
|
|
3166
|
+
this.mapPathToWatchers = new Map();
|
|
3167
|
+
this.watcher = undefined;
|
|
3168
|
+
try {
|
|
3169
|
+
const watcher = fs.watch(rootPath, {
|
|
3170
|
+
recursive: true,
|
|
3171
|
+
});
|
|
3172
|
+
this.watcher = watcher;
|
|
3173
|
+
watcher.on("change", (type, filename) => {
|
|
3174
|
+
if (!filename) {
|
|
3175
|
+
if (recursiveWatcherLogging) {
|
|
3176
|
+
process.stderr.write(
|
|
3177
|
+
`[watchpack] dispatch ${type} event in recursive watcher (${this.rootPath}) to all watchers\n`,
|
|
3178
|
+
);
|
|
2833
3179
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
for (const file of missing) {
|
|
2837
|
-
if (filter(file)) {
|
|
2838
|
-
for (const innerFile of resolver.resolve(file)) {
|
|
2839
|
-
if (file === innerFile || filter(innerFile)) {
|
|
2840
|
-
missingFiles.add(file);
|
|
2841
|
-
addToMap(fileWatchersNeeded, innerFile, file);
|
|
2842
|
-
}
|
|
3180
|
+
for (const w of this.mapWatcherToPath.keys()) {
|
|
3181
|
+
w.emit("change", /** @type {EventType} */ (type));
|
|
2843
3182
|
}
|
|
2844
|
-
}
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
3183
|
+
} else {
|
|
3184
|
+
const dir = path.dirname(/** @type {string} */ (filename));
|
|
3185
|
+
const watchers = this.mapPathToWatchers.get(dir);
|
|
3186
|
+
if (recursiveWatcherLogging) {
|
|
3187
|
+
process.stderr.write(
|
|
3188
|
+
`[watchpack] dispatch ${type} event in recursive watcher (${
|
|
3189
|
+
this.rootPath
|
|
3190
|
+
}) for '${filename}' to ${
|
|
3191
|
+
watchers ? watchers.size : 0
|
|
3192
|
+
} watchers\n`,
|
|
3193
|
+
);
|
|
3194
|
+
}
|
|
3195
|
+
if (watchers === undefined) return;
|
|
3196
|
+
for (const w of watchers) {
|
|
3197
|
+
w.emit(
|
|
3198
|
+
"change",
|
|
3199
|
+
/** @type {EventType} */ (type),
|
|
3200
|
+
path.basename(/** @type {string} */ (filename)),
|
|
3201
|
+
);
|
|
2858
3202
|
}
|
|
2859
3203
|
}
|
|
2860
|
-
}
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
addToMap(fileWatchersNeeded, file, file);
|
|
2865
|
-
}
|
|
2866
|
-
}
|
|
2867
|
-
for (const file of missing) {
|
|
2868
|
-
if (filter(file)) {
|
|
2869
|
-
missingFiles.add(file);
|
|
2870
|
-
addToMap(fileWatchersNeeded, file, file);
|
|
3204
|
+
});
|
|
3205
|
+
watcher.on("error", (error) => {
|
|
3206
|
+
for (const w of this.mapWatcherToPath.keys()) {
|
|
3207
|
+
w.emit("error", error);
|
|
2871
3208
|
}
|
|
2872
|
-
}
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
3209
|
+
});
|
|
3210
|
+
} catch (err) {
|
|
3211
|
+
process.nextTick(() => {
|
|
3212
|
+
for (const w of this.mapWatcherToPath.keys()) {
|
|
3213
|
+
w.emit("error", err);
|
|
2876
3214
|
}
|
|
2877
|
-
}
|
|
3215
|
+
});
|
|
2878
3216
|
}
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
3217
|
+
watcherCount++;
|
|
3218
|
+
if (recursiveWatcherLogging) {
|
|
3219
|
+
process.stderr.write(
|
|
3220
|
+
`[watchpack] created recursive watcher at ${rootPath}\n`,
|
|
3221
|
+
);
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
/**
|
|
3226
|
+
* @param {string} filePath a file path
|
|
3227
|
+
* @param {Watcher} watcher a watcher
|
|
3228
|
+
*/
|
|
3229
|
+
add(filePath, watcher) {
|
|
3230
|
+
underlyingWatcher.set(watcher, this);
|
|
3231
|
+
const subpath = filePath.slice(this.rootPath.length + 1) || ".";
|
|
3232
|
+
this.mapWatcherToPath.set(watcher, subpath);
|
|
3233
|
+
const set = this.mapPathToWatchers.get(subpath);
|
|
3234
|
+
if (set === undefined) {
|
|
3235
|
+
const newSet = new Set();
|
|
3236
|
+
newSet.add(watcher);
|
|
3237
|
+
this.mapPathToWatchers.set(subpath, newSet);
|
|
3238
|
+
} else {
|
|
3239
|
+
set.add(watcher);
|
|
2890
3240
|
}
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
/**
|
|
3244
|
+
* @param {Watcher} watcher a watcher
|
|
3245
|
+
*/
|
|
3246
|
+
remove(watcher) {
|
|
3247
|
+
const subpath = this.mapWatcherToPath.get(watcher);
|
|
3248
|
+
if (!subpath) return;
|
|
3249
|
+
this.mapWatcherToPath.delete(watcher);
|
|
3250
|
+
const set = /** @type {WatcherSet} */ (this.mapPathToWatchers.get(subpath));
|
|
3251
|
+
set.delete(watcher);
|
|
3252
|
+
if (set.size === 0) {
|
|
3253
|
+
this.mapPathToWatchers.delete(subpath);
|
|
2900
3254
|
}
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
for (const [key, directories] of directoryWatchersNeeded) {
|
|
2910
|
-
const watcher = this.watcherManager.watchDirectory(key, startTime);
|
|
2911
|
-
if (watcher) {
|
|
2912
|
-
directoryWatchers.set(
|
|
2913
|
-
key,
|
|
2914
|
-
new WatchpackDirectoryWatcher(this, watcher, directories)
|
|
2915
|
-
);
|
|
2916
|
-
}
|
|
3255
|
+
if (this.mapWatcherToPath.size === 0) {
|
|
3256
|
+
recursiveWatchers.delete(this.rootPath);
|
|
3257
|
+
watcherCount--;
|
|
3258
|
+
if (this.watcher) this.watcher.close();
|
|
3259
|
+
if (recursiveWatcherLogging) {
|
|
3260
|
+
process.stderr.write(
|
|
3261
|
+
`[watchpack] closed recursive watcher at ${this.rootPath}\n`,
|
|
3262
|
+
);
|
|
2917
3263
|
}
|
|
2918
|
-
}
|
|
2919
|
-
this._missing = missingFiles;
|
|
2920
|
-
this.startTime = startTime;
|
|
3264
|
+
}
|
|
2921
3265
|
}
|
|
2922
3266
|
|
|
2923
|
-
|
|
2924
|
-
this.
|
|
2925
|
-
if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
|
|
2926
|
-
for (const w of this.fileWatchers.values()) w.close();
|
|
2927
|
-
for (const w of this.directoryWatchers.values()) w.close();
|
|
2928
|
-
this.fileWatchers.clear();
|
|
2929
|
-
this.directoryWatchers.clear();
|
|
3267
|
+
getWatchers() {
|
|
3268
|
+
return this.mapWatcherToPath;
|
|
2930
3269
|
}
|
|
3270
|
+
}
|
|
2931
3271
|
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
3272
|
+
/**
|
|
3273
|
+
* @typedef {object} WatcherEvents
|
|
3274
|
+
* @property {(eventType: EventType, filename?: string) => void} change change event
|
|
3275
|
+
* @property {(err: unknown) => void} error error event
|
|
3276
|
+
*/
|
|
3277
|
+
|
|
3278
|
+
/**
|
|
3279
|
+
* @extends {EventEmitter<{ [K in keyof WatcherEvents]: Parameters<WatcherEvents[K]> }>}
|
|
3280
|
+
*/
|
|
3281
|
+
class Watcher extends EventEmitter {
|
|
3282
|
+
constructor() {
|
|
3283
|
+
super();
|
|
2935
3284
|
}
|
|
2936
3285
|
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
const obj = Object.create(null);
|
|
2942
|
-
for (const w of directoryWatchers) {
|
|
2943
|
-
const times = w.getTimes();
|
|
2944
|
-
for (const file of Object.keys(times)) obj[file] = times[file];
|
|
3286
|
+
close() {
|
|
3287
|
+
if (pendingWatchers.has(this)) {
|
|
3288
|
+
pendingWatchers.delete(this);
|
|
3289
|
+
return;
|
|
2945
3290
|
}
|
|
2946
|
-
|
|
3291
|
+
const watcher = underlyingWatcher.get(this);
|
|
3292
|
+
/** @type {RecursiveWatcher | DirectWatcher} */
|
|
3293
|
+
(watcher).remove(this);
|
|
3294
|
+
underlyingWatcher.delete(this);
|
|
2947
3295
|
}
|
|
3296
|
+
}
|
|
2948
3297
|
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
3298
|
+
/**
|
|
3299
|
+
* @param {string} filePath a file path
|
|
3300
|
+
* @returns {DirectWatcher} a directory watcher
|
|
3301
|
+
*/
|
|
3302
|
+
const createDirectWatcher = (filePath) => {
|
|
3303
|
+
const existing = directWatchers.get(filePath);
|
|
3304
|
+
if (existing !== undefined) return existing;
|
|
3305
|
+
const w = new DirectWatcher(filePath);
|
|
3306
|
+
directWatchers.set(filePath, w);
|
|
3307
|
+
return w;
|
|
3308
|
+
};
|
|
2954
3309
|
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
3310
|
+
/**
|
|
3311
|
+
* @param {string} rootPath a root path
|
|
3312
|
+
* @returns {RecursiveWatcher} a recursive watcher
|
|
3313
|
+
*/
|
|
3314
|
+
const createRecursiveWatcher = (rootPath) => {
|
|
3315
|
+
const existing = recursiveWatchers.get(rootPath);
|
|
3316
|
+
if (existing !== undefined) return existing;
|
|
3317
|
+
const w = new RecursiveWatcher(rootPath);
|
|
3318
|
+
recursiveWatchers.set(rootPath, w);
|
|
3319
|
+
return w;
|
|
3320
|
+
};
|
|
3321
|
+
|
|
3322
|
+
const execute = () => {
|
|
3323
|
+
/** @type {Map<string, Watcher[] | Watcher>} */
|
|
3324
|
+
const map = new Map();
|
|
3325
|
+
/**
|
|
3326
|
+
* @param {Watcher} watcher a watcher
|
|
3327
|
+
* @param {string} filePath a file path
|
|
3328
|
+
*/
|
|
3329
|
+
const addWatcher = (watcher, filePath) => {
|
|
3330
|
+
const entry = map.get(filePath);
|
|
3331
|
+
if (entry === undefined) {
|
|
3332
|
+
map.set(filePath, watcher);
|
|
3333
|
+
} else if (Array.isArray(entry)) {
|
|
3334
|
+
entry.push(watcher);
|
|
3335
|
+
} else {
|
|
3336
|
+
map.set(filePath, [entry, watcher]);
|
|
2962
3337
|
}
|
|
3338
|
+
};
|
|
3339
|
+
for (const [watcher, filePath] of pendingWatchers) {
|
|
3340
|
+
addWatcher(watcher, filePath);
|
|
2963
3341
|
}
|
|
3342
|
+
pendingWatchers.clear();
|
|
2964
3343
|
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3344
|
+
// Fast case when we are not reaching the limit
|
|
3345
|
+
if (!SUPPORTS_RECURSIVE_WATCHING || watcherLimit - watcherCount >= map.size) {
|
|
3346
|
+
// Create watchers for all entries in the map
|
|
3347
|
+
for (const [filePath, entry] of map) {
|
|
3348
|
+
const w = createDirectWatcher(filePath);
|
|
3349
|
+
if (Array.isArray(entry)) {
|
|
3350
|
+
for (const item of entry) w.add(item);
|
|
3351
|
+
} else {
|
|
3352
|
+
w.add(entry);
|
|
3353
|
+
}
|
|
2969
3354
|
}
|
|
2970
|
-
|
|
2971
|
-
const removals = this.aggregatedRemovals;
|
|
2972
|
-
this.aggregatedChanges = new Set();
|
|
2973
|
-
this.aggregatedRemovals = new Set();
|
|
2974
|
-
return { changes, removals };
|
|
3355
|
+
return;
|
|
2975
3356
|
}
|
|
2976
3357
|
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
3358
|
+
// Reconsider existing watchers to improving watch plan
|
|
3359
|
+
for (const watcher of recursiveWatchers.values()) {
|
|
3360
|
+
for (const [w, subpath] of watcher.getWatchers()) {
|
|
3361
|
+
addWatcher(w, path.join(watcher.rootPath, subpath));
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
for (const watcher of directWatchers.values()) {
|
|
3365
|
+
for (const w of watcher.getWatchers()) {
|
|
3366
|
+
addWatcher(w, watcher.filePath);
|
|
2983
3367
|
}
|
|
2984
|
-
this.aggregatedRemovals.delete(item);
|
|
2985
|
-
this.aggregatedChanges.add(item);
|
|
2986
3368
|
}
|
|
2987
3369
|
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
3370
|
+
// Merge map entries to keep watcher limit
|
|
3371
|
+
// Create a 10% buffer to be able to enter fast case more often
|
|
3372
|
+
const plan = reducePlan(map, watcherLimit * 0.9);
|
|
3373
|
+
|
|
3374
|
+
// Update watchers for all entries in the map
|
|
3375
|
+
for (const [filePath, entry] of plan) {
|
|
3376
|
+
if (entry.size === 1) {
|
|
3377
|
+
for (const [watcher, filePath] of entry) {
|
|
3378
|
+
const w = createDirectWatcher(filePath);
|
|
3379
|
+
const old = underlyingWatcher.get(watcher);
|
|
3380
|
+
if (old === w) continue;
|
|
3381
|
+
w.add(watcher);
|
|
3382
|
+
if (old !== undefined) old.remove(watcher);
|
|
3383
|
+
}
|
|
3384
|
+
} else {
|
|
3385
|
+
const filePaths = new Set(entry.values());
|
|
3386
|
+
if (filePaths.size > 1) {
|
|
3387
|
+
const w = createRecursiveWatcher(filePath);
|
|
3388
|
+
for (const [watcher, watcherPath] of entry) {
|
|
3389
|
+
const old = underlyingWatcher.get(watcher);
|
|
3390
|
+
if (old === w) continue;
|
|
3391
|
+
w.add(watcherPath, watcher);
|
|
3392
|
+
if (old !== undefined) old.remove(watcher);
|
|
3393
|
+
}
|
|
3394
|
+
} else {
|
|
3395
|
+
for (const filePath of filePaths) {
|
|
3396
|
+
const w = createDirectWatcher(filePath);
|
|
3397
|
+
for (const watcher of entry.keys()) {
|
|
3398
|
+
const old = underlyingWatcher.get(watcher);
|
|
3399
|
+
if (old === w) continue;
|
|
3400
|
+
w.add(watcher);
|
|
3401
|
+
if (old !== undefined) old.remove(watcher);
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
2994
3405
|
}
|
|
2995
|
-
this.aggregatedChanges.delete(item);
|
|
2996
|
-
this.aggregatedRemovals.add(item);
|
|
2997
3406
|
}
|
|
3407
|
+
};
|
|
2998
3408
|
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3409
|
+
module.exports.Watcher = Watcher;
|
|
3410
|
+
|
|
3411
|
+
/**
|
|
3412
|
+
* @param {() => void} fn a function
|
|
3413
|
+
*/
|
|
3414
|
+
module.exports.batch = (fn) => {
|
|
3415
|
+
isBatch = true;
|
|
3416
|
+
try {
|
|
3417
|
+
fn();
|
|
3418
|
+
} finally {
|
|
3419
|
+
isBatch = false;
|
|
3420
|
+
execute();
|
|
3006
3421
|
}
|
|
3007
|
-
}
|
|
3422
|
+
};
|
|
3008
3423
|
|
|
3009
|
-
module.exports =
|
|
3424
|
+
module.exports.createHandleChangeEvent = createHandleChangeEvent;
|
|
3425
|
+
|
|
3426
|
+
module.exports.getNumberOfWatchers = () => watcherCount;
|
|
3427
|
+
|
|
3428
|
+
/**
|
|
3429
|
+
* @param {string} filePath a file path
|
|
3430
|
+
* @returns {Watcher} watcher
|
|
3431
|
+
*/
|
|
3432
|
+
module.exports.watch = (filePath) => {
|
|
3433
|
+
const watcher = new Watcher();
|
|
3434
|
+
// Find an existing watcher
|
|
3435
|
+
const directWatcher = directWatchers.get(filePath);
|
|
3436
|
+
if (directWatcher !== undefined) {
|
|
3437
|
+
directWatcher.add(watcher);
|
|
3438
|
+
return watcher;
|
|
3439
|
+
}
|
|
3440
|
+
let current = filePath;
|
|
3441
|
+
for (;;) {
|
|
3442
|
+
const recursiveWatcher = recursiveWatchers.get(current);
|
|
3443
|
+
if (recursiveWatcher !== undefined) {
|
|
3444
|
+
recursiveWatcher.add(filePath, watcher);
|
|
3445
|
+
return watcher;
|
|
3446
|
+
}
|
|
3447
|
+
const parent = path.dirname(current);
|
|
3448
|
+
if (parent === current) break;
|
|
3449
|
+
current = parent;
|
|
3450
|
+
}
|
|
3451
|
+
// Queue up watcher for creation
|
|
3452
|
+
pendingWatchers.set(watcher, filePath);
|
|
3453
|
+
if (!isBatch) execute();
|
|
3454
|
+
return watcher;
|
|
3455
|
+
};
|
|
3456
|
+
|
|
3457
|
+
module.exports.watcherLimit = watcherLimit;
|
|
3010
3458
|
|
|
3011
3459
|
|
|
3012
3460
|
/***/ }),
|
|
@@ -3117,7 +3565,7 @@ module.exports = require("util");
|
|
|
3117
3565
|
/******/ // startup
|
|
3118
3566
|
/******/ // Load entry module and return exports
|
|
3119
3567
|
/******/ // This entry module is referenced by other modules so it can't be inlined
|
|
3120
|
-
/******/ var __webpack_exports__ = __nccwpck_require__(
|
|
3568
|
+
/******/ var __webpack_exports__ = __nccwpck_require__(17);
|
|
3121
3569
|
/******/ module.exports = __webpack_exports__;
|
|
3122
3570
|
/******/
|
|
3123
3571
|
/******/ })()
|