webpack 5.48.0 → 5.51.1
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.
Potentially problematic release.
This version of webpack might be problematic. Click here for more details.
- package/README.md +4 -16
- package/hot/only-dev-server.js +1 -1
- package/hot/poll.js +1 -1
- package/hot/signal.js +1 -1
- package/lib/CompatibilityPlugin.js +21 -4
- package/lib/Compilation.js +8 -3
- package/lib/EvalSourceMapDevToolPlugin.js +2 -2
- package/lib/ExternalModuleFactoryPlugin.js +1 -1
- package/lib/FileSystemInfo.js +665 -193
- package/lib/HotModuleReplacementPlugin.js +4 -4
- package/lib/Module.js +1 -0
- package/lib/MultiCompiler.js +0 -2
- package/lib/NormalModule.js +51 -20
- package/lib/NormalModuleFactory.js +137 -74
- package/lib/Parser.js +1 -0
- package/lib/RuntimeGlobals.js +5 -0
- package/lib/SourceMapDevToolPlugin.js +2 -2
- package/lib/WebpackOptionsApply.js +8 -0
- package/lib/asset/AssetModulesPlugin.js +0 -1
- package/lib/config/defaults.js +27 -6
- package/lib/config/normalization.js +6 -1
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +10 -1
- package/lib/hmr/HotModuleReplacement.runtime.js +5 -1
- package/lib/index.js +0 -3
- package/lib/javascript/JavascriptParser.js +2 -0
- package/lib/library/ModuleLibraryPlugin.js +4 -0
- package/lib/node/ReadFileChunkLoadingRuntimeModule.js +7 -1
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +2 -2
- package/lib/node/ReadFileCompileWasmPlugin.js +2 -1
- package/lib/node/RequireChunkLoadingRuntimeModule.js +7 -1
- package/lib/optimize/ConcatenatedModule.js +3 -3
- package/lib/optimize/SplitChunksPlugin.js +4 -4
- package/lib/schemes/HttpUriPlugin.js +942 -25
- package/lib/serialization/BinaryMiddleware.js +293 -267
- package/lib/util/fs.js +40 -0
- package/lib/util/identifier.js +26 -8
- package/lib/wasm-async/{AsyncWasmChunkLoadingRuntimeModule.js → AsyncWasmLoadingRuntimeModule.js} +3 -3
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +18 -2
- package/lib/web/FetchCompileAsyncWasmPlugin.js +2 -2
- package/lib/web/FetchCompileWasmPlugin.js +2 -1
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +21 -8
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +7 -1
- package/package.json +1 -1
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +43 -0
- package/schemas/plugins/schemes/HttpUriPlugin.check.d.ts +7 -0
- package/schemas/plugins/schemes/HttpUriPlugin.check.js +6 -0
- package/schemas/plugins/schemes/HttpUriPlugin.json +42 -0
- package/types.d.ts +110 -14
- package/lib/schemes/HttpsUriPlugin.js +0 -63
package/lib/FileSystemInfo.js
CHANGED
@@ -10,12 +10,13 @@ const asyncLib = require("neo-async");
|
|
10
10
|
const AsyncQueue = require("./util/AsyncQueue");
|
11
11
|
const StackedCacheMap = require("./util/StackedCacheMap");
|
12
12
|
const createHash = require("./util/createHash");
|
13
|
-
const { join, dirname, relative } = require("./util/fs");
|
13
|
+
const { join, dirname, relative, lstatReadlinkAbsolute } = require("./util/fs");
|
14
14
|
const makeSerializable = require("./util/makeSerializable");
|
15
15
|
const processAsyncTree = require("./util/processAsyncTree");
|
16
16
|
|
17
17
|
/** @typedef {import("./WebpackError")} WebpackError */
|
18
18
|
/** @typedef {import("./logging/Logger").Logger} Logger */
|
19
|
+
/** @typedef {import("./util/fs").IStats} IStats */
|
19
20
|
/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
|
20
21
|
|
21
22
|
const supportsEsm = +process.versions.modules >= 83;
|
@@ -41,17 +42,52 @@ const INVALID = Symbol("invalid");
|
|
41
42
|
* @typedef {Object} FileSystemInfoEntry
|
42
43
|
* @property {number} safeTime
|
43
44
|
* @property {number=} timestamp
|
45
|
+
*/
|
46
|
+
|
47
|
+
/**
|
48
|
+
* @typedef {Object} ResolvedContextFileSystemInfoEntry
|
49
|
+
* @property {number} safeTime
|
50
|
+
* @property {string=} timestampHash
|
51
|
+
*/
|
52
|
+
|
53
|
+
/**
|
54
|
+
* @typedef {Object} ContextFileSystemInfoEntry
|
55
|
+
* @property {number} safeTime
|
44
56
|
* @property {string=} timestampHash
|
57
|
+
* @property {ResolvedContextFileSystemInfoEntry=} resolved
|
58
|
+
* @property {Set<string>=} symlinks
|
45
59
|
*/
|
46
60
|
|
47
61
|
/**
|
48
62
|
* @typedef {Object} TimestampAndHash
|
49
63
|
* @property {number} safeTime
|
50
64
|
* @property {number=} timestamp
|
65
|
+
* @property {string} hash
|
66
|
+
*/
|
67
|
+
|
68
|
+
/**
|
69
|
+
* @typedef {Object} ResolvedContextTimestampAndHash
|
70
|
+
* @property {number} safeTime
|
51
71
|
* @property {string=} timestampHash
|
52
72
|
* @property {string} hash
|
53
73
|
*/
|
54
74
|
|
75
|
+
/**
|
76
|
+
* @typedef {Object} ContextTimestampAndHash
|
77
|
+
* @property {number} safeTime
|
78
|
+
* @property {string=} timestampHash
|
79
|
+
* @property {string} hash
|
80
|
+
* @property {ResolvedContextTimestampAndHash=} resolved
|
81
|
+
* @property {Set<string>=} symlinks
|
82
|
+
*/
|
83
|
+
|
84
|
+
/**
|
85
|
+
* @typedef {Object} ContextHash
|
86
|
+
* @property {string} hash
|
87
|
+
* @property {string=} resolved
|
88
|
+
* @property {Set<string>=} symlinks
|
89
|
+
*/
|
90
|
+
|
55
91
|
/**
|
56
92
|
* @typedef {Object} SnapshotOptimizationEntry
|
57
93
|
* @property {Snapshot} snapshot
|
@@ -175,11 +211,11 @@ class Snapshot {
|
|
175
211
|
this.fileHashes = undefined;
|
176
212
|
/** @type {Map<string, TimestampAndHash | string> | undefined} */
|
177
213
|
this.fileTshs = undefined;
|
178
|
-
/** @type {Map<string,
|
214
|
+
/** @type {Map<string, ResolvedContextFileSystemInfoEntry> | undefined} */
|
179
215
|
this.contextTimestamps = undefined;
|
180
216
|
/** @type {Map<string, string> | undefined} */
|
181
217
|
this.contextHashes = undefined;
|
182
|
-
/** @type {Map<string,
|
218
|
+
/** @type {Map<string, ResolvedContextTimestampAndHash> | undefined} */
|
183
219
|
this.contextTshs = undefined;
|
184
220
|
/** @type {Map<string, boolean> | undefined} */
|
185
221
|
this.missingExistence = undefined;
|
@@ -771,11 +807,29 @@ const getManagedItem = (managedPath, path) => {
|
|
771
807
|
};
|
772
808
|
|
773
809
|
/**
|
774
|
-
* @
|
775
|
-
* @
|
810
|
+
* @template {ContextFileSystemInfoEntry | ContextTimestampAndHash} T
|
811
|
+
* @param {T | "ignore"} entry entry
|
812
|
+
* @returns {T["resolved"] | undefined} the resolved entry
|
776
813
|
*/
|
777
|
-
const
|
778
|
-
|
814
|
+
const getResolvedTimestamp = entry => {
|
815
|
+
if (entry === "ignore") return undefined;
|
816
|
+
if (entry === null) return null;
|
817
|
+
if (entry.resolved !== undefined) return entry.resolved;
|
818
|
+
return entry.symlinks === undefined ? entry : undefined;
|
819
|
+
};
|
820
|
+
|
821
|
+
/**
|
822
|
+
* @param {ContextHash} entry entry
|
823
|
+
* @returns {string | undefined} the resolved entry
|
824
|
+
*/
|
825
|
+
const getResolvedHash = entry => {
|
826
|
+
if (entry === null) return null;
|
827
|
+
if (entry.resolved !== undefined) return entry.resolved;
|
828
|
+
return entry.symlinks === undefined ? entry.hash : undefined;
|
829
|
+
};
|
830
|
+
|
831
|
+
const addAll = (source, target) => {
|
832
|
+
for (const key of source) target.add(key);
|
779
833
|
};
|
780
834
|
|
781
835
|
/**
|
@@ -860,11 +914,11 @@ class FileSystemInfo {
|
|
860
914
|
this._fileHashes = new Map();
|
861
915
|
/** @type {Map<string, TimestampAndHash | string>} */
|
862
916
|
this._fileTshs = new Map();
|
863
|
-
/** @type {StackedCacheMap<string,
|
917
|
+
/** @type {StackedCacheMap<string, ContextFileSystemInfoEntry | "ignore" | null>} */
|
864
918
|
this._contextTimestamps = new StackedCacheMap();
|
865
|
-
/** @type {Map<string,
|
919
|
+
/** @type {Map<string, ContextHash>} */
|
866
920
|
this._contextHashes = new Map();
|
867
|
-
/** @type {Map<string,
|
921
|
+
/** @type {Map<string, ContextTimestampAndHash>} */
|
868
922
|
this._contextTshs = new Map();
|
869
923
|
/** @type {Map<string, string>} */
|
870
924
|
this._managedItems = new Map();
|
@@ -880,18 +934,24 @@ class FileSystemInfo {
|
|
880
934
|
parallelism: 10,
|
881
935
|
processor: this._readFileHash.bind(this)
|
882
936
|
});
|
883
|
-
/** @type {AsyncQueue<string, string,
|
937
|
+
/** @type {AsyncQueue<string, string, ContextFileSystemInfoEntry | null>} */
|
884
938
|
this.contextTimestampQueue = new AsyncQueue({
|
885
939
|
name: "context timestamp",
|
886
940
|
parallelism: 2,
|
887
941
|
processor: this._readContextTimestamp.bind(this)
|
888
942
|
});
|
889
|
-
/** @type {AsyncQueue<string, string,
|
943
|
+
/** @type {AsyncQueue<string, string, ContextHash | null>} */
|
890
944
|
this.contextHashQueue = new AsyncQueue({
|
891
945
|
name: "context hash",
|
892
946
|
parallelism: 2,
|
893
947
|
processor: this._readContextHash.bind(this)
|
894
948
|
});
|
949
|
+
/** @type {AsyncQueue<string, string, ContextTimestampAndHash | null>} */
|
950
|
+
this.contextTshQueue = new AsyncQueue({
|
951
|
+
name: "context hash and timestamp",
|
952
|
+
parallelism: 2,
|
953
|
+
processor: this._readContextTimestampAndHash.bind(this)
|
954
|
+
});
|
895
955
|
/** @type {AsyncQueue<string, string, string | null>} */
|
896
956
|
this.managedItemQueue = new AsyncQueue({
|
897
957
|
name: "managed item info",
|
@@ -1093,13 +1153,34 @@ class FileSystemInfo {
|
|
1093
1153
|
|
1094
1154
|
/**
|
1095
1155
|
* @param {string} path context path
|
1096
|
-
* @param {function(WebpackError=, (
|
1156
|
+
* @param {function(WebpackError=, (ResolvedContextFileSystemInfoEntry | "ignore" | null)=): void} callback callback function
|
1097
1157
|
* @returns {void}
|
1098
1158
|
*/
|
1099
1159
|
getContextTimestamp(path, callback) {
|
1160
|
+
const cache = this._contextTimestamps.get(path);
|
1161
|
+
if (cache !== undefined) {
|
1162
|
+
const resolved = getResolvedTimestamp(cache);
|
1163
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1164
|
+
this._resolveContextTimestamp(cache, callback);
|
1165
|
+
}
|
1166
|
+
this.contextTimestampQueue.add(path, (err, entry) => {
|
1167
|
+
const resolved = getResolvedTimestamp(entry);
|
1168
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1169
|
+
this._resolveContextTimestamp(entry, callback);
|
1170
|
+
});
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
/**
|
1174
|
+
* @param {string} path context path
|
1175
|
+
* @param {function(WebpackError=, (ContextFileSystemInfoEntry | "ignore" | null)=): void} callback callback function
|
1176
|
+
* @returns {void}
|
1177
|
+
*/
|
1178
|
+
_getUnresolvedContextTimestamp(path, callback) {
|
1100
1179
|
const cache = this._contextTimestamps.get(path);
|
1101
1180
|
if (cache !== undefined) return callback(null, cache);
|
1102
|
-
this.contextTimestampQueue.add(path,
|
1181
|
+
this.contextTimestampQueue.add(path, (err, entry) => {
|
1182
|
+
return callback(null, entry);
|
1183
|
+
});
|
1103
1184
|
}
|
1104
1185
|
|
1105
1186
|
/**
|
@@ -1120,8 +1201,61 @@ class FileSystemInfo {
|
|
1120
1201
|
*/
|
1121
1202
|
getContextHash(path, callback) {
|
1122
1203
|
const cache = this._contextHashes.get(path);
|
1204
|
+
if (cache !== undefined) {
|
1205
|
+
const resolved = getResolvedHash(cache);
|
1206
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1207
|
+
this._resolveContextHash(cache, callback);
|
1208
|
+
}
|
1209
|
+
this.contextHashQueue.add(path, (err, entry) => {
|
1210
|
+
const resolved = getResolvedHash(entry);
|
1211
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1212
|
+
this._resolveContextHash(entry, callback);
|
1213
|
+
});
|
1214
|
+
}
|
1215
|
+
|
1216
|
+
/**
|
1217
|
+
* @param {string} path context path
|
1218
|
+
* @param {function(WebpackError=, ContextHash=): void} callback callback function
|
1219
|
+
* @returns {void}
|
1220
|
+
*/
|
1221
|
+
_getUnresolvedContextHash(path, callback) {
|
1222
|
+
const cache = this._contextHashes.get(path);
|
1223
|
+
if (cache !== undefined) return callback(null, cache);
|
1224
|
+
this.contextHashQueue.add(path, (err, entry) => {
|
1225
|
+
return callback(null, entry);
|
1226
|
+
});
|
1227
|
+
}
|
1228
|
+
|
1229
|
+
/**
|
1230
|
+
* @param {string} path context path
|
1231
|
+
* @param {function(WebpackError=, ResolvedContextTimestampAndHash=): void} callback callback function
|
1232
|
+
* @returns {void}
|
1233
|
+
*/
|
1234
|
+
getContextTsh(path, callback) {
|
1235
|
+
const cache = this._contextTshs.get(path);
|
1236
|
+
if (cache !== undefined) {
|
1237
|
+
const resolved = getResolvedTimestamp(cache);
|
1238
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1239
|
+
this._resolveContextTsh(cache, callback);
|
1240
|
+
}
|
1241
|
+
this.contextTshQueue.add(path, (err, entry) => {
|
1242
|
+
const resolved = getResolvedTimestamp(entry);
|
1243
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1244
|
+
this._resolveContextTsh(entry, callback);
|
1245
|
+
});
|
1246
|
+
}
|
1247
|
+
|
1248
|
+
/**
|
1249
|
+
* @param {string} path context path
|
1250
|
+
* @param {function(WebpackError=, ContextTimestampAndHash=): void} callback callback function
|
1251
|
+
* @returns {void}
|
1252
|
+
*/
|
1253
|
+
_getUnresolvedContextTsh(path, callback) {
|
1254
|
+
const cache = this._contextTshs.get(path);
|
1123
1255
|
if (cache !== undefined) return callback(null, cache);
|
1124
|
-
this.
|
1256
|
+
this.contextTshQueue.add(path, (err, entry) => {
|
1257
|
+
return callback(null, entry);
|
1258
|
+
});
|
1125
1259
|
}
|
1126
1260
|
|
1127
1261
|
_createBuildDependenciesResolvers() {
|
@@ -2007,11 +2141,15 @@ class FileSystemInfo {
|
|
2007
2141
|
);
|
2008
2142
|
for (const path of capturedDirectories) {
|
2009
2143
|
const cache = this._contextTshs.get(path);
|
2010
|
-
|
2011
|
-
|
2144
|
+
let resolved;
|
2145
|
+
if (
|
2146
|
+
cache !== undefined &&
|
2147
|
+
(resolved = getResolvedTimestamp(cache)) !== undefined
|
2148
|
+
) {
|
2149
|
+
contextTshs.set(path, resolved);
|
2012
2150
|
} else {
|
2013
2151
|
jobs++;
|
2014
|
-
|
2152
|
+
const callback = (err, entry) => {
|
2015
2153
|
if (err) {
|
2016
2154
|
if (this.logger) {
|
2017
2155
|
this.logger.debug(
|
@@ -2023,7 +2161,12 @@ class FileSystemInfo {
|
|
2023
2161
|
contextTshs.set(path, entry);
|
2024
2162
|
jobDone();
|
2025
2163
|
}
|
2026
|
-
}
|
2164
|
+
};
|
2165
|
+
if (cache !== undefined) {
|
2166
|
+
this._resolveContextTsh(cache, callback);
|
2167
|
+
} else {
|
2168
|
+
this.getContextTsh(path, callback);
|
2169
|
+
}
|
2027
2170
|
}
|
2028
2171
|
}
|
2029
2172
|
break;
|
@@ -2035,11 +2178,15 @@ class FileSystemInfo {
|
|
2035
2178
|
);
|
2036
2179
|
for (const path of capturedDirectories) {
|
2037
2180
|
const cache = this._contextHashes.get(path);
|
2038
|
-
|
2039
|
-
|
2181
|
+
let resolved;
|
2182
|
+
if (
|
2183
|
+
cache !== undefined &&
|
2184
|
+
(resolved = getResolvedHash(cache)) !== undefined
|
2185
|
+
) {
|
2186
|
+
contextHashes.set(path, resolved);
|
2040
2187
|
} else {
|
2041
2188
|
jobs++;
|
2042
|
-
|
2189
|
+
const callback = (err, entry) => {
|
2043
2190
|
if (err) {
|
2044
2191
|
if (this.logger) {
|
2045
2192
|
this.logger.debug(
|
@@ -2051,7 +2198,12 @@ class FileSystemInfo {
|
|
2051
2198
|
contextHashes.set(path, entry);
|
2052
2199
|
jobDone();
|
2053
2200
|
}
|
2054
|
-
}
|
2201
|
+
};
|
2202
|
+
if (cache !== undefined) {
|
2203
|
+
this._resolveContextHash(cache, callback);
|
2204
|
+
} else {
|
2205
|
+
this.getContextHash(path, callback);
|
2206
|
+
}
|
2055
2207
|
}
|
2056
2208
|
}
|
2057
2209
|
break;
|
@@ -2064,13 +2216,15 @@ class FileSystemInfo {
|
|
2064
2216
|
);
|
2065
2217
|
for (const path of capturedDirectories) {
|
2066
2218
|
const cache = this._contextTimestamps.get(path);
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2219
|
+
let resolved;
|
2220
|
+
if (
|
2221
|
+
cache !== undefined &&
|
2222
|
+
(resolved = getResolvedTimestamp(cache)) !== undefined
|
2223
|
+
) {
|
2224
|
+
contextTimestamps.set(path, resolved);
|
2225
|
+
} else if (cache !== "ignore") {
|
2072
2226
|
jobs++;
|
2073
|
-
|
2227
|
+
const callback = (err, entry) => {
|
2074
2228
|
if (err) {
|
2075
2229
|
if (this.logger) {
|
2076
2230
|
this.logger.debug(
|
@@ -2082,7 +2236,12 @@ class FileSystemInfo {
|
|
2082
2236
|
contextTimestamps.set(path, entry);
|
2083
2237
|
jobDone();
|
2084
2238
|
}
|
2085
|
-
}
|
2239
|
+
};
|
2240
|
+
if (cache !== undefined) {
|
2241
|
+
this._resolveContextTimestamp(cache, callback);
|
2242
|
+
} else {
|
2243
|
+
this.getContextTimestamp(path, callback);
|
2244
|
+
}
|
2086
2245
|
}
|
2087
2246
|
}
|
2088
2247
|
break;
|
@@ -2099,7 +2258,7 @@ class FileSystemInfo {
|
|
2099
2258
|
const cache = this._fileTimestamps.get(path);
|
2100
2259
|
if (cache !== undefined) {
|
2101
2260
|
if (cache !== "ignore") {
|
2102
|
-
missingExistence.set(path,
|
2261
|
+
missingExistence.set(path, Boolean(cache));
|
2103
2262
|
}
|
2104
2263
|
} else {
|
2105
2264
|
jobs++;
|
@@ -2112,7 +2271,7 @@ class FileSystemInfo {
|
|
2112
2271
|
}
|
2113
2272
|
jobError();
|
2114
2273
|
} else {
|
2115
|
-
missingExistence.set(path,
|
2274
|
+
missingExistence.set(path, Boolean(entry));
|
2116
2275
|
jobDone();
|
2117
2276
|
}
|
2118
2277
|
});
|
@@ -2321,17 +2480,7 @@ class FileSystemInfo {
|
|
2321
2480
|
*/
|
2322
2481
|
const checkFile = (path, current, snap, log = true) => {
|
2323
2482
|
if (current === snap) return true;
|
2324
|
-
if (!current
|
2325
|
-
// If existence of item differs
|
2326
|
-
// it's invalid
|
2327
|
-
if (log && this._remainingLogs > 0) {
|
2328
|
-
this._log(
|
2329
|
-
path,
|
2330
|
-
current ? "it didn't exist before" : "it does no longer exist"
|
2331
|
-
);
|
2332
|
-
}
|
2333
|
-
return false;
|
2334
|
-
}
|
2483
|
+
if (!checkExistence(path, Boolean(current), Boolean(snap))) return false;
|
2335
2484
|
if (current) {
|
2336
2485
|
// For existing items only
|
2337
2486
|
if (typeof startTime === "number" && current.safeTime > startTime) {
|
@@ -2363,6 +2512,34 @@ class FileSystemInfo {
|
|
2363
2512
|
}
|
2364
2513
|
return false;
|
2365
2514
|
}
|
2515
|
+
}
|
2516
|
+
return true;
|
2517
|
+
};
|
2518
|
+
/**
|
2519
|
+
* @param {string} path file path
|
2520
|
+
* @param {ResolvedContextFileSystemInfoEntry} current current entry
|
2521
|
+
* @param {ResolvedContextFileSystemInfoEntry} snap entry from snapshot
|
2522
|
+
* @param {boolean} log log reason
|
2523
|
+
* @returns {boolean} true, if ok
|
2524
|
+
*/
|
2525
|
+
const checkContext = (path, current, snap, log = true) => {
|
2526
|
+
if (current === snap) return true;
|
2527
|
+
if (!checkExistence(path, Boolean(current), Boolean(snap))) return false;
|
2528
|
+
if (current) {
|
2529
|
+
// For existing items only
|
2530
|
+
if (typeof startTime === "number" && current.safeTime > startTime) {
|
2531
|
+
// If a change happened after starting reading the item
|
2532
|
+
// this may no longer be valid
|
2533
|
+
if (log && this._remainingLogs > 0) {
|
2534
|
+
this._log(
|
2535
|
+
path,
|
2536
|
+
`it may have changed (%d) after the start time of the snapshot (%d)`,
|
2537
|
+
current.safeTime,
|
2538
|
+
startTime
|
2539
|
+
);
|
2540
|
+
}
|
2541
|
+
return false;
|
2542
|
+
}
|
2366
2543
|
if (
|
2367
2544
|
snap.timestampHash !== undefined &&
|
2368
2545
|
current.timestampHash !== snap.timestampHash
|
@@ -2487,41 +2664,59 @@ class FileSystemInfo {
|
|
2487
2664
|
this._statTestedEntries += contextTimestamps.size;
|
2488
2665
|
for (const [path, ts] of contextTimestamps) {
|
2489
2666
|
const cache = this._contextTimestamps.get(path);
|
2490
|
-
|
2491
|
-
|
2667
|
+
let resolved;
|
2668
|
+
if (
|
2669
|
+
cache !== undefined &&
|
2670
|
+
(resolved = getResolvedTimestamp(cache)) !== undefined
|
2671
|
+
) {
|
2672
|
+
if (!checkContext(path, resolved, ts)) {
|
2492
2673
|
invalid();
|
2493
2674
|
return;
|
2494
2675
|
}
|
2495
|
-
} else {
|
2676
|
+
} else if (cache !== "ignore") {
|
2496
2677
|
jobs++;
|
2497
|
-
|
2678
|
+
const callback = (err, entry) => {
|
2498
2679
|
if (err) return invalidWithError(path, err);
|
2499
|
-
if (!
|
2680
|
+
if (!checkContext(path, entry, ts)) {
|
2500
2681
|
invalid();
|
2501
2682
|
} else {
|
2502
2683
|
jobDone();
|
2503
2684
|
}
|
2504
|
-
}
|
2685
|
+
};
|
2686
|
+
if (cache !== undefined) {
|
2687
|
+
this._resolveContextTimestamp(cache, callback);
|
2688
|
+
} else {
|
2689
|
+
this.getContextTimestamp(path, callback);
|
2690
|
+
}
|
2505
2691
|
}
|
2506
2692
|
}
|
2507
2693
|
}
|
2508
2694
|
const processContextHashSnapshot = (path, hash) => {
|
2509
2695
|
const cache = this._contextHashes.get(path);
|
2510
|
-
|
2511
|
-
|
2696
|
+
let resolved;
|
2697
|
+
if (
|
2698
|
+
cache !== undefined &&
|
2699
|
+
(resolved = getResolvedHash(cache)) !== undefined
|
2700
|
+
) {
|
2701
|
+
if (!checkHash(path, resolved, hash)) {
|
2512
2702
|
invalid();
|
2513
2703
|
return;
|
2514
2704
|
}
|
2515
2705
|
} else {
|
2516
2706
|
jobs++;
|
2517
|
-
|
2707
|
+
const callback = (err, entry) => {
|
2518
2708
|
if (err) return invalidWithError(path, err);
|
2519
2709
|
if (!checkHash(path, entry, hash)) {
|
2520
2710
|
invalid();
|
2521
2711
|
} else {
|
2522
2712
|
jobDone();
|
2523
2713
|
}
|
2524
|
-
}
|
2714
|
+
};
|
2715
|
+
if (cache !== undefined) {
|
2716
|
+
this._resolveContextHash(cache, callback);
|
2717
|
+
} else {
|
2718
|
+
this.getContextHash(path, callback);
|
2719
|
+
}
|
2525
2720
|
}
|
2526
2721
|
};
|
2527
2722
|
if (snapshot.hasContextHashes()) {
|
@@ -2539,19 +2734,28 @@ class FileSystemInfo {
|
|
2539
2734
|
processContextHashSnapshot(path, tsh);
|
2540
2735
|
} else {
|
2541
2736
|
const cache = this._contextTimestamps.get(path);
|
2542
|
-
|
2543
|
-
|
2737
|
+
let resolved;
|
2738
|
+
if (
|
2739
|
+
cache !== undefined &&
|
2740
|
+
(resolved = getResolvedTimestamp(cache)) !== undefined
|
2741
|
+
) {
|
2742
|
+
if (!checkContext(path, resolved, tsh, false)) {
|
2544
2743
|
processContextHashSnapshot(path, tsh.hash);
|
2545
2744
|
}
|
2546
|
-
} else {
|
2745
|
+
} else if (cache !== "ignore") {
|
2547
2746
|
jobs++;
|
2548
|
-
|
2747
|
+
const callback = (err, entry) => {
|
2549
2748
|
if (err) return invalidWithError(path, err);
|
2550
|
-
if (!
|
2749
|
+
if (!checkContext(path, entry, tsh, false)) {
|
2551
2750
|
processContextHashSnapshot(path, tsh.hash);
|
2552
2751
|
}
|
2553
2752
|
jobDone();
|
2554
|
-
}
|
2753
|
+
};
|
2754
|
+
if (cache !== undefined) {
|
2755
|
+
this._resolveContextTsh(cache, callback);
|
2756
|
+
} else {
|
2757
|
+
this.getContextTsh(path, callback);
|
2758
|
+
}
|
2555
2759
|
}
|
2556
2760
|
}
|
2557
2761
|
}
|
@@ -2564,7 +2768,7 @@ class FileSystemInfo {
|
|
2564
2768
|
if (cache !== undefined) {
|
2565
2769
|
if (
|
2566
2770
|
cache !== "ignore" &&
|
2567
|
-
!checkExistence(path,
|
2771
|
+
!checkExistence(path, Boolean(cache), Boolean(existence))
|
2568
2772
|
) {
|
2569
2773
|
invalid();
|
2570
2774
|
return;
|
@@ -2573,7 +2777,7 @@ class FileSystemInfo {
|
|
2573
2777
|
jobs++;
|
2574
2778
|
this.fileTimestampQueue.add(path, (err, entry) => {
|
2575
2779
|
if (err) return invalidWithError(path, err);
|
2576
|
-
if (!checkExistence(path,
|
2780
|
+
if (!checkExistence(path, Boolean(entry), Boolean(existence))) {
|
2577
2781
|
invalid();
|
2578
2782
|
} else {
|
2579
2783
|
jobDone();
|
@@ -2727,12 +2931,34 @@ class FileSystemInfo {
|
|
2727
2931
|
}
|
2728
2932
|
}
|
2729
2933
|
|
2730
|
-
|
2934
|
+
/**
|
2935
|
+
* @template T
|
2936
|
+
* @template ItemType
|
2937
|
+
* @param {Object} options options
|
2938
|
+
* @param {string} options.path path
|
2939
|
+
* @param {function(string): ItemType} options.fromImmutablePath called when context item is an immutable path
|
2940
|
+
* @param {function(string): ItemType} options.fromManagedItem called when context item is a managed path
|
2941
|
+
* @param {function(string, string, function(Error=, ItemType=): void): void} options.fromSymlink called when context item is a symlink
|
2942
|
+
* @param {function(string, IStats, function(Error=, ItemType=): void): void} options.fromFile called when context item is a file
|
2943
|
+
* @param {function(string, IStats, function(Error=, ItemType=): void): void} options.fromDirectory called when context item is a directory
|
2944
|
+
* @param {function(string[], ItemType[]): T} options.reduce called from all context items
|
2945
|
+
* @param {function(Error=, (T)=): void} callback callback
|
2946
|
+
*/
|
2947
|
+
_readContext(
|
2948
|
+
{
|
2949
|
+
path,
|
2950
|
+
fromImmutablePath,
|
2951
|
+
fromManagedItem,
|
2952
|
+
fromSymlink,
|
2953
|
+
fromFile,
|
2954
|
+
fromDirectory,
|
2955
|
+
reduce
|
2956
|
+
},
|
2957
|
+
callback
|
2958
|
+
) {
|
2731
2959
|
this.fs.readdir(path, (err, _files) => {
|
2732
2960
|
if (err) {
|
2733
2961
|
if (err.code === "ENOENT") {
|
2734
|
-
this._contextTimestamps.set(path, null);
|
2735
|
-
this._cachedDeprecatedContextTimestamps = undefined;
|
2736
2962
|
return callback(null, null);
|
2737
2963
|
}
|
2738
2964
|
return callback(err);
|
@@ -2745,47 +2971,94 @@ class FileSystemInfo {
|
|
2745
2971
|
files,
|
2746
2972
|
(file, callback) => {
|
2747
2973
|
const child = join(this.fs, path, file);
|
2748
|
-
|
2749
|
-
if (
|
2750
|
-
|
2751
|
-
|
2752
|
-
if (path.startsWith(immutablePath)) {
|
2753
|
-
// ignore any immutable path for timestamping
|
2754
|
-
return callback(null, null);
|
2755
|
-
}
|
2974
|
+
for (const immutablePath of this.immutablePathsWithSlash) {
|
2975
|
+
if (path.startsWith(immutablePath)) {
|
2976
|
+
// ignore any immutable path for timestamping
|
2977
|
+
return callback(null, fromImmutablePath(immutablePath));
|
2756
2978
|
}
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2762
|
-
|
2763
|
-
|
2764
|
-
|
2765
|
-
|
2766
|
-
|
2767
|
-
});
|
2768
|
-
});
|
2769
|
-
}
|
2979
|
+
}
|
2980
|
+
for (const managedPath of this.managedPathsWithSlash) {
|
2981
|
+
if (path.startsWith(managedPath)) {
|
2982
|
+
const managedItem = getManagedItem(managedPath, child);
|
2983
|
+
if (managedItem) {
|
2984
|
+
// construct timestampHash from managed info
|
2985
|
+
return this.managedItemQueue.add(managedItem, (err, info) => {
|
2986
|
+
if (err) return callback(err);
|
2987
|
+
return callback(null, fromManagedItem(info));
|
2988
|
+
});
|
2770
2989
|
}
|
2771
2990
|
}
|
2991
|
+
}
|
2992
|
+
|
2993
|
+
lstatReadlinkAbsolute(this.fs, child, (err, stat) => {
|
2994
|
+
if (err) return callback(err);
|
2995
|
+
|
2996
|
+
if (typeof stat === "string") {
|
2997
|
+
return fromSymlink(child, stat, callback);
|
2998
|
+
}
|
2772
2999
|
|
2773
3000
|
if (stat.isFile()) {
|
2774
|
-
return
|
3001
|
+
return fromFile(child, stat, callback);
|
2775
3002
|
}
|
2776
3003
|
if (stat.isDirectory()) {
|
2777
|
-
|
2778
|
-
this.getContextTimestamp(child, (err, tsEntry) => {
|
2779
|
-
this.contextTimestampQueue.decreaseParallelism();
|
2780
|
-
callback(err, tsEntry);
|
2781
|
-
});
|
2782
|
-
return;
|
3004
|
+
return fromDirectory(child, stat, callback);
|
2783
3005
|
}
|
2784
3006
|
callback(null, null);
|
2785
3007
|
});
|
2786
3008
|
},
|
2787
|
-
(err,
|
3009
|
+
(err, results) => {
|
2788
3010
|
if (err) return callback(err);
|
3011
|
+
const result = reduce(files, results);
|
3012
|
+
callback(null, result);
|
3013
|
+
}
|
3014
|
+
);
|
3015
|
+
});
|
3016
|
+
}
|
3017
|
+
|
3018
|
+
_readContextTimestamp(path, callback) {
|
3019
|
+
this._readContext(
|
3020
|
+
{
|
3021
|
+
path,
|
3022
|
+
fromImmutablePath: () => null,
|
3023
|
+
fromManagedItem: info => ({
|
3024
|
+
safeTime: 0,
|
3025
|
+
timestampHash: info
|
3026
|
+
}),
|
3027
|
+
fromSymlink: (file, target, callback) => {
|
3028
|
+
callback(null, {
|
3029
|
+
timestampHash: target,
|
3030
|
+
symlinks: new Set([target])
|
3031
|
+
});
|
3032
|
+
},
|
3033
|
+
fromFile: (file, stat, callback) => {
|
3034
|
+
// Prefer the cached value over our new stat to report consistent results
|
3035
|
+
const cache = this._fileTimestamps.get(file);
|
3036
|
+
if (cache !== undefined)
|
3037
|
+
return callback(null, cache === "ignore" ? null : cache);
|
3038
|
+
|
3039
|
+
const mtime = +stat.mtime;
|
3040
|
+
|
3041
|
+
if (mtime) applyMtime(mtime);
|
3042
|
+
|
3043
|
+
const ts = {
|
3044
|
+
safeTime: mtime ? mtime + FS_ACCURACY : Infinity,
|
3045
|
+
timestamp: mtime
|
3046
|
+
};
|
3047
|
+
|
3048
|
+
this._fileTimestamps.set(file, ts);
|
3049
|
+
this._cachedDeprecatedFileTimestamps = undefined;
|
3050
|
+
callback(null, ts);
|
3051
|
+
},
|
3052
|
+
fromDirectory: (directory, stat, callback) => {
|
3053
|
+
this.contextTimestampQueue.increaseParallelism();
|
3054
|
+
this._getUnresolvedContextTimestamp(directory, (err, tsEntry) => {
|
3055
|
+
this.contextTimestampQueue.decreaseParallelism();
|
3056
|
+
callback(err, tsEntry);
|
3057
|
+
});
|
3058
|
+
},
|
3059
|
+
reduce: (files, tsEntries) => {
|
3060
|
+
let symlinks = undefined;
|
3061
|
+
|
2789
3062
|
const hash = createHash("md4");
|
2790
3063
|
|
2791
3064
|
for (const file of files) hash.update(file);
|
@@ -2802,6 +3075,10 @@ class FileSystemInfo {
|
|
2802
3075
|
hash.update("d");
|
2803
3076
|
hash.update(`${entry.timestampHash}`);
|
2804
3077
|
}
|
3078
|
+
if (entry.symlinks !== undefined) {
|
3079
|
+
if (symlinks === undefined) symlinks = new Set();
|
3080
|
+
addAll(entry.symlinks, symlinks);
|
3081
|
+
}
|
2805
3082
|
if (entry.safeTime) {
|
2806
3083
|
safeTime = Math.max(safeTime, entry.safeTime);
|
2807
3084
|
}
|
@@ -2813,131 +3090,326 @@ class FileSystemInfo {
|
|
2813
3090
|
safeTime,
|
2814
3091
|
timestampHash: digest
|
2815
3092
|
};
|
2816
|
-
|
2817
|
-
|
2818
|
-
this._cachedDeprecatedContextTimestamps = undefined;
|
2819
|
-
|
2820
|
-
callback(null, result);
|
3093
|
+
if (symlinks) result.symlinks = symlinks;
|
3094
|
+
return result;
|
2821
3095
|
}
|
2822
|
-
|
2823
|
-
|
2824
|
-
|
3096
|
+
},
|
3097
|
+
(err, result) => {
|
3098
|
+
if (err) return callback(err);
|
3099
|
+
this._contextTimestamps.set(path, result);
|
3100
|
+
this._cachedDeprecatedContextTimestamps = undefined;
|
2825
3101
|
|
2826
|
-
|
2827
|
-
this.fs.readdir(path, (err, _files) => {
|
2828
|
-
if (err) {
|
2829
|
-
if (err.code === "ENOENT") {
|
2830
|
-
this._contextHashes.set(path, null);
|
2831
|
-
return callback(null, null);
|
2832
|
-
}
|
2833
|
-
return callback(err);
|
3102
|
+
callback(null, result);
|
2834
3103
|
}
|
2835
|
-
|
2836
|
-
|
2837
|
-
.filter(file => !/^\./.test(file))
|
2838
|
-
.sort();
|
2839
|
-
asyncLib.map(
|
2840
|
-
files,
|
2841
|
-
(file, callback) => {
|
2842
|
-
const child = join(this.fs, path, file);
|
2843
|
-
this.fs.stat(child, (err, stat) => {
|
2844
|
-
if (err) return callback(err);
|
3104
|
+
);
|
3105
|
+
}
|
2845
3106
|
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
2850
|
-
|
3107
|
+
_resolveContextTimestamp(entry, callback) {
|
3108
|
+
const hashes = [];
|
3109
|
+
let safeTime = 0;
|
3110
|
+
processAsyncTree(
|
3111
|
+
entry.symlinks,
|
3112
|
+
10,
|
3113
|
+
(target, push, callback) => {
|
3114
|
+
this._getUnresolvedContextTimestamp(target, (err, entry) => {
|
3115
|
+
if (err) return callback(err);
|
3116
|
+
if (entry && entry !== "ignore") {
|
3117
|
+
hashes.push(entry.timestampHash);
|
3118
|
+
if (entry.safeTime) {
|
3119
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
2851
3120
|
}
|
2852
|
-
|
2853
|
-
|
2854
|
-
const managedItem = getManagedItem(managedPath, child);
|
2855
|
-
if (managedItem) {
|
2856
|
-
// construct hash from managed info
|
2857
|
-
return this.managedItemQueue.add(managedItem, (err, info) => {
|
2858
|
-
if (err) return callback(err);
|
2859
|
-
callback(null, info || "");
|
2860
|
-
});
|
2861
|
-
}
|
2862
|
-
}
|
3121
|
+
if (entry.symlinks !== undefined) {
|
3122
|
+
for (const target of entry.symlinks) push(target);
|
2863
3123
|
}
|
3124
|
+
}
|
3125
|
+
callback();
|
3126
|
+
});
|
3127
|
+
},
|
3128
|
+
err => {
|
3129
|
+
if (err) return callback(err);
|
3130
|
+
const hash = createHash("md4");
|
3131
|
+
hash.update(entry.timestampHash);
|
3132
|
+
if (entry.safeTime) {
|
3133
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
3134
|
+
}
|
3135
|
+
hashes.sort();
|
3136
|
+
for (const h of hashes) {
|
3137
|
+
hash.update(h);
|
3138
|
+
}
|
3139
|
+
callback(
|
3140
|
+
null,
|
3141
|
+
(entry.resolved = {
|
3142
|
+
safeTime,
|
3143
|
+
timestampHash: /** @type {string} */ (hash.digest("hex"))
|
3144
|
+
})
|
3145
|
+
);
|
3146
|
+
}
|
3147
|
+
);
|
3148
|
+
}
|
2864
3149
|
|
2865
|
-
|
2866
|
-
|
2867
|
-
|
2868
|
-
|
2869
|
-
|
2870
|
-
|
2871
|
-
|
2872
|
-
|
2873
|
-
|
2874
|
-
|
2875
|
-
});
|
2876
|
-
return;
|
2877
|
-
}
|
2878
|
-
callback(null, "");
|
3150
|
+
_readContextHash(path, callback) {
|
3151
|
+
this._readContext(
|
3152
|
+
{
|
3153
|
+
path,
|
3154
|
+
fromImmutablePath: () => "",
|
3155
|
+
fromManagedItem: info => info || "",
|
3156
|
+
fromSymlink: (file, target, callback) => {
|
3157
|
+
callback(null, {
|
3158
|
+
hash: target,
|
3159
|
+
symlinks: new Set([target])
|
2879
3160
|
});
|
2880
3161
|
},
|
2881
|
-
(
|
2882
|
-
|
3162
|
+
fromFile: (file, stat, callback) =>
|
3163
|
+
this.getFileHash(file, (err, hash) => {
|
3164
|
+
callback(err, hash || "");
|
3165
|
+
}),
|
3166
|
+
fromDirectory: (directory, stat, callback) => {
|
3167
|
+
this.contextHashQueue.increaseParallelism();
|
3168
|
+
this._getUnresolvedContextHash(directory, (err, hash) => {
|
3169
|
+
this.contextHashQueue.decreaseParallelism();
|
3170
|
+
callback(err, hash || "");
|
3171
|
+
});
|
3172
|
+
},
|
3173
|
+
/**
|
3174
|
+
* @param {string[]} files files
|
3175
|
+
* @param {(string | ContextHash)[]} fileHashes hashes
|
3176
|
+
* @returns {ContextHash} reduced hash
|
3177
|
+
*/
|
3178
|
+
reduce: (files, fileHashes) => {
|
3179
|
+
let symlinks = undefined;
|
2883
3180
|
const hash = createHash("md4");
|
2884
3181
|
|
2885
3182
|
for (const file of files) hash.update(file);
|
2886
|
-
for (const
|
2887
|
-
|
2888
|
-
|
2889
|
-
|
2890
|
-
|
3183
|
+
for (const entry of fileHashes) {
|
3184
|
+
if (typeof entry === "string") {
|
3185
|
+
hash.update(entry);
|
3186
|
+
} else {
|
3187
|
+
hash.update(entry.hash);
|
3188
|
+
if (entry.symlinks) {
|
3189
|
+
if (symlinks === undefined) symlinks = new Set();
|
3190
|
+
addAll(entry.symlinks, symlinks);
|
3191
|
+
}
|
3192
|
+
}
|
3193
|
+
}
|
2891
3194
|
|
2892
|
-
|
3195
|
+
const result = {
|
3196
|
+
hash: /** @type {string} */ (hash.digest("hex"))
|
3197
|
+
};
|
3198
|
+
if (symlinks) result.symlinks = symlinks;
|
3199
|
+
return result;
|
2893
3200
|
}
|
2894
|
-
|
2895
|
-
|
3201
|
+
},
|
3202
|
+
(err, result) => {
|
3203
|
+
if (err) return callback(err);
|
3204
|
+
this._contextHashes.set(path, result);
|
3205
|
+
return callback(null, result);
|
3206
|
+
}
|
3207
|
+
);
|
2896
3208
|
}
|
2897
3209
|
|
2898
|
-
|
2899
|
-
const
|
2900
|
-
|
2901
|
-
|
2902
|
-
|
2903
|
-
|
2904
|
-
|
2905
|
-
|
2906
|
-
|
2907
|
-
|
2908
|
-
|
2909
|
-
|
2910
|
-
|
2911
|
-
|
3210
|
+
_resolveContextHash(entry, callback) {
|
3211
|
+
const hashes = [];
|
3212
|
+
processAsyncTree(
|
3213
|
+
entry.symlinks,
|
3214
|
+
10,
|
3215
|
+
(target, push, callback) => {
|
3216
|
+
this._getUnresolvedContextHash(target, (err, hash) => {
|
3217
|
+
if (err) return callback(err);
|
3218
|
+
if (hash) {
|
3219
|
+
hashes.push(hash.hash);
|
3220
|
+
if (hash.symlinks !== undefined) {
|
3221
|
+
for (const target of hash.symlinks) push(target);
|
3222
|
+
}
|
3223
|
+
}
|
3224
|
+
callback();
|
3225
|
+
});
|
3226
|
+
},
|
3227
|
+
err => {
|
3228
|
+
if (err) return callback(err);
|
3229
|
+
const hash = createHash("md4");
|
3230
|
+
hash.update(entry.hash);
|
3231
|
+
hashes.sort();
|
3232
|
+
for (const h of hashes) {
|
3233
|
+
hash.update(h);
|
2912
3234
|
}
|
3235
|
+
callback(
|
3236
|
+
null,
|
3237
|
+
(entry.resolved = /** @type {string} */ (hash.digest("hex")))
|
3238
|
+
);
|
3239
|
+
}
|
3240
|
+
);
|
3241
|
+
}
|
3242
|
+
|
3243
|
+
_readContextTimestampAndHash(path, callback) {
|
3244
|
+
const finalize = (timestamp, hash) => {
|
3245
|
+
const result =
|
3246
|
+
timestamp === "ignore"
|
3247
|
+
? hash
|
3248
|
+
: {
|
3249
|
+
...timestamp,
|
3250
|
+
...hash
|
3251
|
+
};
|
3252
|
+
this._contextTshs.set(path, result);
|
3253
|
+
callback(null, result);
|
3254
|
+
};
|
3255
|
+
const cachedHash = this._contextHashes.get(path);
|
3256
|
+
const cachedTimestamp = this._contextTimestamps.get(path);
|
3257
|
+
if (cachedHash !== undefined) {
|
3258
|
+
if (cachedTimestamp !== undefined) {
|
3259
|
+
finalize(cachedTimestamp, cachedHash);
|
2913
3260
|
} else {
|
2914
3261
|
this.contextTimestampQueue.add(path, (err, entry) => {
|
2915
|
-
if (err)
|
2916
|
-
|
2917
|
-
}
|
2918
|
-
const result = {
|
2919
|
-
...entry,
|
2920
|
-
hash
|
2921
|
-
};
|
2922
|
-
this._contextTshs.set(path, result);
|
2923
|
-
return callback(null, result);
|
3262
|
+
if (err) return callback(err);
|
3263
|
+
finalize(entry, cachedHash);
|
2924
3264
|
});
|
2925
3265
|
}
|
2926
|
-
};
|
2927
|
-
|
2928
|
-
const cache = this._contextHashes.get(path);
|
2929
|
-
if (cache !== undefined) {
|
2930
|
-
continueWithHash(cache);
|
2931
3266
|
} else {
|
2932
|
-
|
2933
|
-
|
2934
|
-
return callback(err);
|
2935
|
-
|
2936
|
-
|
2937
|
-
}
|
3267
|
+
if (cachedTimestamp !== undefined) {
|
3268
|
+
this.contextHashQueue.add(path, (err, entry) => {
|
3269
|
+
if (err) return callback(err);
|
3270
|
+
finalize(cachedTimestamp, entry);
|
3271
|
+
});
|
3272
|
+
} else {
|
3273
|
+
this._readContext(
|
3274
|
+
{
|
3275
|
+
path,
|
3276
|
+
fromImmutablePath: () => null,
|
3277
|
+
fromManagedItem: info => ({
|
3278
|
+
safeTime: 0,
|
3279
|
+
timestampHash: info,
|
3280
|
+
hash: info || ""
|
3281
|
+
}),
|
3282
|
+
fromSymlink: (fle, target, callback) => {
|
3283
|
+
callback(null, {
|
3284
|
+
timestampHash: target,
|
3285
|
+
hash: target,
|
3286
|
+
symlinks: new Set([target])
|
3287
|
+
});
|
3288
|
+
},
|
3289
|
+
fromFile: (file, stat, callback) => {
|
3290
|
+
this._getFileTimestampAndHash(file, callback);
|
3291
|
+
},
|
3292
|
+
fromDirectory: (directory, stat, callback) => {
|
3293
|
+
this.contextTshQueue.increaseParallelism();
|
3294
|
+
this.contextTshQueue.add(directory, (err, result) => {
|
3295
|
+
this.contextTshQueue.decreaseParallelism();
|
3296
|
+
callback(err, result);
|
3297
|
+
});
|
3298
|
+
},
|
3299
|
+
/**
|
3300
|
+
* @param {string[]} files files
|
3301
|
+
* @param {(Partial<TimestampAndHash> & Partial<ContextTimestampAndHash> | string | null)[]} results results
|
3302
|
+
* @returns {ContextTimestampAndHash} tsh
|
3303
|
+
*/
|
3304
|
+
reduce: (files, results) => {
|
3305
|
+
let symlinks = undefined;
|
3306
|
+
|
3307
|
+
const tsHash = createHash("md4");
|
3308
|
+
const hash = createHash("md4");
|
3309
|
+
|
3310
|
+
for (const file of files) {
|
3311
|
+
tsHash.update(file);
|
3312
|
+
hash.update(file);
|
3313
|
+
}
|
3314
|
+
let safeTime = 0;
|
3315
|
+
for (const entry of results) {
|
3316
|
+
if (!entry) {
|
3317
|
+
tsHash.update("n");
|
3318
|
+
continue;
|
3319
|
+
}
|
3320
|
+
if (typeof entry === "string") {
|
3321
|
+
tsHash.update("n");
|
3322
|
+
hash.update(entry);
|
3323
|
+
continue;
|
3324
|
+
}
|
3325
|
+
if (entry.timestamp) {
|
3326
|
+
tsHash.update("f");
|
3327
|
+
tsHash.update(`${entry.timestamp}`);
|
3328
|
+
} else if (entry.timestampHash) {
|
3329
|
+
tsHash.update("d");
|
3330
|
+
tsHash.update(`${entry.timestampHash}`);
|
3331
|
+
}
|
3332
|
+
if (entry.symlinks !== undefined) {
|
3333
|
+
if (symlinks === undefined) symlinks = new Set();
|
3334
|
+
addAll(entry.symlinks, symlinks);
|
3335
|
+
}
|
3336
|
+
if (entry.safeTime) {
|
3337
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
3338
|
+
}
|
3339
|
+
hash.update(entry.hash);
|
3340
|
+
}
|
3341
|
+
|
3342
|
+
const result = {
|
3343
|
+
safeTime,
|
3344
|
+
timestampHash: /** @type {string} */ (tsHash.digest("hex")),
|
3345
|
+
hash: /** @type {string} */ (hash.digest("hex"))
|
3346
|
+
};
|
3347
|
+
if (symlinks) result.symlinks = symlinks;
|
3348
|
+
return result;
|
3349
|
+
}
|
3350
|
+
},
|
3351
|
+
(err, result) => {
|
3352
|
+
if (err) return callback(err);
|
3353
|
+
this._contextTshs.set(path, result);
|
3354
|
+
return callback(null, result);
|
3355
|
+
}
|
3356
|
+
);
|
3357
|
+
}
|
2938
3358
|
}
|
2939
3359
|
}
|
2940
3360
|
|
3361
|
+
_resolveContextTsh(entry, callback) {
|
3362
|
+
const hashes = [];
|
3363
|
+
const tsHashes = [];
|
3364
|
+
let safeTime = 0;
|
3365
|
+
processAsyncTree(
|
3366
|
+
entry.symlinks,
|
3367
|
+
10,
|
3368
|
+
(target, push, callback) => {
|
3369
|
+
this._getUnresolvedContextTsh(target, (err, entry) => {
|
3370
|
+
if (err) return callback(err);
|
3371
|
+
if (entry) {
|
3372
|
+
hashes.push(entry.hash);
|
3373
|
+
if (entry.timestampHash) tsHashes.push(entry.timestampHash);
|
3374
|
+
if (entry.safeTime) {
|
3375
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
3376
|
+
}
|
3377
|
+
if (entry.symlinks !== undefined) {
|
3378
|
+
for (const target of entry.symlinks) push(target);
|
3379
|
+
}
|
3380
|
+
}
|
3381
|
+
callback();
|
3382
|
+
});
|
3383
|
+
},
|
3384
|
+
err => {
|
3385
|
+
if (err) return callback(err);
|
3386
|
+
const hash = createHash("md4");
|
3387
|
+
const tsHash = createHash("md4");
|
3388
|
+
hash.update(entry.hash);
|
3389
|
+
if (entry.timestampHash) tsHash.update(entry.timestampHash);
|
3390
|
+
if (entry.safeTime) {
|
3391
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
3392
|
+
}
|
3393
|
+
hashes.sort();
|
3394
|
+
for (const h of hashes) {
|
3395
|
+
hash.update(h);
|
3396
|
+
}
|
3397
|
+
tsHashes.sort();
|
3398
|
+
for (const h of tsHashes) {
|
3399
|
+
tsHash.update(h);
|
3400
|
+
}
|
3401
|
+
callback(
|
3402
|
+
null,
|
3403
|
+
(entry.resolved = {
|
3404
|
+
safeTime,
|
3405
|
+
timestampHash: /** @type {string} */ (tsHash.digest("hex")),
|
3406
|
+
hash: /** @type {string} */ (hash.digest("hex"))
|
3407
|
+
})
|
3408
|
+
);
|
3409
|
+
}
|
3410
|
+
);
|
3411
|
+
}
|
3412
|
+
|
2941
3413
|
_getManagedItemDirectoryInfo(path, callback) {
|
2942
3414
|
this.fs.readdir(path, (err, elements) => {
|
2943
3415
|
if (err) {
|