webpack 5.50.0 → 5.52.0
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/lib/ChunkGraph.js +75 -1
- package/lib/Compilation.js +10 -1
- package/lib/Compiler.js +7 -0
- package/lib/EvalSourceMapDevToolPlugin.js +2 -2
- package/lib/ExternalModule.js +25 -16
- package/lib/FileSystemInfo.js +660 -191
- package/lib/HotModuleReplacementPlugin.js +14 -0
- package/lib/InitFragment.js +23 -0
- package/lib/NormalModule.js +10 -2
- package/lib/NormalModuleFactory.js +7 -4
- package/lib/RuntimeGlobals.js +5 -0
- package/lib/RuntimeModule.js +2 -1
- package/lib/SourceMapDevToolPlugin.js +2 -2
- package/lib/Watching.js +8 -10
- package/lib/WebpackOptionsApply.js +1 -3
- package/lib/config/defaults.js +0 -1
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +6 -2
- package/lib/dependencies/LoaderPlugin.js +94 -98
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +10 -1
- 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/MangleExportsPlugin.js +21 -4
- package/lib/optimize/SplitChunksPlugin.js +1 -1
- package/lib/runtime/GetChunkFilenameRuntimeModule.js +1 -0
- package/lib/util/fs.js +40 -0
- package/lib/util/identifier.js +26 -8
- package/lib/util/internalSerializables.js +1 -0
- package/lib/util/propertyAccess.js +54 -1
- 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 +3 -2
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +0 -4
- package/types.d.ts +62 -12
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,10 +1153,30 @@ 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
|
+
return this._resolveContextTimestamp(cache, callback);
|
1165
|
+
}
|
1166
|
+
this.contextTimestampQueue.add(path, (err, entry) => {
|
1167
|
+
if (err) return callback(err);
|
1168
|
+
const resolved = getResolvedTimestamp(entry);
|
1169
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1170
|
+
this._resolveContextTimestamp(entry, callback);
|
1171
|
+
});
|
1172
|
+
}
|
1173
|
+
|
1174
|
+
/**
|
1175
|
+
* @param {string} path context path
|
1176
|
+
* @param {function(WebpackError=, (ContextFileSystemInfoEntry | "ignore" | null)=): void} callback callback function
|
1177
|
+
* @returns {void}
|
1178
|
+
*/
|
1179
|
+
_getUnresolvedContextTimestamp(path, callback) {
|
1100
1180
|
const cache = this._contextTimestamps.get(path);
|
1101
1181
|
if (cache !== undefined) return callback(null, cache);
|
1102
1182
|
this.contextTimestampQueue.add(path, callback);
|
@@ -1119,11 +1199,62 @@ class FileSystemInfo {
|
|
1119
1199
|
* @returns {void}
|
1120
1200
|
*/
|
1121
1201
|
getContextHash(path, callback) {
|
1202
|
+
const cache = this._contextHashes.get(path);
|
1203
|
+
if (cache !== undefined) {
|
1204
|
+
const resolved = getResolvedHash(cache);
|
1205
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1206
|
+
return this._resolveContextHash(cache, callback);
|
1207
|
+
}
|
1208
|
+
this.contextHashQueue.add(path, (err, entry) => {
|
1209
|
+
if (err) return callback(err);
|
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) {
|
1122
1222
|
const cache = this._contextHashes.get(path);
|
1123
1223
|
if (cache !== undefined) return callback(null, cache);
|
1124
1224
|
this.contextHashQueue.add(path, callback);
|
1125
1225
|
}
|
1126
1226
|
|
1227
|
+
/**
|
1228
|
+
* @param {string} path context path
|
1229
|
+
* @param {function(WebpackError=, ResolvedContextTimestampAndHash=): void} callback callback function
|
1230
|
+
* @returns {void}
|
1231
|
+
*/
|
1232
|
+
getContextTsh(path, callback) {
|
1233
|
+
const cache = this._contextTshs.get(path);
|
1234
|
+
if (cache !== undefined) {
|
1235
|
+
const resolved = getResolvedTimestamp(cache);
|
1236
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1237
|
+
return this._resolveContextTsh(cache, callback);
|
1238
|
+
}
|
1239
|
+
this.contextTshQueue.add(path, (err, entry) => {
|
1240
|
+
if (err) return callback(err);
|
1241
|
+
const resolved = getResolvedTimestamp(entry);
|
1242
|
+
if (resolved !== undefined) return callback(null, resolved);
|
1243
|
+
this._resolveContextTsh(entry, callback);
|
1244
|
+
});
|
1245
|
+
}
|
1246
|
+
|
1247
|
+
/**
|
1248
|
+
* @param {string} path context path
|
1249
|
+
* @param {function(WebpackError=, ContextTimestampAndHash=): void} callback callback function
|
1250
|
+
* @returns {void}
|
1251
|
+
*/
|
1252
|
+
_getUnresolvedContextTsh(path, callback) {
|
1253
|
+
const cache = this._contextTshs.get(path);
|
1254
|
+
if (cache !== undefined) return callback(null, cache);
|
1255
|
+
this.contextTshQueue.add(path, callback);
|
1256
|
+
}
|
1257
|
+
|
1127
1258
|
_createBuildDependenciesResolvers() {
|
1128
1259
|
const resolveContext = createResolver({
|
1129
1260
|
resolveToContext: true,
|
@@ -2007,11 +2138,15 @@ class FileSystemInfo {
|
|
2007
2138
|
);
|
2008
2139
|
for (const path of capturedDirectories) {
|
2009
2140
|
const cache = this._contextTshs.get(path);
|
2010
|
-
|
2011
|
-
|
2141
|
+
let resolved;
|
2142
|
+
if (
|
2143
|
+
cache !== undefined &&
|
2144
|
+
(resolved = getResolvedTimestamp(cache)) !== undefined
|
2145
|
+
) {
|
2146
|
+
contextTshs.set(path, resolved);
|
2012
2147
|
} else {
|
2013
2148
|
jobs++;
|
2014
|
-
|
2149
|
+
const callback = (err, entry) => {
|
2015
2150
|
if (err) {
|
2016
2151
|
if (this.logger) {
|
2017
2152
|
this.logger.debug(
|
@@ -2023,7 +2158,12 @@ class FileSystemInfo {
|
|
2023
2158
|
contextTshs.set(path, entry);
|
2024
2159
|
jobDone();
|
2025
2160
|
}
|
2026
|
-
}
|
2161
|
+
};
|
2162
|
+
if (cache !== undefined) {
|
2163
|
+
this._resolveContextTsh(cache, callback);
|
2164
|
+
} else {
|
2165
|
+
this.getContextTsh(path, callback);
|
2166
|
+
}
|
2027
2167
|
}
|
2028
2168
|
}
|
2029
2169
|
break;
|
@@ -2035,11 +2175,15 @@ class FileSystemInfo {
|
|
2035
2175
|
);
|
2036
2176
|
for (const path of capturedDirectories) {
|
2037
2177
|
const cache = this._contextHashes.get(path);
|
2038
|
-
|
2039
|
-
|
2178
|
+
let resolved;
|
2179
|
+
if (
|
2180
|
+
cache !== undefined &&
|
2181
|
+
(resolved = getResolvedHash(cache)) !== undefined
|
2182
|
+
) {
|
2183
|
+
contextHashes.set(path, resolved);
|
2040
2184
|
} else {
|
2041
2185
|
jobs++;
|
2042
|
-
|
2186
|
+
const callback = (err, entry) => {
|
2043
2187
|
if (err) {
|
2044
2188
|
if (this.logger) {
|
2045
2189
|
this.logger.debug(
|
@@ -2051,7 +2195,12 @@ class FileSystemInfo {
|
|
2051
2195
|
contextHashes.set(path, entry);
|
2052
2196
|
jobDone();
|
2053
2197
|
}
|
2054
|
-
}
|
2198
|
+
};
|
2199
|
+
if (cache !== undefined) {
|
2200
|
+
this._resolveContextHash(cache, callback);
|
2201
|
+
} else {
|
2202
|
+
this.getContextHash(path, callback);
|
2203
|
+
}
|
2055
2204
|
}
|
2056
2205
|
}
|
2057
2206
|
break;
|
@@ -2064,13 +2213,15 @@ class FileSystemInfo {
|
|
2064
2213
|
);
|
2065
2214
|
for (const path of capturedDirectories) {
|
2066
2215
|
const cache = this._contextTimestamps.get(path);
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2216
|
+
let resolved;
|
2217
|
+
if (
|
2218
|
+
cache !== undefined &&
|
2219
|
+
(resolved = getResolvedTimestamp(cache)) !== undefined
|
2220
|
+
) {
|
2221
|
+
contextTimestamps.set(path, resolved);
|
2222
|
+
} else if (cache !== "ignore") {
|
2072
2223
|
jobs++;
|
2073
|
-
|
2224
|
+
const callback = (err, entry) => {
|
2074
2225
|
if (err) {
|
2075
2226
|
if (this.logger) {
|
2076
2227
|
this.logger.debug(
|
@@ -2082,7 +2233,12 @@ class FileSystemInfo {
|
|
2082
2233
|
contextTimestamps.set(path, entry);
|
2083
2234
|
jobDone();
|
2084
2235
|
}
|
2085
|
-
}
|
2236
|
+
};
|
2237
|
+
if (cache !== undefined) {
|
2238
|
+
this._resolveContextTimestamp(cache, callback);
|
2239
|
+
} else {
|
2240
|
+
this.getContextTimestamp(path, callback);
|
2241
|
+
}
|
2086
2242
|
}
|
2087
2243
|
}
|
2088
2244
|
break;
|
@@ -2099,7 +2255,7 @@ class FileSystemInfo {
|
|
2099
2255
|
const cache = this._fileTimestamps.get(path);
|
2100
2256
|
if (cache !== undefined) {
|
2101
2257
|
if (cache !== "ignore") {
|
2102
|
-
missingExistence.set(path,
|
2258
|
+
missingExistence.set(path, Boolean(cache));
|
2103
2259
|
}
|
2104
2260
|
} else {
|
2105
2261
|
jobs++;
|
@@ -2112,7 +2268,7 @@ class FileSystemInfo {
|
|
2112
2268
|
}
|
2113
2269
|
jobError();
|
2114
2270
|
} else {
|
2115
|
-
missingExistence.set(path,
|
2271
|
+
missingExistence.set(path, Boolean(entry));
|
2116
2272
|
jobDone();
|
2117
2273
|
}
|
2118
2274
|
});
|
@@ -2321,17 +2477,7 @@ class FileSystemInfo {
|
|
2321
2477
|
*/
|
2322
2478
|
const checkFile = (path, current, snap, log = true) => {
|
2323
2479
|
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
|
-
}
|
2480
|
+
if (!checkExistence(path, Boolean(current), Boolean(snap))) return false;
|
2335
2481
|
if (current) {
|
2336
2482
|
// For existing items only
|
2337
2483
|
if (typeof startTime === "number" && current.safeTime > startTime) {
|
@@ -2363,6 +2509,34 @@ class FileSystemInfo {
|
|
2363
2509
|
}
|
2364
2510
|
return false;
|
2365
2511
|
}
|
2512
|
+
}
|
2513
|
+
return true;
|
2514
|
+
};
|
2515
|
+
/**
|
2516
|
+
* @param {string} path file path
|
2517
|
+
* @param {ResolvedContextFileSystemInfoEntry} current current entry
|
2518
|
+
* @param {ResolvedContextFileSystemInfoEntry} snap entry from snapshot
|
2519
|
+
* @param {boolean} log log reason
|
2520
|
+
* @returns {boolean} true, if ok
|
2521
|
+
*/
|
2522
|
+
const checkContext = (path, current, snap, log = true) => {
|
2523
|
+
if (current === snap) return true;
|
2524
|
+
if (!checkExistence(path, Boolean(current), Boolean(snap))) return false;
|
2525
|
+
if (current) {
|
2526
|
+
// For existing items only
|
2527
|
+
if (typeof startTime === "number" && current.safeTime > startTime) {
|
2528
|
+
// If a change happened after starting reading the item
|
2529
|
+
// this may no longer be valid
|
2530
|
+
if (log && this._remainingLogs > 0) {
|
2531
|
+
this._log(
|
2532
|
+
path,
|
2533
|
+
`it may have changed (%d) after the start time of the snapshot (%d)`,
|
2534
|
+
current.safeTime,
|
2535
|
+
startTime
|
2536
|
+
);
|
2537
|
+
}
|
2538
|
+
return false;
|
2539
|
+
}
|
2366
2540
|
if (
|
2367
2541
|
snap.timestampHash !== undefined &&
|
2368
2542
|
current.timestampHash !== snap.timestampHash
|
@@ -2487,41 +2661,59 @@ class FileSystemInfo {
|
|
2487
2661
|
this._statTestedEntries += contextTimestamps.size;
|
2488
2662
|
for (const [path, ts] of contextTimestamps) {
|
2489
2663
|
const cache = this._contextTimestamps.get(path);
|
2490
|
-
|
2491
|
-
|
2664
|
+
let resolved;
|
2665
|
+
if (
|
2666
|
+
cache !== undefined &&
|
2667
|
+
(resolved = getResolvedTimestamp(cache)) !== undefined
|
2668
|
+
) {
|
2669
|
+
if (!checkContext(path, resolved, ts)) {
|
2492
2670
|
invalid();
|
2493
2671
|
return;
|
2494
2672
|
}
|
2495
|
-
} else {
|
2673
|
+
} else if (cache !== "ignore") {
|
2496
2674
|
jobs++;
|
2497
|
-
|
2675
|
+
const callback = (err, entry) => {
|
2498
2676
|
if (err) return invalidWithError(path, err);
|
2499
|
-
if (!
|
2677
|
+
if (!checkContext(path, entry, ts)) {
|
2500
2678
|
invalid();
|
2501
2679
|
} else {
|
2502
2680
|
jobDone();
|
2503
2681
|
}
|
2504
|
-
}
|
2682
|
+
};
|
2683
|
+
if (cache !== undefined) {
|
2684
|
+
this._resolveContextTimestamp(cache, callback);
|
2685
|
+
} else {
|
2686
|
+
this.getContextTimestamp(path, callback);
|
2687
|
+
}
|
2505
2688
|
}
|
2506
2689
|
}
|
2507
2690
|
}
|
2508
2691
|
const processContextHashSnapshot = (path, hash) => {
|
2509
2692
|
const cache = this._contextHashes.get(path);
|
2510
|
-
|
2511
|
-
|
2693
|
+
let resolved;
|
2694
|
+
if (
|
2695
|
+
cache !== undefined &&
|
2696
|
+
(resolved = getResolvedHash(cache)) !== undefined
|
2697
|
+
) {
|
2698
|
+
if (!checkHash(path, resolved, hash)) {
|
2512
2699
|
invalid();
|
2513
2700
|
return;
|
2514
2701
|
}
|
2515
2702
|
} else {
|
2516
2703
|
jobs++;
|
2517
|
-
|
2704
|
+
const callback = (err, entry) => {
|
2518
2705
|
if (err) return invalidWithError(path, err);
|
2519
2706
|
if (!checkHash(path, entry, hash)) {
|
2520
2707
|
invalid();
|
2521
2708
|
} else {
|
2522
2709
|
jobDone();
|
2523
2710
|
}
|
2524
|
-
}
|
2711
|
+
};
|
2712
|
+
if (cache !== undefined) {
|
2713
|
+
this._resolveContextHash(cache, callback);
|
2714
|
+
} else {
|
2715
|
+
this.getContextHash(path, callback);
|
2716
|
+
}
|
2525
2717
|
}
|
2526
2718
|
};
|
2527
2719
|
if (snapshot.hasContextHashes()) {
|
@@ -2539,19 +2731,28 @@ class FileSystemInfo {
|
|
2539
2731
|
processContextHashSnapshot(path, tsh);
|
2540
2732
|
} else {
|
2541
2733
|
const cache = this._contextTimestamps.get(path);
|
2542
|
-
|
2543
|
-
|
2734
|
+
let resolved;
|
2735
|
+
if (
|
2736
|
+
cache !== undefined &&
|
2737
|
+
(resolved = getResolvedTimestamp(cache)) !== undefined
|
2738
|
+
) {
|
2739
|
+
if (!checkContext(path, resolved, tsh, false)) {
|
2544
2740
|
processContextHashSnapshot(path, tsh.hash);
|
2545
2741
|
}
|
2546
|
-
} else {
|
2742
|
+
} else if (cache !== "ignore") {
|
2547
2743
|
jobs++;
|
2548
|
-
|
2744
|
+
const callback = (err, entry) => {
|
2549
2745
|
if (err) return invalidWithError(path, err);
|
2550
|
-
if (!
|
2746
|
+
if (!checkContext(path, entry, tsh, false)) {
|
2551
2747
|
processContextHashSnapshot(path, tsh.hash);
|
2552
2748
|
}
|
2553
2749
|
jobDone();
|
2554
|
-
}
|
2750
|
+
};
|
2751
|
+
if (cache !== undefined) {
|
2752
|
+
this._resolveContextTsh(cache, callback);
|
2753
|
+
} else {
|
2754
|
+
this.getContextTsh(path, callback);
|
2755
|
+
}
|
2555
2756
|
}
|
2556
2757
|
}
|
2557
2758
|
}
|
@@ -2564,7 +2765,7 @@ class FileSystemInfo {
|
|
2564
2765
|
if (cache !== undefined) {
|
2565
2766
|
if (
|
2566
2767
|
cache !== "ignore" &&
|
2567
|
-
!checkExistence(path,
|
2768
|
+
!checkExistence(path, Boolean(cache), Boolean(existence))
|
2568
2769
|
) {
|
2569
2770
|
invalid();
|
2570
2771
|
return;
|
@@ -2573,7 +2774,7 @@ class FileSystemInfo {
|
|
2573
2774
|
jobs++;
|
2574
2775
|
this.fileTimestampQueue.add(path, (err, entry) => {
|
2575
2776
|
if (err) return invalidWithError(path, err);
|
2576
|
-
if (!checkExistence(path,
|
2777
|
+
if (!checkExistence(path, Boolean(entry), Boolean(existence))) {
|
2577
2778
|
invalid();
|
2578
2779
|
} else {
|
2579
2780
|
jobDone();
|
@@ -2727,12 +2928,34 @@ class FileSystemInfo {
|
|
2727
2928
|
}
|
2728
2929
|
}
|
2729
2930
|
|
2730
|
-
|
2931
|
+
/**
|
2932
|
+
* @template T
|
2933
|
+
* @template ItemType
|
2934
|
+
* @param {Object} options options
|
2935
|
+
* @param {string} options.path path
|
2936
|
+
* @param {function(string): ItemType} options.fromImmutablePath called when context item is an immutable path
|
2937
|
+
* @param {function(string): ItemType} options.fromManagedItem called when context item is a managed path
|
2938
|
+
* @param {function(string, string, function(Error=, ItemType=): void): void} options.fromSymlink called when context item is a symlink
|
2939
|
+
* @param {function(string, IStats, function(Error=, ItemType=): void): void} options.fromFile called when context item is a file
|
2940
|
+
* @param {function(string, IStats, function(Error=, ItemType=): void): void} options.fromDirectory called when context item is a directory
|
2941
|
+
* @param {function(string[], ItemType[]): T} options.reduce called from all context items
|
2942
|
+
* @param {function(Error=, (T)=): void} callback callback
|
2943
|
+
*/
|
2944
|
+
_readContext(
|
2945
|
+
{
|
2946
|
+
path,
|
2947
|
+
fromImmutablePath,
|
2948
|
+
fromManagedItem,
|
2949
|
+
fromSymlink,
|
2950
|
+
fromFile,
|
2951
|
+
fromDirectory,
|
2952
|
+
reduce
|
2953
|
+
},
|
2954
|
+
callback
|
2955
|
+
) {
|
2731
2956
|
this.fs.readdir(path, (err, _files) => {
|
2732
2957
|
if (err) {
|
2733
2958
|
if (err.code === "ENOENT") {
|
2734
|
-
this._contextTimestamps.set(path, null);
|
2735
|
-
this._cachedDeprecatedContextTimestamps = undefined;
|
2736
2959
|
return callback(null, null);
|
2737
2960
|
}
|
2738
2961
|
return callback(err);
|
@@ -2745,47 +2968,94 @@ class FileSystemInfo {
|
|
2745
2968
|
files,
|
2746
2969
|
(file, callback) => {
|
2747
2970
|
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
|
-
}
|
2971
|
+
for (const immutablePath of this.immutablePathsWithSlash) {
|
2972
|
+
if (path.startsWith(immutablePath)) {
|
2973
|
+
// ignore any immutable path for timestamping
|
2974
|
+
return callback(null, fromImmutablePath(immutablePath));
|
2756
2975
|
}
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2762
|
-
|
2763
|
-
|
2764
|
-
|
2765
|
-
|
2766
|
-
|
2767
|
-
});
|
2768
|
-
});
|
2769
|
-
}
|
2976
|
+
}
|
2977
|
+
for (const managedPath of this.managedPathsWithSlash) {
|
2978
|
+
if (path.startsWith(managedPath)) {
|
2979
|
+
const managedItem = getManagedItem(managedPath, child);
|
2980
|
+
if (managedItem) {
|
2981
|
+
// construct timestampHash from managed info
|
2982
|
+
return this.managedItemQueue.add(managedItem, (err, info) => {
|
2983
|
+
if (err) return callback(err);
|
2984
|
+
return callback(null, fromManagedItem(info));
|
2985
|
+
});
|
2770
2986
|
}
|
2771
2987
|
}
|
2988
|
+
}
|
2989
|
+
|
2990
|
+
lstatReadlinkAbsolute(this.fs, child, (err, stat) => {
|
2991
|
+
if (err) return callback(err);
|
2992
|
+
|
2993
|
+
if (typeof stat === "string") {
|
2994
|
+
return fromSymlink(child, stat, callback);
|
2995
|
+
}
|
2772
2996
|
|
2773
2997
|
if (stat.isFile()) {
|
2774
|
-
return
|
2998
|
+
return fromFile(child, stat, callback);
|
2775
2999
|
}
|
2776
3000
|
if (stat.isDirectory()) {
|
2777
|
-
|
2778
|
-
this.getContextTimestamp(child, (err, tsEntry) => {
|
2779
|
-
this.contextTimestampQueue.decreaseParallelism();
|
2780
|
-
callback(err, tsEntry);
|
2781
|
-
});
|
2782
|
-
return;
|
3001
|
+
return fromDirectory(child, stat, callback);
|
2783
3002
|
}
|
2784
3003
|
callback(null, null);
|
2785
3004
|
});
|
2786
3005
|
},
|
2787
|
-
(err,
|
3006
|
+
(err, results) => {
|
2788
3007
|
if (err) return callback(err);
|
3008
|
+
const result = reduce(files, results);
|
3009
|
+
callback(null, result);
|
3010
|
+
}
|
3011
|
+
);
|
3012
|
+
});
|
3013
|
+
}
|
3014
|
+
|
3015
|
+
_readContextTimestamp(path, callback) {
|
3016
|
+
this._readContext(
|
3017
|
+
{
|
3018
|
+
path,
|
3019
|
+
fromImmutablePath: () => null,
|
3020
|
+
fromManagedItem: info => ({
|
3021
|
+
safeTime: 0,
|
3022
|
+
timestampHash: info
|
3023
|
+
}),
|
3024
|
+
fromSymlink: (file, target, callback) => {
|
3025
|
+
callback(null, {
|
3026
|
+
timestampHash: target,
|
3027
|
+
symlinks: new Set([target])
|
3028
|
+
});
|
3029
|
+
},
|
3030
|
+
fromFile: (file, stat, callback) => {
|
3031
|
+
// Prefer the cached value over our new stat to report consistent results
|
3032
|
+
const cache = this._fileTimestamps.get(file);
|
3033
|
+
if (cache !== undefined)
|
3034
|
+
return callback(null, cache === "ignore" ? null : cache);
|
3035
|
+
|
3036
|
+
const mtime = +stat.mtime;
|
3037
|
+
|
3038
|
+
if (mtime) applyMtime(mtime);
|
3039
|
+
|
3040
|
+
const ts = {
|
3041
|
+
safeTime: mtime ? mtime + FS_ACCURACY : Infinity,
|
3042
|
+
timestamp: mtime
|
3043
|
+
};
|
3044
|
+
|
3045
|
+
this._fileTimestamps.set(file, ts);
|
3046
|
+
this._cachedDeprecatedFileTimestamps = undefined;
|
3047
|
+
callback(null, ts);
|
3048
|
+
},
|
3049
|
+
fromDirectory: (directory, stat, callback) => {
|
3050
|
+
this.contextTimestampQueue.increaseParallelism();
|
3051
|
+
this._getUnresolvedContextTimestamp(directory, (err, tsEntry) => {
|
3052
|
+
this.contextTimestampQueue.decreaseParallelism();
|
3053
|
+
callback(err, tsEntry);
|
3054
|
+
});
|
3055
|
+
},
|
3056
|
+
reduce: (files, tsEntries) => {
|
3057
|
+
let symlinks = undefined;
|
3058
|
+
|
2789
3059
|
const hash = createHash("md4");
|
2790
3060
|
|
2791
3061
|
for (const file of files) hash.update(file);
|
@@ -2802,6 +3072,10 @@ class FileSystemInfo {
|
|
2802
3072
|
hash.update("d");
|
2803
3073
|
hash.update(`${entry.timestampHash}`);
|
2804
3074
|
}
|
3075
|
+
if (entry.symlinks !== undefined) {
|
3076
|
+
if (symlinks === undefined) symlinks = new Set();
|
3077
|
+
addAll(entry.symlinks, symlinks);
|
3078
|
+
}
|
2805
3079
|
if (entry.safeTime) {
|
2806
3080
|
safeTime = Math.max(safeTime, entry.safeTime);
|
2807
3081
|
}
|
@@ -2813,131 +3087,326 @@ class FileSystemInfo {
|
|
2813
3087
|
safeTime,
|
2814
3088
|
timestampHash: digest
|
2815
3089
|
};
|
2816
|
-
|
2817
|
-
|
2818
|
-
this._cachedDeprecatedContextTimestamps = undefined;
|
2819
|
-
|
2820
|
-
callback(null, result);
|
3090
|
+
if (symlinks) result.symlinks = symlinks;
|
3091
|
+
return result;
|
2821
3092
|
}
|
2822
|
-
|
2823
|
-
|
2824
|
-
|
3093
|
+
},
|
3094
|
+
(err, result) => {
|
3095
|
+
if (err) return callback(err);
|
3096
|
+
this._contextTimestamps.set(path, result);
|
3097
|
+
this._cachedDeprecatedContextTimestamps = undefined;
|
2825
3098
|
|
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);
|
3099
|
+
callback(null, result);
|
2834
3100
|
}
|
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);
|
3101
|
+
);
|
3102
|
+
}
|
2845
3103
|
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
2850
|
-
|
3104
|
+
_resolveContextTimestamp(entry, callback) {
|
3105
|
+
const hashes = [];
|
3106
|
+
let safeTime = 0;
|
3107
|
+
processAsyncTree(
|
3108
|
+
entry.symlinks,
|
3109
|
+
10,
|
3110
|
+
(target, push, callback) => {
|
3111
|
+
this._getUnresolvedContextTimestamp(target, (err, entry) => {
|
3112
|
+
if (err) return callback(err);
|
3113
|
+
if (entry && entry !== "ignore") {
|
3114
|
+
hashes.push(entry.timestampHash);
|
3115
|
+
if (entry.safeTime) {
|
3116
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
2851
3117
|
}
|
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
|
-
}
|
3118
|
+
if (entry.symlinks !== undefined) {
|
3119
|
+
for (const target of entry.symlinks) push(target);
|
2863
3120
|
}
|
3121
|
+
}
|
3122
|
+
callback();
|
3123
|
+
});
|
3124
|
+
},
|
3125
|
+
err => {
|
3126
|
+
if (err) return callback(err);
|
3127
|
+
const hash = createHash("md4");
|
3128
|
+
hash.update(entry.timestampHash);
|
3129
|
+
if (entry.safeTime) {
|
3130
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
3131
|
+
}
|
3132
|
+
hashes.sort();
|
3133
|
+
for (const h of hashes) {
|
3134
|
+
hash.update(h);
|
3135
|
+
}
|
3136
|
+
callback(
|
3137
|
+
null,
|
3138
|
+
(entry.resolved = {
|
3139
|
+
safeTime,
|
3140
|
+
timestampHash: /** @type {string} */ (hash.digest("hex"))
|
3141
|
+
})
|
3142
|
+
);
|
3143
|
+
}
|
3144
|
+
);
|
3145
|
+
}
|
2864
3146
|
|
2865
|
-
|
2866
|
-
|
2867
|
-
|
2868
|
-
|
2869
|
-
|
2870
|
-
|
2871
|
-
|
2872
|
-
|
2873
|
-
|
2874
|
-
|
2875
|
-
});
|
2876
|
-
return;
|
2877
|
-
}
|
2878
|
-
callback(null, "");
|
3147
|
+
_readContextHash(path, callback) {
|
3148
|
+
this._readContext(
|
3149
|
+
{
|
3150
|
+
path,
|
3151
|
+
fromImmutablePath: () => "",
|
3152
|
+
fromManagedItem: info => info || "",
|
3153
|
+
fromSymlink: (file, target, callback) => {
|
3154
|
+
callback(null, {
|
3155
|
+
hash: target,
|
3156
|
+
symlinks: new Set([target])
|
2879
3157
|
});
|
2880
3158
|
},
|
2881
|
-
(
|
2882
|
-
|
3159
|
+
fromFile: (file, stat, callback) =>
|
3160
|
+
this.getFileHash(file, (err, hash) => {
|
3161
|
+
callback(err, hash || "");
|
3162
|
+
}),
|
3163
|
+
fromDirectory: (directory, stat, callback) => {
|
3164
|
+
this.contextHashQueue.increaseParallelism();
|
3165
|
+
this._getUnresolvedContextHash(directory, (err, hash) => {
|
3166
|
+
this.contextHashQueue.decreaseParallelism();
|
3167
|
+
callback(err, hash || "");
|
3168
|
+
});
|
3169
|
+
},
|
3170
|
+
/**
|
3171
|
+
* @param {string[]} files files
|
3172
|
+
* @param {(string | ContextHash)[]} fileHashes hashes
|
3173
|
+
* @returns {ContextHash} reduced hash
|
3174
|
+
*/
|
3175
|
+
reduce: (files, fileHashes) => {
|
3176
|
+
let symlinks = undefined;
|
2883
3177
|
const hash = createHash("md4");
|
2884
3178
|
|
2885
3179
|
for (const file of files) hash.update(file);
|
2886
|
-
for (const
|
2887
|
-
|
2888
|
-
|
2889
|
-
|
2890
|
-
|
3180
|
+
for (const entry of fileHashes) {
|
3181
|
+
if (typeof entry === "string") {
|
3182
|
+
hash.update(entry);
|
3183
|
+
} else {
|
3184
|
+
hash.update(entry.hash);
|
3185
|
+
if (entry.symlinks) {
|
3186
|
+
if (symlinks === undefined) symlinks = new Set();
|
3187
|
+
addAll(entry.symlinks, symlinks);
|
3188
|
+
}
|
3189
|
+
}
|
3190
|
+
}
|
2891
3191
|
|
2892
|
-
|
3192
|
+
const result = {
|
3193
|
+
hash: /** @type {string} */ (hash.digest("hex"))
|
3194
|
+
};
|
3195
|
+
if (symlinks) result.symlinks = symlinks;
|
3196
|
+
return result;
|
2893
3197
|
}
|
2894
|
-
|
2895
|
-
|
3198
|
+
},
|
3199
|
+
(err, result) => {
|
3200
|
+
if (err) return callback(err);
|
3201
|
+
this._contextHashes.set(path, result);
|
3202
|
+
return callback(null, result);
|
3203
|
+
}
|
3204
|
+
);
|
2896
3205
|
}
|
2897
3206
|
|
2898
|
-
|
2899
|
-
const
|
2900
|
-
|
2901
|
-
|
2902
|
-
|
2903
|
-
|
2904
|
-
|
2905
|
-
|
2906
|
-
|
2907
|
-
|
2908
|
-
|
2909
|
-
|
2910
|
-
|
2911
|
-
|
3207
|
+
_resolveContextHash(entry, callback) {
|
3208
|
+
const hashes = [];
|
3209
|
+
processAsyncTree(
|
3210
|
+
entry.symlinks,
|
3211
|
+
10,
|
3212
|
+
(target, push, callback) => {
|
3213
|
+
this._getUnresolvedContextHash(target, (err, hash) => {
|
3214
|
+
if (err) return callback(err);
|
3215
|
+
if (hash) {
|
3216
|
+
hashes.push(hash.hash);
|
3217
|
+
if (hash.symlinks !== undefined) {
|
3218
|
+
for (const target of hash.symlinks) push(target);
|
3219
|
+
}
|
3220
|
+
}
|
3221
|
+
callback();
|
3222
|
+
});
|
3223
|
+
},
|
3224
|
+
err => {
|
3225
|
+
if (err) return callback(err);
|
3226
|
+
const hash = createHash("md4");
|
3227
|
+
hash.update(entry.hash);
|
3228
|
+
hashes.sort();
|
3229
|
+
for (const h of hashes) {
|
3230
|
+
hash.update(h);
|
2912
3231
|
}
|
3232
|
+
callback(
|
3233
|
+
null,
|
3234
|
+
(entry.resolved = /** @type {string} */ (hash.digest("hex")))
|
3235
|
+
);
|
3236
|
+
}
|
3237
|
+
);
|
3238
|
+
}
|
3239
|
+
|
3240
|
+
_readContextTimestampAndHash(path, callback) {
|
3241
|
+
const finalize = (timestamp, hash) => {
|
3242
|
+
const result =
|
3243
|
+
timestamp === "ignore"
|
3244
|
+
? hash
|
3245
|
+
: {
|
3246
|
+
...timestamp,
|
3247
|
+
...hash
|
3248
|
+
};
|
3249
|
+
this._contextTshs.set(path, result);
|
3250
|
+
callback(null, result);
|
3251
|
+
};
|
3252
|
+
const cachedHash = this._contextHashes.get(path);
|
3253
|
+
const cachedTimestamp = this._contextTimestamps.get(path);
|
3254
|
+
if (cachedHash !== undefined) {
|
3255
|
+
if (cachedTimestamp !== undefined) {
|
3256
|
+
finalize(cachedTimestamp, cachedHash);
|
2913
3257
|
} else {
|
2914
3258
|
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);
|
3259
|
+
if (err) return callback(err);
|
3260
|
+
finalize(entry, cachedHash);
|
2924
3261
|
});
|
2925
3262
|
}
|
2926
|
-
};
|
2927
|
-
|
2928
|
-
const cache = this._contextHashes.get(path);
|
2929
|
-
if (cache !== undefined) {
|
2930
|
-
continueWithHash(cache);
|
2931
3263
|
} else {
|
2932
|
-
|
2933
|
-
|
2934
|
-
return callback(err);
|
2935
|
-
|
2936
|
-
|
2937
|
-
}
|
3264
|
+
if (cachedTimestamp !== undefined) {
|
3265
|
+
this.contextHashQueue.add(path, (err, entry) => {
|
3266
|
+
if (err) return callback(err);
|
3267
|
+
finalize(cachedTimestamp, entry);
|
3268
|
+
});
|
3269
|
+
} else {
|
3270
|
+
this._readContext(
|
3271
|
+
{
|
3272
|
+
path,
|
3273
|
+
fromImmutablePath: () => null,
|
3274
|
+
fromManagedItem: info => ({
|
3275
|
+
safeTime: 0,
|
3276
|
+
timestampHash: info,
|
3277
|
+
hash: info || ""
|
3278
|
+
}),
|
3279
|
+
fromSymlink: (fle, target, callback) => {
|
3280
|
+
callback(null, {
|
3281
|
+
timestampHash: target,
|
3282
|
+
hash: target,
|
3283
|
+
symlinks: new Set([target])
|
3284
|
+
});
|
3285
|
+
},
|
3286
|
+
fromFile: (file, stat, callback) => {
|
3287
|
+
this._getFileTimestampAndHash(file, callback);
|
3288
|
+
},
|
3289
|
+
fromDirectory: (directory, stat, callback) => {
|
3290
|
+
this.contextTshQueue.increaseParallelism();
|
3291
|
+
this.contextTshQueue.add(directory, (err, result) => {
|
3292
|
+
this.contextTshQueue.decreaseParallelism();
|
3293
|
+
callback(err, result);
|
3294
|
+
});
|
3295
|
+
},
|
3296
|
+
/**
|
3297
|
+
* @param {string[]} files files
|
3298
|
+
* @param {(Partial<TimestampAndHash> & Partial<ContextTimestampAndHash> | string | null)[]} results results
|
3299
|
+
* @returns {ContextTimestampAndHash} tsh
|
3300
|
+
*/
|
3301
|
+
reduce: (files, results) => {
|
3302
|
+
let symlinks = undefined;
|
3303
|
+
|
3304
|
+
const tsHash = createHash("md4");
|
3305
|
+
const hash = createHash("md4");
|
3306
|
+
|
3307
|
+
for (const file of files) {
|
3308
|
+
tsHash.update(file);
|
3309
|
+
hash.update(file);
|
3310
|
+
}
|
3311
|
+
let safeTime = 0;
|
3312
|
+
for (const entry of results) {
|
3313
|
+
if (!entry) {
|
3314
|
+
tsHash.update("n");
|
3315
|
+
continue;
|
3316
|
+
}
|
3317
|
+
if (typeof entry === "string") {
|
3318
|
+
tsHash.update("n");
|
3319
|
+
hash.update(entry);
|
3320
|
+
continue;
|
3321
|
+
}
|
3322
|
+
if (entry.timestamp) {
|
3323
|
+
tsHash.update("f");
|
3324
|
+
tsHash.update(`${entry.timestamp}`);
|
3325
|
+
} else if (entry.timestampHash) {
|
3326
|
+
tsHash.update("d");
|
3327
|
+
tsHash.update(`${entry.timestampHash}`);
|
3328
|
+
}
|
3329
|
+
if (entry.symlinks !== undefined) {
|
3330
|
+
if (symlinks === undefined) symlinks = new Set();
|
3331
|
+
addAll(entry.symlinks, symlinks);
|
3332
|
+
}
|
3333
|
+
if (entry.safeTime) {
|
3334
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
3335
|
+
}
|
3336
|
+
hash.update(entry.hash);
|
3337
|
+
}
|
3338
|
+
|
3339
|
+
const result = {
|
3340
|
+
safeTime,
|
3341
|
+
timestampHash: /** @type {string} */ (tsHash.digest("hex")),
|
3342
|
+
hash: /** @type {string} */ (hash.digest("hex"))
|
3343
|
+
};
|
3344
|
+
if (symlinks) result.symlinks = symlinks;
|
3345
|
+
return result;
|
3346
|
+
}
|
3347
|
+
},
|
3348
|
+
(err, result) => {
|
3349
|
+
if (err) return callback(err);
|
3350
|
+
this._contextTshs.set(path, result);
|
3351
|
+
return callback(null, result);
|
3352
|
+
}
|
3353
|
+
);
|
3354
|
+
}
|
2938
3355
|
}
|
2939
3356
|
}
|
2940
3357
|
|
3358
|
+
_resolveContextTsh(entry, callback) {
|
3359
|
+
const hashes = [];
|
3360
|
+
const tsHashes = [];
|
3361
|
+
let safeTime = 0;
|
3362
|
+
processAsyncTree(
|
3363
|
+
entry.symlinks,
|
3364
|
+
10,
|
3365
|
+
(target, push, callback) => {
|
3366
|
+
this._getUnresolvedContextTsh(target, (err, entry) => {
|
3367
|
+
if (err) return callback(err);
|
3368
|
+
if (entry) {
|
3369
|
+
hashes.push(entry.hash);
|
3370
|
+
if (entry.timestampHash) tsHashes.push(entry.timestampHash);
|
3371
|
+
if (entry.safeTime) {
|
3372
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
3373
|
+
}
|
3374
|
+
if (entry.symlinks !== undefined) {
|
3375
|
+
for (const target of entry.symlinks) push(target);
|
3376
|
+
}
|
3377
|
+
}
|
3378
|
+
callback();
|
3379
|
+
});
|
3380
|
+
},
|
3381
|
+
err => {
|
3382
|
+
if (err) return callback(err);
|
3383
|
+
const hash = createHash("md4");
|
3384
|
+
const tsHash = createHash("md4");
|
3385
|
+
hash.update(entry.hash);
|
3386
|
+
if (entry.timestampHash) tsHash.update(entry.timestampHash);
|
3387
|
+
if (entry.safeTime) {
|
3388
|
+
safeTime = Math.max(safeTime, entry.safeTime);
|
3389
|
+
}
|
3390
|
+
hashes.sort();
|
3391
|
+
for (const h of hashes) {
|
3392
|
+
hash.update(h);
|
3393
|
+
}
|
3394
|
+
tsHashes.sort();
|
3395
|
+
for (const h of tsHashes) {
|
3396
|
+
tsHash.update(h);
|
3397
|
+
}
|
3398
|
+
callback(
|
3399
|
+
null,
|
3400
|
+
(entry.resolved = {
|
3401
|
+
safeTime,
|
3402
|
+
timestampHash: /** @type {string} */ (tsHash.digest("hex")),
|
3403
|
+
hash: /** @type {string} */ (hash.digest("hex"))
|
3404
|
+
})
|
3405
|
+
);
|
3406
|
+
}
|
3407
|
+
);
|
3408
|
+
}
|
3409
|
+
|
2941
3410
|
_getManagedItemDirectoryInfo(path, callback) {
|
2942
3411
|
this.fs.readdir(path, (err, elements) => {
|
2943
3412
|
if (err) {
|