@atlaspack/core 2.16.2-dev.14 → 2.16.2-dev.55

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/lib/Atlaspack.js +10 -2
  3. package/lib/AtlaspackConfig.schema.js +7 -1
  4. package/lib/BundleGraph.js +2 -100
  5. package/lib/PackagerRunner.js +44 -9
  6. package/lib/RequestTracker.js +326 -121
  7. package/lib/Transformation.js +2 -2
  8. package/lib/UncommittedAsset.js +17 -0
  9. package/lib/atlaspack-v3/worker/compat/environment.js +2 -2
  10. package/lib/atlaspack-v3/worker/compat/mutable-asset.js +6 -6
  11. package/lib/atlaspack-v3/worker/compat/plugin-config.js +5 -5
  12. package/lib/atlaspack-v3/worker/index.js +3 -0
  13. package/lib/atlaspack-v3/worker/worker.js +8 -0
  14. package/lib/dumpGraphToGraphViz.js +1 -1
  15. package/lib/public/BundleGraph.js +21 -8
  16. package/lib/public/Config.js +28 -0
  17. package/lib/requests/AssetGraphRequest.js +13 -1
  18. package/lib/requests/BundleGraphRequest.js +13 -1
  19. package/lib/requests/WriteBundleRequest.js +11 -2
  20. package/lib/resolveOptions.js +7 -4
  21. package/lib/worker.js +18 -1
  22. package/package.json +23 -19
  23. package/src/Atlaspack.js +13 -5
  24. package/src/BundleGraph.js +0 -167
  25. package/src/PackagerRunner.js +60 -9
  26. package/src/RequestTracker.js +491 -137
  27. package/src/UncommittedAsset.js +16 -1
  28. package/src/atlaspack-v3/worker/compat/plugin-config.js +9 -5
  29. package/src/atlaspack-v3/worker/worker.js +7 -0
  30. package/src/public/BundleGraph.js +22 -15
  31. package/src/public/Config.js +39 -5
  32. package/src/requests/AssetGraphRequest.js +13 -3
  33. package/src/requests/BundleGraphRequest.js +13 -3
  34. package/src/requests/WriteBundleRequest.js +9 -2
  35. package/src/resolveOptions.js +4 -2
  36. package/test/RequestTracker.test.js +120 -5
  37. package/test/test-utils.js +1 -7
@@ -6,9 +6,13 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.RequestGraph = void 0;
7
7
  exports.cleanUpOrphans = cleanUpOrphans;
8
8
  exports.default = void 0;
9
+ exports.getBiggestFSEventsInvalidations = getBiggestFSEventsInvalidations;
9
10
  exports.getWatcherOptions = getWatcherOptions;
11
+ exports.invalidateRequestGraph = invalidateRequestGraph;
12
+ exports.invalidateRequestGraphFSEvents = invalidateRequestGraphFSEvents;
10
13
  exports.readAndDeserializeRequestGraph = readAndDeserializeRequestGraph;
11
14
  exports.requestTypes = exports.requestGraphEdgeTypes = void 0;
15
+ exports.runInvalidation = runInvalidation;
12
16
  function _assert() {
13
17
  const data = _interopRequireWildcard(require("assert"));
14
18
  _assert = function () {
@@ -84,6 +88,13 @@ var _projectPath = require("./projectPath");
84
88
  var _ReporterRunner = require("./ReporterRunner");
85
89
  var _ConfigRequest = require("./requests/ConfigRequest");
86
90
  var _utils2 = require("./utils");
91
+ function _perf_hooks() {
92
+ const data = require("perf_hooks");
93
+ _perf_hooks = function () {
94
+ return data;
95
+ };
96
+ return data;
97
+ }
87
98
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
88
99
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
89
100
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -279,6 +290,12 @@ class RequestGraph extends _graph().ContentGraph {
279
290
  // If the node is invalidated, the cached request chunk on disk needs to be re-written
280
291
  this.removeCachedRequestChunkForNode(nodeId);
281
292
  }
293
+
294
+ /**
295
+ * Nodes that are invalidated on start-up, such as JavaScript babel configuration files which are
296
+ * imported when the build kicks-off and might doing arbitrary work such as reading from the file
297
+ * system.
298
+ */
282
299
  invalidateUnpredictableNodes() {
283
300
  for (let nodeId of this.unpredicatableNodeIds) {
284
301
  let node = (0, _nullthrows().default)(this.getNode(nodeId));
@@ -286,6 +303,10 @@ class RequestGraph extends _graph().ContentGraph {
286
303
  this.invalidateNode(nodeId, _constants.STARTUP);
287
304
  }
288
305
  }
306
+
307
+ /**
308
+ * Effectively uncacheable nodes.
309
+ */
289
310
  invalidateOnBuildNodes() {
290
311
  for (let nodeId of this.invalidateOnBuildNodeIds) {
291
312
  let node = (0, _nullthrows().default)(this.getNode(nodeId));
@@ -293,29 +314,45 @@ class RequestGraph extends _graph().ContentGraph {
293
314
  this.invalidateNode(nodeId, _constants.STARTUP);
294
315
  }
295
316
  }
317
+
318
+ /**
319
+ * Nodes invalidated by environment changes, corresponds to `env: ...` inputs.
320
+ */
296
321
  invalidateEnvNodes(env) {
322
+ const invalidatedKeys = [];
297
323
  for (let nodeId of this.envNodeIds) {
298
324
  let node = (0, _nullthrows().default)(this.getNode(nodeId));
299
325
  (0, _assert().default)(node.type === ENV);
300
- if (env[keyFromEnvContentKey(node.id)] !== node.value) {
326
+ const key = keyFromEnvContentKey(node.id);
327
+ if (env[key] !== node.value) {
328
+ invalidatedKeys.push(key);
301
329
  let parentNodes = this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_update);
302
330
  for (let parentNode of parentNodes) {
303
331
  this.invalidateNode(parentNode, _constants.ENV_CHANGE);
304
332
  }
305
333
  }
306
334
  }
335
+ return invalidatedKeys;
307
336
  }
337
+
338
+ /**
339
+ * Nodes invalidated by option changes.
340
+ */
308
341
  invalidateOptionNodes(options) {
342
+ const invalidatedKeys = [];
309
343
  for (let nodeId of this.optionNodeIds) {
310
344
  let node = (0, _nullthrows().default)(this.getNode(nodeId));
311
345
  (0, _assert().default)(node.type === OPTION);
312
- if ((0, _utils2.hashFromOption)(options[keyFromOptionContentKey(node.id)]) !== node.hash) {
346
+ const key = keyFromOptionContentKey(node.id);
347
+ if ((0, _utils2.hashFromOption)(options[key]) !== node.hash) {
348
+ invalidatedKeys.push(key);
313
349
  let parentNodes = this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_update);
314
350
  for (let parentNode of parentNodes) {
315
351
  this.invalidateNode(parentNode, _constants.OPTION_CHANGE);
316
352
  }
317
353
  }
318
354
  }
355
+ return invalidatedKeys;
319
356
  }
320
357
  invalidateOnConfigKeyChange(requestNodeId, filePath, configKey, contentHash) {
321
358
  let configKeyNodeId = this.addNode(nodeFromConfigKey(filePath, configKey, contentHash));
@@ -556,10 +593,12 @@ class RequestGraph extends _graph().ContentGraph {
556
593
  aboveCache.set(fileNameNodeId, above);
557
594
  return above;
558
595
  };
596
+ const invalidationsByPath = new Map();
559
597
  for (let {
560
598
  path: _path,
561
599
  type
562
600
  } of events) {
601
+ const invalidationsBefore = this.getInvalidNodeCount();
563
602
  if (!enableOptimization && process.env.ATLASPACK_DISABLE_CACHE_TIMEOUT !== 'true' && ++count === 256) {
564
603
  let duration = Date.now() - startTime;
565
604
  predictedTime = duration * (events.length >> 8);
@@ -596,7 +635,10 @@ class RequestGraph extends _graph().ContentGraph {
596
635
  this.invalidNodeIds.add(id);
597
636
  }
598
637
  }
599
- return true;
638
+ return {
639
+ didInvalidate: true,
640
+ invalidationsByPath: new Map()
641
+ };
600
642
  }
601
643
 
602
644
  // sometimes mac os reports update events as create events.
@@ -671,6 +713,9 @@ class RequestGraph extends _graph().ContentGraph {
671
713
  }
672
714
  }
673
715
  }
716
+ const invalidationsAfter = this.getInvalidNodeCount();
717
+ const invalidationsForEvent = invalidationsAfter - invalidationsBefore;
718
+ invalidationsByPath.set(_path, (invalidationsByPath.get(_path) ?? 0) + invalidationsForEvent);
674
719
  }
675
720
  if ((0, _featureFlags().getFeatureFlag)('fixQuadraticCacheInvalidation')) {
676
721
  cleanUpOrphans(this);
@@ -688,7 +733,10 @@ class RequestGraph extends _graph().ContentGraph {
688
733
  numberOfInvalidatedNodes: invalidatedNodes.size
689
734
  }
690
735
  });
691
- return didInvalidate && this.invalidNodeIds.size > 0;
736
+ return {
737
+ didInvalidate,
738
+ invalidationsByPath
739
+ };
692
740
  }
693
741
  hasCachedRequestChunk(index) {
694
742
  return this.cachedRequestChunks.has(index);
@@ -699,6 +747,13 @@ class RequestGraph extends _graph().ContentGraph {
699
747
  removeCachedRequestChunkForNode(nodeId) {
700
748
  this.cachedRequestChunks.delete(Math.floor(nodeId / this.nodesPerBlob));
701
749
  }
750
+
751
+ /**
752
+ * Returns the number of invalidated nodes in the graph.
753
+ */
754
+ getInvalidNodeCount() {
755
+ return this.invalidNodeIds.size;
756
+ }
702
757
  }
703
758
  exports.RequestGraph = RequestGraph;
704
759
  class RequestTracker {
@@ -895,40 +950,41 @@ class RequestTracker {
895
950
  }
896
951
  createAPI(requestId, previousInvalidations) {
897
952
  let subRequestContentKeys = new Set();
898
- return {
899
- api: {
900
- invalidateOnFileCreate: input => this.graph.invalidateOnFileCreate(requestId, input),
901
- invalidateOnConfigKeyChange: (filePath, configKey, contentHash) => this.graph.invalidateOnConfigKeyChange(requestId, filePath, configKey, contentHash),
902
- invalidateOnFileDelete: filePath => this.graph.invalidateOnFileDelete(requestId, filePath),
903
- invalidateOnFileUpdate: filePath => this.graph.invalidateOnFileUpdate(requestId, filePath),
904
- invalidateOnStartup: () => this.graph.invalidateOnStartup(requestId),
905
- invalidateOnBuild: () => this.graph.invalidateOnBuild(requestId),
906
- invalidateOnEnvChange: env => this.graph.invalidateOnEnvChange(requestId, env, this.options.env[env]),
907
- invalidateOnOptionChange: option => this.graph.invalidateOnOptionChange(requestId, option, this.options[option]),
908
- getInvalidations: () => previousInvalidations,
909
- storeResult: (result, cacheKey) => {
910
- this.storeResult(requestId, result, cacheKey);
911
- },
912
- getSubRequests: () => this.graph.getSubRequests(requestId),
913
- getInvalidSubRequests: () => this.graph.getInvalidSubRequests(requestId),
914
- getPreviousResult: ifMatch => {
915
- var _this$graph$getNode;
916
- let contentKey = (0, _nullthrows().default)((_this$graph$getNode = this.graph.getNode(requestId)) === null || _this$graph$getNode === void 0 ? void 0 : _this$graph$getNode.id);
917
- return this.getRequestResult(contentKey, ifMatch);
918
- },
919
- getRequestResult: id => this.getRequestResult(id),
920
- canSkipSubrequest: contentKey => {
921
- if (this.graph.hasContentKey(contentKey) && this.hasValidResult(this.graph.getNodeIdByContentKey(contentKey))) {
922
- subRequestContentKeys.add(contentKey);
923
- return true;
924
- }
925
- return false;
926
- },
927
- runRequest: (subRequest, opts) => {
928
- subRequestContentKeys.add(subRequest.id);
929
- return this.runRequest(subRequest, opts);
953
+ let api = {
954
+ invalidateOnFileCreate: input => this.graph.invalidateOnFileCreate(requestId, input),
955
+ invalidateOnConfigKeyChange: (filePath, configKey, contentHash) => this.graph.invalidateOnConfigKeyChange(requestId, filePath, configKey, contentHash),
956
+ invalidateOnFileDelete: filePath => this.graph.invalidateOnFileDelete(requestId, filePath),
957
+ invalidateOnFileUpdate: filePath => this.graph.invalidateOnFileUpdate(requestId, filePath),
958
+ invalidateOnStartup: () => this.graph.invalidateOnStartup(requestId),
959
+ invalidateOnBuild: () => this.graph.invalidateOnBuild(requestId),
960
+ invalidateOnEnvChange: env => this.graph.invalidateOnEnvChange(requestId, env, this.options.env[env]),
961
+ invalidateOnOptionChange: option => this.graph.invalidateOnOptionChange(requestId, option, this.options[option]),
962
+ getInvalidations: () => previousInvalidations,
963
+ storeResult: (result, cacheKey) => {
964
+ this.storeResult(requestId, result, cacheKey);
965
+ },
966
+ getSubRequests: () => this.graph.getSubRequests(requestId),
967
+ getInvalidSubRequests: () => this.graph.getInvalidSubRequests(requestId),
968
+ getPreviousResult: ifMatch => {
969
+ var _this$graph$getNode;
970
+ let contentKey = (0, _nullthrows().default)((_this$graph$getNode = this.graph.getNode(requestId)) === null || _this$graph$getNode === void 0 ? void 0 : _this$graph$getNode.id);
971
+ return this.getRequestResult(contentKey, ifMatch);
972
+ },
973
+ getRequestResult: id => this.getRequestResult(id),
974
+ canSkipSubrequest: contentKey => {
975
+ if (this.graph.hasContentKey(contentKey) && this.hasValidResult(this.graph.getNodeIdByContentKey(contentKey))) {
976
+ subRequestContentKeys.add(contentKey);
977
+ return true;
930
978
  }
979
+ return false;
931
980
  },
981
+ runRequest: (subRequest, opts) => {
982
+ subRequestContentKeys.add(subRequest.id);
983
+ return this.runRequest(subRequest, opts);
984
+ }
985
+ };
986
+ return {
987
+ api,
932
988
  subRequestContentKeys
933
989
  };
934
990
  }
@@ -944,84 +1000,85 @@ class RequestTracker {
944
1000
  return result;
945
1001
  }
946
1002
  }
947
- await runCacheImprovements(async cache => {
948
- await cache.getNativeRef().startWriteTransaction();
949
- }, () => Promise.resolve());
950
1003
  let cacheKey = getCacheKey(this.options);
951
- let requestGraphKey = `requestGraph-${cacheKey}`;
1004
+ let requestGraphKey = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? `${cacheKey}/RequestGraph` : `requestGraph-${cacheKey}`;
1005
+ let snapshotKey = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? `${cacheKey}/snapshot` : `snapshot-${cacheKey}`;
952
1006
  if (this.options.shouldDisableCache) {
953
1007
  return;
954
1008
  }
955
1009
  let total = 0;
956
- (0, _ReporterRunner.report)({
957
- type: 'cache',
958
- phase: 'start',
959
- total,
960
- size: this.graph.nodes.length
961
- });
962
- let serialisedGraph = this.graph.serialize();
963
-
964
- // Delete an existing request graph cache, to prevent invalid states
965
- await this.options.cache.deleteLargeBlob(requestGraphKey);
966
- const serialiseAndSet = async (key, contents) => {
967
- if (signal !== null && signal !== void 0 && signal.aborted) {
968
- throw new Error('Serialization was aborted');
969
- }
970
- await runCacheImprovements(cache => {
971
- (0, _logger().instrument)(`cache.put(${key})`, () => {
972
- cache.getNativeRef().putNoConfirm(key, (0, _buildCache().serialize)(contents));
973
- });
974
- return Promise.resolve();
975
- }, async () => {
976
- await this.options.cache.setLargeBlob(key, (0, _buildCache().serialize)(contents), signal ? {
977
- signal: signal
978
- } : undefined);
979
- });
980
- total += 1;
1010
+ await runCacheImprovements(async cache => {
1011
+ await cache.getNativeRef().startWriteTransaction();
1012
+ }, () => Promise.resolve());
1013
+ try {
981
1014
  (0, _ReporterRunner.report)({
982
1015
  type: 'cache',
983
- phase: 'write',
1016
+ phase: 'start',
984
1017
  total,
985
1018
  size: this.graph.nodes.length
986
1019
  });
987
- };
988
- let queue = new (_utils().PromiseQueue)({
989
- maxConcurrent: 32
990
- });
1020
+ let serialisedGraph = this.graph.serialize();
991
1021
 
992
- // Preallocating a sparse array is faster than pushing when N is high enough
993
- let cacheableNodes = new Array(serialisedGraph.nodes.length);
994
- for (let i = 0; i < serialisedGraph.nodes.length; i += 1) {
995
- let node = serialisedGraph.nodes[i];
996
- let resultCacheKey = node === null || node === void 0 ? void 0 : node.resultCacheKey;
997
- if ((node === null || node === void 0 ? void 0 : node.type) === REQUEST && resultCacheKey != null && (node === null || node === void 0 ? void 0 : node.result) != null) {
998
- queue.add(() => serialiseAndSet(resultCacheKey, node.result));
1022
+ // Delete an existing request graph cache, to prevent invalid states
1023
+ await this.options.cache.deleteLargeBlob(requestGraphKey);
1024
+ const serialiseAndSet = async (key, contents) => {
1025
+ if (signal !== null && signal !== void 0 && signal.aborted) {
1026
+ throw new Error('Serialization was aborted');
1027
+ }
1028
+ await runCacheImprovements(cache => {
1029
+ (0, _logger().instrument)(`RequestTracker::writeToCache::cache.put(${key})`, () => {
1030
+ cache.getNativeRef().putNoConfirm(key, (0, _buildCache().serialize)(contents));
1031
+ });
1032
+ return Promise.resolve();
1033
+ }, async () => {
1034
+ await this.options.cache.setLargeBlob(key, (0, _buildCache().serialize)(contents), signal ? {
1035
+ signal: signal
1036
+ } : undefined);
1037
+ });
1038
+ total += 1;
1039
+ (0, _ReporterRunner.report)({
1040
+ type: 'cache',
1041
+ phase: 'write',
1042
+ total,
1043
+ size: this.graph.nodes.length
1044
+ });
1045
+ };
1046
+ let queue = new (_utils().PromiseQueue)({
1047
+ maxConcurrent: 32
1048
+ });
999
1049
 
1000
- // eslint-disable-next-line no-unused-vars
1001
- let {
1002
- result: _,
1003
- ...newNode
1004
- } = node;
1005
- cacheableNodes[i] = newNode;
1006
- } else {
1007
- cacheableNodes[i] = node;
1050
+ // Preallocating a sparse array is faster than pushing when N is high enough
1051
+ let cacheableNodes = new Array(serialisedGraph.nodes.length);
1052
+ for (let i = 0; i < serialisedGraph.nodes.length; i += 1) {
1053
+ let node = serialisedGraph.nodes[i];
1054
+ let resultCacheKey = node === null || node === void 0 ? void 0 : node.resultCacheKey;
1055
+ if ((node === null || node === void 0 ? void 0 : node.type) === REQUEST && resultCacheKey != null && (node === null || node === void 0 ? void 0 : node.result) != null) {
1056
+ queue.add(() => serialiseAndSet(resultCacheKey, node.result));
1057
+
1058
+ // eslint-disable-next-line no-unused-vars
1059
+ let {
1060
+ result: _,
1061
+ ...newNode
1062
+ } = node;
1063
+ cacheableNodes[i] = newNode;
1064
+ } else {
1065
+ cacheableNodes[i] = node;
1066
+ }
1008
1067
  }
1009
- }
1010
- let nodeCountsPerBlob = [];
1011
- for (let i = 0; i * this.graph.nodesPerBlob < cacheableNodes.length; i += 1) {
1012
- let nodesStartIndex = i * this.graph.nodesPerBlob;
1013
- let nodesEndIndex = Math.min((i + 1) * this.graph.nodesPerBlob, cacheableNodes.length);
1014
- nodeCountsPerBlob.push(nodesEndIndex - nodesStartIndex);
1015
- if (!this.graph.hasCachedRequestChunk(i)) {
1016
- // We assume the request graph nodes are immutable and won't change
1017
- let nodesToCache = cacheableNodes.slice(nodesStartIndex, nodesEndIndex);
1018
- queue.add(() => serialiseAndSet(getRequestGraphNodeKey(i, cacheKey), nodesToCache).then(() => {
1019
- // Succeeded in writing to disk, save that we have completed this chunk
1020
- this.graph.setCachedRequestChunk(i);
1021
- }));
1068
+ let nodeCountsPerBlob = [];
1069
+ for (let i = 0; i * this.graph.nodesPerBlob < cacheableNodes.length; i += 1) {
1070
+ let nodesStartIndex = i * this.graph.nodesPerBlob;
1071
+ let nodesEndIndex = Math.min((i + 1) * this.graph.nodesPerBlob, cacheableNodes.length);
1072
+ nodeCountsPerBlob.push(nodesEndIndex - nodesStartIndex);
1073
+ if (!this.graph.hasCachedRequestChunk(i)) {
1074
+ // We assume the request graph nodes are immutable and won't change
1075
+ let nodesToCache = cacheableNodes.slice(nodesStartIndex, nodesEndIndex);
1076
+ queue.add(() => serialiseAndSet(getRequestGraphNodeKey(i, cacheKey), nodesToCache).then(() => {
1077
+ // Succeeded in writing to disk, save that we have completed this chunk
1078
+ this.graph.setCachedRequestChunk(i);
1079
+ }));
1080
+ }
1022
1081
  }
1023
- }
1024
- try {
1025
1082
  await queue.run();
1026
1083
 
1027
1084
  // Set the request graph after the queue is flushed to avoid writing an invalid state
@@ -1030,7 +1087,7 @@ class RequestTracker {
1030
1087
  nodeCountsPerBlob,
1031
1088
  nodes: undefined
1032
1089
  });
1033
- await runCacheImprovements(() => serialiseAndSet(`request_tracker:cache_metadata:${cacheKey}`, {
1090
+ await runCacheImprovements(() => serialiseAndSet(`${cacheKey}/cache_metadata`, {
1034
1091
  version: _constants.ATLASPACK_VERSION,
1035
1092
  entries: this.options.entries,
1036
1093
  mode: this.options.mode,
@@ -1038,15 +1095,16 @@ class RequestTracker {
1038
1095
  watchBackend: this.options.watchBackend
1039
1096
  }), () => Promise.resolve());
1040
1097
  let opts = getWatcherOptions(this.options);
1041
- let snapshotPath = _path2().default.join(this.options.cacheDir, `snapshot-${cacheKey}` + '.txt');
1098
+ let snapshotPath = _path2().default.join(this.options.cacheDir, snapshotKey + '.txt');
1042
1099
  await this.options.outputFS.writeSnapshot(this.options.watchDir, snapshotPath, opts);
1043
1100
  } catch (err) {
1044
1101
  // If we have aborted, ignore the error and continue
1045
1102
  if (!(signal !== null && signal !== void 0 && signal.aborted)) throw err;
1103
+ } finally {
1104
+ await runCacheImprovements(async cache => {
1105
+ await cache.getNativeRef().commitWriteTransaction();
1106
+ }, () => Promise.resolve());
1046
1107
  }
1047
- await runCacheImprovements(async cache => {
1048
- await cache.getNativeRef().commitWriteTransaction();
1049
- }, () => Promise.resolve());
1050
1108
  (0, _ReporterRunner.report)({
1051
1109
  type: 'cache',
1052
1110
  phase: 'end',
@@ -1075,7 +1133,8 @@ function getWatcherOptions({
1075
1133
  watchDir,
1076
1134
  watchBackend
1077
1135
  }) {
1078
- const uniqueDirs = [...new Set([...watchIgnore, ...['.git', '.hg'], cacheDir])];
1136
+ const vcsDirs = ['.git', '.hg'];
1137
+ const uniqueDirs = [...new Set([...watchIgnore, ...vcsDirs, cacheDir])];
1079
1138
  const ignore = uniqueDirs.map(dir => _path2().default.resolve(watchDir, dir));
1080
1139
  return {
1081
1140
  ignore,
@@ -1083,17 +1142,30 @@ function getWatcherOptions({
1083
1142
  };
1084
1143
  }
1085
1144
  function getCacheKey(options) {
1145
+ if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
1146
+ const hash = (0, _rust().hashString)(`${_constants.ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${options.mode}:${options.shouldBuildLazily ? 'lazy' : 'eager'}:${options.watchBackend ?? ''}`);
1147
+ return `RequestTracker/${_constants.ATLASPACK_VERSION}/${hash}`;
1148
+ }
1086
1149
  return (0, _rust().hashString)(`${_constants.ATLASPACK_VERSION}:${JSON.stringify(options.entries)}:${options.mode}:${options.shouldBuildLazily ? 'lazy' : 'eager'}:${options.watchBackend ?? ''}`);
1087
1150
  }
1088
1151
  function getRequestGraphNodeKey(index, cacheKey) {
1152
+ if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
1153
+ return `${cacheKey}/RequestGraph/nodes/${index}`;
1154
+ }
1089
1155
  return `requestGraph-nodes-${index}-${cacheKey}`;
1090
1156
  }
1091
1157
  async function readAndDeserializeRequestGraph(cache, requestGraphKey, cacheKey) {
1092
1158
  let bufferLength = 0;
1093
1159
  const getAndDeserialize = async key => {
1094
- let buffer = await cache.getLargeBlob(key);
1095
- bufferLength += Buffer.byteLength(buffer);
1096
- return (0, _buildCache().deserialize)(buffer);
1160
+ if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
1161
+ const buffer = await cache.getBlob(key);
1162
+ bufferLength += Buffer.byteLength(buffer);
1163
+ return (0, _buildCache().deserialize)(buffer);
1164
+ } else {
1165
+ const buffer = await cache.getLargeBlob(key);
1166
+ bufferLength += Buffer.byteLength(buffer);
1167
+ return (0, _buildCache().deserialize)(buffer);
1168
+ }
1097
1169
  };
1098
1170
  let serializedRequestGraph = await getAndDeserialize(requestGraphKey);
1099
1171
  let nodePromises = serializedRequestGraph.nodeCountsPerBlob.map(async (nodesCount, i) => {
@@ -1115,19 +1187,30 @@ async function loadRequestGraph(options) {
1115
1187
  return new RequestGraph();
1116
1188
  }
1117
1189
  let cacheKey = getCacheKey(options);
1118
- let requestGraphKey = `requestGraph-${cacheKey}`;
1190
+ let requestGraphKey = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? `${cacheKey}/RequestGraph` : `requestGraph-${cacheKey}`;
1119
1191
  let timeout;
1120
- const snapshotKey = `snapshot-${cacheKey}`;
1192
+ const snapshotKey = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? `${cacheKey}/snapshot` : `snapshot-${cacheKey}`;
1121
1193
  const snapshotPath = _path2().default.join(options.cacheDir, snapshotKey + '.txt');
1194
+ const commonMeta = {
1195
+ cacheKey,
1196
+ snapshotKey,
1197
+ cacheKeyOptions: {
1198
+ version: _constants.ATLASPACK_VERSION,
1199
+ entries: options.entries,
1200
+ mode: options.mode,
1201
+ shouldBuildLazily: options.shouldBuildLazily,
1202
+ watchBackend: options.watchBackend
1203
+ }
1204
+ };
1122
1205
  _logger().default.verbose({
1123
1206
  origin: '@atlaspack/core',
1124
1207
  message: 'Loading request graph',
1125
1208
  meta: {
1126
- cacheKey,
1127
- snapshotKey
1209
+ ...commonMeta
1128
1210
  }
1129
1211
  });
1130
- if (await options.cache.hasLargeBlob(requestGraphKey)) {
1212
+ const hasRequestGraphInCache = (0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements') ? await options.cache.has(requestGraphKey) : await options.cache.hasLargeBlob(requestGraphKey);
1213
+ if (hasRequestGraphInCache) {
1131
1214
  try {
1132
1215
  let {
1133
1216
  requestGraph
@@ -1146,16 +1229,29 @@ async function loadRequestGraph(options) {
1146
1229
  origin: '@atlaspack/core',
1147
1230
  message: `File system event count: ${events.length}`,
1148
1231
  meta: {
1232
+ ...commonMeta,
1149
1233
  trackableEvent: 'watcher_events_count',
1150
1234
  watcherEventCount: events.length,
1151
1235
  duration: Date.now() - startTime
1152
1236
  }
1153
1237
  });
1154
- requestGraph.invalidateUnpredictableNodes();
1155
- requestGraph.invalidateOnBuildNodes();
1156
- requestGraph.invalidateEnvNodes(options.env);
1157
- requestGraph.invalidateOptionNodes(options);
1158
- await requestGraph.respondToFSEvents(options.unstableFileInvalidations || events, options, 10000, true);
1238
+ if ((0, _featureFlags().getFeatureFlag)('verboseRequestInvalidationStats')) {
1239
+ const invalidationStats = await invalidateRequestGraph(requestGraph, options, events);
1240
+ _logger().default.verbose({
1241
+ origin: '@atlaspack/core',
1242
+ message: 'Request track loaded from cache',
1243
+ meta: {
1244
+ ...commonMeta,
1245
+ trackableEvent: 'request_tracker_cache_key_hit',
1246
+ invalidationStats
1247
+ }
1248
+ });
1249
+ } else {
1250
+ requestGraph.invalidateUnpredictableNodes();
1251
+ requestGraph.invalidateOnBuildNodes();
1252
+ requestGraph.invalidateEnvNodes(options.env);
1253
+ requestGraph.invalidateOptionNodes(options);
1254
+ }
1159
1255
  return requestGraph;
1160
1256
  } catch (e) {
1161
1257
  // Prevent logging fs events took too long warning
@@ -1170,12 +1266,106 @@ async function loadRequestGraph(options) {
1170
1266
  origin: '@atlaspack/core',
1171
1267
  message: 'Cache entry for request tracker was not found, initializing a clean cache.',
1172
1268
  meta: {
1173
- cacheKey,
1174
- snapshotKey
1269
+ ...commonMeta,
1270
+ trackableEvent: 'request_tracker_cache_key_miss'
1175
1271
  }
1176
1272
  });
1177
1273
  return new RequestGraph();
1178
1274
  }
1275
+
1276
+ /**
1277
+ * A wrapper around an invalidation type / method
1278
+ */
1279
+
1280
+ /**
1281
+ * Details about an invalidation.
1282
+ *
1283
+ * If this is a fs events invalidation, this key will contain statistics about invalidations
1284
+ * by path.
1285
+ *
1286
+ * If this is a env or option invalidation, this key will contain the list of changed environment
1287
+ * variables or options.
1288
+ */
1289
+
1290
+ /**
1291
+ * Number of invalidations for a given file-system event.
1292
+ */
1293
+
1294
+ /**
1295
+ * Information about a certain cache invalidation type.
1296
+ */
1297
+
1298
+ /**
1299
+ * Respond to unpredictable, build, environment changes, option changes and file-system events
1300
+ * invalidating RequestGraph nodes.
1301
+ *
1302
+ * Returns the count of nodes invalidated by each invalidation type.
1303
+ */
1304
+ async function invalidateRequestGraph(requestGraph, options, events) {
1305
+ const invalidationFns = [{
1306
+ key: 'unpredictable',
1307
+ fn: () => requestGraph.invalidateUnpredictableNodes()
1308
+ }, {
1309
+ key: 'onBuild',
1310
+ fn: () => requestGraph.invalidateOnBuildNodes()
1311
+ }, {
1312
+ key: 'env',
1313
+ fn: () => requestGraph.invalidateEnvNodes(options.env)
1314
+ }, {
1315
+ key: 'option',
1316
+ fn: () => requestGraph.invalidateOptionNodes(options)
1317
+ }, {
1318
+ key: 'fsEvents',
1319
+ fn: () => invalidateRequestGraphFSEvents(requestGraph, options, events)
1320
+ }];
1321
+ const invalidations = [];
1322
+ for (const invalidation of invalidationFns) {
1323
+ invalidations.push(await runInvalidation(requestGraph, invalidation));
1324
+ }
1325
+ const invalidatedCount = invalidations.reduce((acc, invalidation) => acc + invalidation.count, 0);
1326
+ const requestCount = requestGraph.nodes.reduce((acc, node) => acc + ((node === null || node === void 0 ? void 0 : node.type) === REQUEST ? 1 : 0), 0);
1327
+ const nodeCount = requestGraph.nodes.length;
1328
+ const nodeInvalidationRatio = invalidatedCount / nodeCount;
1329
+ const requestInvalidationRatio = invalidatedCount / requestCount;
1330
+ return {
1331
+ invalidations,
1332
+ nodeCount,
1333
+ requestCount,
1334
+ invalidatedCount,
1335
+ nodeInvalidationRatio,
1336
+ requestInvalidationRatio
1337
+ };
1338
+ }
1339
+ /**
1340
+ * Invalidate the request graph based on file-system events.
1341
+ *
1342
+ * Returns statistics about the invalidations.
1343
+ */
1344
+ async function invalidateRequestGraphFSEvents(requestGraph, options, events) {
1345
+ const {
1346
+ invalidationsByPath
1347
+ } = await requestGraph.respondToFSEvents(options.unstableFileInvalidations || events, options, 10000, true);
1348
+ const biggestInvalidations = getBiggestFSEventsInvalidations(invalidationsByPath);
1349
+ return {
1350
+ biggestInvalidations
1351
+ };
1352
+ }
1353
+ /**
1354
+ * Runs an invalidation function and reports metrics.
1355
+ */
1356
+ async function runInvalidation(requestGraph, invalidationFn) {
1357
+ const start = _perf_hooks().performance.now();
1358
+ const startInvalidationCount = requestGraph.getInvalidNodeCount();
1359
+ const result = await invalidationFn.fn();
1360
+ const count = requestGraph.getInvalidNodeCount() - startInvalidationCount;
1361
+ const duration = _perf_hooks().performance.now() - start;
1362
+ return {
1363
+ key: invalidationFn.key,
1364
+ count,
1365
+ detail: result ?? null,
1366
+ duration
1367
+ };
1368
+ }
1179
1369
  function logErrorOnBailout(options, snapshotPath, e) {
1180
1370
  if (e.message && e.message.includes('invalid clockspec')) {
1181
1371
  const snapshotContents = options.inputFS.readFileSync(snapshotPath, 'utf-8');
@@ -1215,4 +1405,19 @@ function cleanUpOrphans(graph) {
1215
1405
  }
1216
1406
  });
1217
1407
  return removedNodeIds;
1408
+ }
1409
+
1410
+ /**
1411
+ * Returns paths that invalidated the most nodes
1412
+ */
1413
+ function getBiggestFSEventsInvalidations(invalidationsByPath, limit = 10) {
1414
+ const invalidations = [];
1415
+ for (const [path, count] of invalidationsByPath) {
1416
+ invalidations.push({
1417
+ path,
1418
+ count
1419
+ });
1420
+ }
1421
+ invalidations.sort((a, b) => b.count - a.count);
1422
+ return invalidations.slice(0, limit);
1218
1423
  }
@@ -173,12 +173,12 @@ class Transformation {
173
173
  });
174
174
 
175
175
  // Prefer `isSource` originating from the AssetRequest.
176
-
176
+ let isSource = isSourceOverride ?? summarizedIsSource;
177
177
  return new _UncommittedAsset.default({
178
178
  value: (0, _assetUtils.createAsset)(this.options.projectRoot, {
179
179
  code,
180
180
  filePath,
181
- isSource: isSourceOverride ?? summarizedIsSource,
181
+ isSource,
182
182
  type: _path().default.extname((0, _projectPath.fromProjectPathRelative)(filePath)).slice(1),
183
183
  pipeline,
184
184
  env,