@atlaspack/core 2.12.1-canary.3430 → 2.12.1-canary.3432

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/Atlaspack.js CHANGED
@@ -520,6 +520,9 @@ class Atlaspack {
520
520
  });
521
521
  return this.#farm.takeHeapSnapshot();
522
522
  }
523
+ async unstable_invalidate() {
524
+ await this._init();
525
+ }
523
526
  async unstable_transform(options) {
524
527
  var _options$env;
525
528
  if (!this.#initialized) {
@@ -465,18 +465,32 @@ class RequestGraph extends _graph().ContentGraph {
465
465
  return node;
466
466
  });
467
467
  }
468
- invalidateFileNameNode(node, filePath, matchNodes) {
468
+ invalidateFileNameNode(node, filePath, matchNodes, invalidateNode) {
469
469
  // If there is an edge between this file_name node and one of the original file nodes pointed to
470
470
  // by the original file_name node, and the matched node is inside the current directory, invalidate
471
471
  // all connected requests pointed to by the file node.
472
- let dirname = _path2().default.dirname((0, _projectPath.fromProjectPathRelative)(filePath));
472
+
473
473
  let nodeId = this.getNodeIdByContentKey(node.id);
474
- for (let matchNode of matchNodes) {
475
- let matchNodeId = this.getNodeIdByContentKey(matchNode.id);
476
- if (this.hasEdge(nodeId, matchNodeId, requestGraphEdgeTypes.invalidated_by_create_above) && (0, _utils().isDirectoryInside)((0, _projectPath.fromProjectPathRelative)((0, _projectPath.toProjectPathUnsafe)(matchNode.id)), dirname)) {
477
- let connectedNodes = this.getNodeIdsConnectedTo(matchNodeId, requestGraphEdgeTypes.invalidated_by_create);
474
+ let dirname = _path2().default.dirname((0, _projectPath.fromProjectPathRelative)(filePath));
475
+ if ((0, _featureFlags().getFeatureFlag)('fixQuadraticCacheInvalidation')) {
476
+ while (dirname !== '/') {
477
+ if (!this.hasContentKey(dirname)) break;
478
+ const matchNodeId = this.getNodeIdByContentKey(dirname);
479
+ if (!this.hasEdge(nodeId, matchNodeId, requestGraphEdgeTypes.invalidated_by_create_above)) break;
480
+ const connectedNodes = this.getNodeIdsConnectedTo(matchNodeId, requestGraphEdgeTypes.invalidated_by_create);
478
481
  for (let connectedNode of connectedNodes) {
479
- this.invalidateNode(connectedNode, _constants.FILE_CREATE);
482
+ invalidateNode(connectedNode, _constants.FILE_CREATE);
483
+ }
484
+ dirname = _path2().default.dirname(dirname);
485
+ }
486
+ } else {
487
+ for (let matchNode of matchNodes) {
488
+ let matchNodeId = this.getNodeIdByContentKey(matchNode.id);
489
+ if (this.hasEdge(nodeId, matchNodeId, requestGraphEdgeTypes.invalidated_by_create_above) && (0, _utils().isDirectoryInside)((0, _projectPath.fromProjectPathRelative)((0, _projectPath.toProjectPathUnsafe)(matchNode.id)), dirname)) {
490
+ let connectedNodes = this.getNodeIdsConnectedTo(matchNodeId, requestGraphEdgeTypes.invalidated_by_create);
491
+ for (let connectedNode of connectedNodes) {
492
+ this.invalidateNode(connectedNode, _constants.FILE_CREATE);
493
+ }
480
494
  }
481
495
  }
482
496
  }
@@ -489,7 +503,7 @@ class RequestGraph extends _graph().ContentGraph {
489
503
  if (this.hasEdge(nodeId, this.getNodeIdByContentKey(contentKey), requestGraphEdgeTypes.dirname)) {
490
504
  let parent = (0, _nullthrows().default)(this.getNodeByContentKey(contentKey));
491
505
  (0, _assert().default)(parent.type === FILE_NAME);
492
- this.invalidateFileNameNode(parent, (0, _projectPath.toProjectPathUnsafe)(dirname), matchNodes);
506
+ this.invalidateFileNameNode(parent, (0, _projectPath.toProjectPathUnsafe)(dirname), matchNodes, invalidateNode);
493
507
  }
494
508
  }
495
509
  }
@@ -498,12 +512,38 @@ class RequestGraph extends _graph().ContentGraph {
498
512
  let count = 0;
499
513
  let predictedTime = 0;
500
514
  let startTime = Date.now();
501
- const removeOrphans = !(0, _featureFlags().getFeatureFlag)('fixQuadraticCacheInvalidation');
515
+ const enableOptimization = (0, _featureFlags().getFeatureFlag)('fixQuadraticCacheInvalidation');
516
+ const removeOrphans = !enableOptimization;
517
+ const invalidatedNodes = new Set();
518
+ const invalidateNode = (nodeId, reason) => {
519
+ if (enableOptimization && invalidatedNodes.has(nodeId)) {
520
+ return;
521
+ }
522
+ invalidatedNodes.add(nodeId);
523
+ this.invalidateNode(nodeId, reason);
524
+ };
525
+ const aboveCache = new Map();
526
+ const getAbove = fileNameNodeId => {
527
+ const cachedResult = aboveCache.get(fileNameNodeId);
528
+ if (enableOptimization && cachedResult) {
529
+ return cachedResult;
530
+ }
531
+ let above = [];
532
+ const children = this.getNodeIdsConnectedTo(fileNameNodeId, requestGraphEdgeTypes.invalidated_by_create_above);
533
+ for (const nodeId of children) {
534
+ let node = (0, _nullthrows().default)(this.getNode(nodeId));
535
+ if (node.type === FILE) {
536
+ above.push(node);
537
+ }
538
+ }
539
+ aboveCache.set(fileNameNodeId, above);
540
+ return above;
541
+ };
502
542
  for (let {
503
543
  path: _path,
504
544
  type
505
545
  } of events) {
506
- if (++count === 256) {
546
+ if (!enableOptimization && process.env.ATLASPACK_DISABLE_CACHE_TIMEOUT !== 'true' && ++count === 256) {
507
547
  let duration = Date.now() - startTime;
508
548
  predictedTime = duration * (events.length >> 8);
509
549
  if (predictedTime > threshold) {
@@ -550,13 +590,13 @@ class RequestGraph extends _graph().ContentGraph {
550
590
  let nodes = this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_update);
551
591
  for (let connectedNode of nodes) {
552
592
  didInvalidate = true;
553
- this.invalidateNode(connectedNode, _constants.FILE_UPDATE);
593
+ invalidateNode(connectedNode, _constants.FILE_UPDATE);
554
594
  }
555
595
  if (type === 'create') {
556
596
  let nodes = this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_create);
557
597
  for (let connectedNode of nodes) {
558
598
  didInvalidate = true;
559
- this.invalidateNode(connectedNode, _constants.FILE_CREATE);
599
+ invalidateNode(connectedNode, _constants.FILE_CREATE);
560
600
  }
561
601
  }
562
602
  } else if (type === 'create') {
@@ -566,17 +606,10 @@ class RequestGraph extends _graph().ContentGraph {
566
606
  let fileNameNodeId = this.getNodeIdByContentKey('file_name:' + basename);
567
607
 
568
608
  // Find potential file nodes to be invalidated if this file name pattern matches
569
- let above = [];
570
- for (const nodeId of this.getNodeIdsConnectedTo(fileNameNodeId, requestGraphEdgeTypes.invalidated_by_create_above)) {
571
- let node = (0, _nullthrows().default)(this.getNode(nodeId));
572
- // these might also be `glob` nodes which get handled below, we only care about files here.
573
- if (node.type === FILE) {
574
- above.push(node);
575
- }
576
- }
609
+ let above = getAbove(fileNameNodeId);
577
610
  if (above.length > 0) {
578
611
  didInvalidate = true;
579
- this.invalidateFileNameNode(fileNameNode, _filePath, above);
612
+ this.invalidateFileNameNode(fileNameNode, _filePath, above, invalidateNode);
580
613
  }
581
614
  }
582
615
  for (let globeNodeId of this.globNodeIds) {
@@ -586,7 +619,7 @@ class RequestGraph extends _graph().ContentGraph {
586
619
  let connectedNodes = this.getNodeIdsConnectedTo(globeNodeId, requestGraphEdgeTypes.invalidated_by_create);
587
620
  for (let connectedNode of connectedNodes) {
588
621
  didInvalidate = true;
589
- this.invalidateNode(connectedNode, _constants.FILE_CREATE);
622
+ invalidateNode(connectedNode, _constants.FILE_CREATE);
590
623
  }
591
624
  }
592
625
  }
@@ -594,7 +627,7 @@ class RequestGraph extends _graph().ContentGraph {
594
627
  let nodeId = this.getNodeIdByContentKey(filePath);
595
628
  for (let connectedNode of this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_delete)) {
596
629
  didInvalidate = true;
597
- this.invalidateNode(connectedNode, _constants.FILE_DELETE);
630
+ invalidateNode(connectedNode, _constants.FILE_DELETE);
598
631
  }
599
632
 
600
633
  // Delete the file node since it doesn't exist anymore.
@@ -614,7 +647,7 @@ class RequestGraph extends _graph().ContentGraph {
614
647
  }
615
648
  if (isInvalid) {
616
649
  for (let connectedNode of this.getNodeIdsConnectedTo(nodeId, requestGraphEdgeTypes.invalidated_by_update)) {
617
- this.invalidateNode(connectedNode, type === 'delete' ? _constants.FILE_DELETE : _constants.FILE_UPDATE);
650
+ invalidateNode(connectedNode, type === 'delete' ? _constants.FILE_DELETE : _constants.FILE_UPDATE);
618
651
  }
619
652
  didInvalidate = true;
620
653
  this.removeNode(nodeId, removeOrphans);
@@ -632,7 +665,9 @@ class RequestGraph extends _graph().ContentGraph {
632
665
  meta: {
633
666
  trackableEvent: 'fsevent_response_time',
634
667
  duration,
635
- predictedTime
668
+ predictedTime,
669
+ numberOfEvents: events.length,
670
+ numberOfInvalidatedNodes: invalidatedNodes.size
636
671
  }
637
672
  });
638
673
  return didInvalidate && this.invalidNodeIds.size > 0;
@@ -1035,7 +1070,16 @@ async function loadRequestGraph(options) {
1035
1070
  let cacheKey = getCacheKey(options);
1036
1071
  let requestGraphKey = `requestGraph-${cacheKey}`;
1037
1072
  let timeout;
1038
- const snapshotPath = _path2().default.join(options.cacheDir, `snapshot-${cacheKey}` + '.txt');
1073
+ const snapshotKey = `snapshot-${cacheKey}`;
1074
+ const snapshotPath = _path2().default.join(options.cacheDir, snapshotKey + '.txt');
1075
+ _logger().default.verbose({
1076
+ origin: '@atlaspack/core',
1077
+ message: 'Loading request graph',
1078
+ meta: {
1079
+ cacheKey,
1080
+ snapshotKey
1081
+ }
1082
+ });
1039
1083
  if (await options.cache.hasLargeBlob(requestGraphKey)) {
1040
1084
  try {
1041
1085
  let {
@@ -1075,6 +1119,14 @@ async function loadRequestGraph(options) {
1075
1119
  return new RequestGraph();
1076
1120
  }
1077
1121
  }
1122
+ _logger().default.verbose({
1123
+ origin: '@atlaspack/core',
1124
+ message: 'Cache entry for request tracker was not found, initializing a clean cache.',
1125
+ meta: {
1126
+ cacheKey,
1127
+ snapshotKey
1128
+ }
1129
+ });
1078
1130
  return new RequestGraph();
1079
1131
  }
1080
1132
  function logErrorOnBailout(options, snapshotPath, e) {
@@ -1101,6 +1153,9 @@ function logErrorOnBailout(options, snapshotPath, e) {
1101
1153
  }
1102
1154
  }
1103
1155
  function cleanUpOrphans(graph) {
1156
+ if (graph.rootNodeId == null) {
1157
+ return [];
1158
+ }
1104
1159
  const reachableNodes = new Set();
1105
1160
  graph.traverse(nodeId => {
1106
1161
  reachableNodes.add(nodeId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaspack/core",
3
- "version": "2.12.1-canary.3430+7ad3bbfcb",
3
+ "version": "2.12.1-canary.3432+415929ab6",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -20,20 +20,20 @@
20
20
  "check-ts": "tsc --noEmit index.d.ts"
21
21
  },
22
22
  "dependencies": {
23
- "@atlaspack/cache": "2.12.1-canary.3430+7ad3bbfcb",
24
- "@atlaspack/diagnostic": "2.12.1-canary.3430+7ad3bbfcb",
25
- "@atlaspack/events": "2.12.1-canary.3430+7ad3bbfcb",
26
- "@atlaspack/feature-flags": "2.12.1-canary.3430+7ad3bbfcb",
27
- "@atlaspack/fs": "2.12.1-canary.3430+7ad3bbfcb",
28
- "@atlaspack/graph": "3.2.1-canary.3430+7ad3bbfcb",
29
- "@atlaspack/logger": "2.12.1-canary.3430+7ad3bbfcb",
30
- "@atlaspack/package-manager": "2.12.1-canary.3430+7ad3bbfcb",
31
- "@atlaspack/plugin": "2.12.1-canary.3430+7ad3bbfcb",
32
- "@atlaspack/profiler": "2.12.1-canary.3430+7ad3bbfcb",
33
- "@atlaspack/rust": "2.12.1-canary.3430+7ad3bbfcb",
34
- "@atlaspack/types": "2.12.1-canary.3430+7ad3bbfcb",
35
- "@atlaspack/utils": "2.12.1-canary.3430+7ad3bbfcb",
36
- "@atlaspack/workers": "2.12.1-canary.3430+7ad3bbfcb",
23
+ "@atlaspack/cache": "2.12.1-canary.3432+415929ab6",
24
+ "@atlaspack/diagnostic": "2.12.1-canary.3432+415929ab6",
25
+ "@atlaspack/events": "2.12.1-canary.3432+415929ab6",
26
+ "@atlaspack/feature-flags": "2.12.1-canary.3432+415929ab6",
27
+ "@atlaspack/fs": "2.12.1-canary.3432+415929ab6",
28
+ "@atlaspack/graph": "3.2.1-canary.3432+415929ab6",
29
+ "@atlaspack/logger": "2.12.1-canary.3432+415929ab6",
30
+ "@atlaspack/package-manager": "2.12.1-canary.3432+415929ab6",
31
+ "@atlaspack/plugin": "2.12.1-canary.3432+415929ab6",
32
+ "@atlaspack/profiler": "2.12.1-canary.3432+415929ab6",
33
+ "@atlaspack/rust": "2.12.1-canary.3432+415929ab6",
34
+ "@atlaspack/types": "2.12.1-canary.3432+415929ab6",
35
+ "@atlaspack/utils": "2.12.1-canary.3432+415929ab6",
36
+ "@atlaspack/workers": "2.12.1-canary.3432+415929ab6",
37
37
  "@mischnic/json-sourcemap": "^0.1.0",
38
38
  "@parcel/source-map": "^2.1.1",
39
39
  "base-x": "^3.0.8",
@@ -57,5 +57,5 @@
57
57
  "browser": {
58
58
  "./src/serializerCore.js": "./src/serializerCore.browser.js"
59
59
  },
60
- "gitHead": "7ad3bbfcbddc6764dfdc9c4660341b003911cc07"
60
+ "gitHead": "415929ab63f5314913c27d3dfdb965f2e76bedf2"
61
61
  }
package/src/Atlaspack.js CHANGED
@@ -544,6 +544,10 @@ export default class Atlaspack {
544
544
  return this.#farm.takeHeapSnapshot();
545
545
  }
546
546
 
547
+ async unstable_invalidate(): Promise<void> {
548
+ await this._init();
549
+ }
550
+
547
551
  async unstable_transform(
548
552
  options: AtlaspackTransformOptions,
549
553
  ): Promise<Array<Asset>> {
@@ -818,32 +818,59 @@ export class RequestGraph extends ContentGraph<
818
818
  node: FileNameNode,
819
819
  filePath: ProjectPath,
820
820
  matchNodes: Array<FileNode>,
821
+ invalidateNode: (NodeId, InvalidateReason) => void,
821
822
  ) {
822
823
  // If there is an edge between this file_name node and one of the original file nodes pointed to
823
824
  // by the original file_name node, and the matched node is inside the current directory, invalidate
824
825
  // all connected requests pointed to by the file node.
825
- let dirname = path.dirname(fromProjectPathRelative(filePath));
826
826
 
827
827
  let nodeId = this.getNodeIdByContentKey(node.id);
828
- for (let matchNode of matchNodes) {
829
- let matchNodeId = this.getNodeIdByContentKey(matchNode.id);
830
- if (
831
- this.hasEdge(
832
- nodeId,
833
- matchNodeId,
834
- requestGraphEdgeTypes.invalidated_by_create_above,
835
- ) &&
836
- isDirectoryInside(
837
- fromProjectPathRelative(toProjectPathUnsafe(matchNode.id)),
838
- dirname,
828
+ let dirname = path.dirname(fromProjectPathRelative(filePath));
829
+
830
+ if (getFeatureFlag('fixQuadraticCacheInvalidation')) {
831
+ while (dirname !== '/') {
832
+ if (!this.hasContentKey(dirname)) break;
833
+ const matchNodeId = this.getNodeIdByContentKey(dirname);
834
+ if (
835
+ !this.hasEdge(
836
+ nodeId,
837
+ matchNodeId,
838
+ requestGraphEdgeTypes.invalidated_by_create_above,
839
+ )
839
840
  )
840
- ) {
841
- let connectedNodes = this.getNodeIdsConnectedTo(
841
+ break;
842
+
843
+ const connectedNodes = this.getNodeIdsConnectedTo(
842
844
  matchNodeId,
843
845
  requestGraphEdgeTypes.invalidated_by_create,
844
846
  );
845
847
  for (let connectedNode of connectedNodes) {
846
- this.invalidateNode(connectedNode, FILE_CREATE);
848
+ invalidateNode(connectedNode, FILE_CREATE);
849
+ }
850
+
851
+ dirname = path.dirname(dirname);
852
+ }
853
+ } else {
854
+ for (let matchNode of matchNodes) {
855
+ let matchNodeId = this.getNodeIdByContentKey(matchNode.id);
856
+ if (
857
+ this.hasEdge(
858
+ nodeId,
859
+ matchNodeId,
860
+ requestGraphEdgeTypes.invalidated_by_create_above,
861
+ ) &&
862
+ isDirectoryInside(
863
+ fromProjectPathRelative(toProjectPathUnsafe(matchNode.id)),
864
+ dirname,
865
+ )
866
+ ) {
867
+ let connectedNodes = this.getNodeIdsConnectedTo(
868
+ matchNodeId,
869
+ requestGraphEdgeTypes.invalidated_by_create,
870
+ );
871
+ for (let connectedNode of connectedNodes) {
872
+ this.invalidateNode(connectedNode, FILE_CREATE);
873
+ }
847
874
  }
848
875
  }
849
876
  }
@@ -866,6 +893,7 @@ export class RequestGraph extends ContentGraph<
866
893
  parent,
867
894
  toProjectPathUnsafe(dirname),
868
895
  matchNodes,
896
+ invalidateNode,
869
897
  );
870
898
  }
871
899
  }
@@ -880,10 +908,45 @@ export class RequestGraph extends ContentGraph<
880
908
  let count = 0;
881
909
  let predictedTime = 0;
882
910
  let startTime = Date.now();
883
- const removeOrphans = !getFeatureFlag('fixQuadraticCacheInvalidation');
911
+ const enableOptimization = getFeatureFlag('fixQuadraticCacheInvalidation');
912
+ const removeOrphans = !enableOptimization;
913
+
914
+ const invalidatedNodes = new Set();
915
+ const invalidateNode = (nodeId, reason) => {
916
+ if (enableOptimization && invalidatedNodes.has(nodeId)) {
917
+ return;
918
+ }
919
+ invalidatedNodes.add(nodeId);
920
+ this.invalidateNode(nodeId, reason);
921
+ };
922
+ const aboveCache = new Map();
923
+ const getAbove = fileNameNodeId => {
924
+ const cachedResult = aboveCache.get(fileNameNodeId);
925
+ if (enableOptimization && cachedResult) {
926
+ return cachedResult;
927
+ }
928
+
929
+ let above = [];
930
+ const children = this.getNodeIdsConnectedTo(
931
+ fileNameNodeId,
932
+ requestGraphEdgeTypes.invalidated_by_create_above,
933
+ );
934
+ for (const nodeId of children) {
935
+ let node = nullthrows(this.getNode(nodeId));
936
+ if (node.type === FILE) {
937
+ above.push(node);
938
+ }
939
+ }
940
+ aboveCache.set(fileNameNodeId, above);
941
+ return above;
942
+ };
884
943
 
885
944
  for (let {path: _path, type} of events) {
886
- if (++count === 256) {
945
+ if (
946
+ !enableOptimization &&
947
+ process.env.ATLASPACK_DISABLE_CACHE_TIMEOUT !== 'true' &&
948
+ ++count === 256
949
+ ) {
887
950
  let duration = Date.now() - startTime;
888
951
  predictedTime = duration * (events.length >> 8);
889
952
  if (predictedTime > threshold) {
@@ -939,7 +1002,7 @@ export class RequestGraph extends ContentGraph<
939
1002
 
940
1003
  for (let connectedNode of nodes) {
941
1004
  didInvalidate = true;
942
- this.invalidateNode(connectedNode, FILE_UPDATE);
1005
+ invalidateNode(connectedNode, FILE_UPDATE);
943
1006
  }
944
1007
 
945
1008
  if (type === 'create') {
@@ -949,7 +1012,7 @@ export class RequestGraph extends ContentGraph<
949
1012
  );
950
1013
  for (let connectedNode of nodes) {
951
1014
  didInvalidate = true;
952
- this.invalidateNode(connectedNode, FILE_CREATE);
1015
+ invalidateNode(connectedNode, FILE_CREATE);
953
1016
  }
954
1017
  }
955
1018
  } else if (type === 'create') {
@@ -961,21 +1024,15 @@ export class RequestGraph extends ContentGraph<
961
1024
  );
962
1025
 
963
1026
  // Find potential file nodes to be invalidated if this file name pattern matches
964
- let above: Array<FileNode> = [];
965
- for (const nodeId of this.getNodeIdsConnectedTo(
966
- fileNameNodeId,
967
- requestGraphEdgeTypes.invalidated_by_create_above,
968
- )) {
969
- let node = nullthrows(this.getNode(nodeId));
970
- // these might also be `glob` nodes which get handled below, we only care about files here.
971
- if (node.type === FILE) {
972
- above.push(node);
973
- }
974
- }
975
-
1027
+ let above: Array<FileNode> = getAbove(fileNameNodeId);
976
1028
  if (above.length > 0) {
977
1029
  didInvalidate = true;
978
- this.invalidateFileNameNode(fileNameNode, _filePath, above);
1030
+ this.invalidateFileNameNode(
1031
+ fileNameNode,
1032
+ _filePath,
1033
+ above,
1034
+ invalidateNode,
1035
+ );
979
1036
  }
980
1037
  }
981
1038
 
@@ -990,7 +1047,7 @@ export class RequestGraph extends ContentGraph<
990
1047
  );
991
1048
  for (let connectedNode of connectedNodes) {
992
1049
  didInvalidate = true;
993
- this.invalidateNode(connectedNode, FILE_CREATE);
1050
+ invalidateNode(connectedNode, FILE_CREATE);
994
1051
  }
995
1052
  }
996
1053
  }
@@ -1001,7 +1058,7 @@ export class RequestGraph extends ContentGraph<
1001
1058
  requestGraphEdgeTypes.invalidated_by_delete,
1002
1059
  )) {
1003
1060
  didInvalidate = true;
1004
- this.invalidateNode(connectedNode, FILE_DELETE);
1061
+ invalidateNode(connectedNode, FILE_DELETE);
1005
1062
  }
1006
1063
 
1007
1064
  // Delete the file node since it doesn't exist anymore.
@@ -1033,7 +1090,7 @@ export class RequestGraph extends ContentGraph<
1033
1090
  nodeId,
1034
1091
  requestGraphEdgeTypes.invalidated_by_update,
1035
1092
  )) {
1036
- this.invalidateNode(
1093
+ invalidateNode(
1037
1094
  connectedNode,
1038
1095
  type === 'delete' ? FILE_DELETE : FILE_UPDATE,
1039
1096
  );
@@ -1057,6 +1114,8 @@ export class RequestGraph extends ContentGraph<
1057
1114
  trackableEvent: 'fsevent_response_time',
1058
1115
  duration,
1059
1116
  predictedTime,
1117
+ numberOfEvents: events.length,
1118
+ numberOfInvalidatedNodes: invalidatedNodes.size,
1060
1119
  },
1061
1120
  });
1062
1121
 
@@ -1615,6 +1674,15 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1615
1674
  let timeout;
1616
1675
  const snapshotKey = `snapshot-${cacheKey}`;
1617
1676
  const snapshotPath = path.join(options.cacheDir, snapshotKey + '.txt');
1677
+
1678
+ logger.verbose({
1679
+ origin: '@atlaspack/core',
1680
+ message: 'Loading request graph',
1681
+ meta: {
1682
+ cacheKey,
1683
+ snapshotKey,
1684
+ },
1685
+ });
1618
1686
  if (await options.cache.hasLargeBlob(requestGraphKey)) {
1619
1687
  try {
1620
1688
  let {requestGraph} = await readAndDeserializeRequestGraph(
@@ -1670,6 +1738,15 @@ async function loadRequestGraph(options): Async<RequestGraph> {
1670
1738
  }
1671
1739
  }
1672
1740
 
1741
+ logger.verbose({
1742
+ origin: '@atlaspack/core',
1743
+ message:
1744
+ 'Cache entry for request tracker was not found, initializing a clean cache.',
1745
+ meta: {
1746
+ cacheKey,
1747
+ snapshotKey,
1748
+ },
1749
+ });
1673
1750
  return new RequestGraph();
1674
1751
  }
1675
1752
 
@@ -1705,6 +1782,10 @@ function logErrorOnBailout(
1705
1782
  }
1706
1783
 
1707
1784
  export function cleanUpOrphans<N, E: number>(graph: Graph<N, E>): NodeId[] {
1785
+ if (graph.rootNodeId == null) {
1786
+ return [];
1787
+ }
1788
+
1708
1789
  const reachableNodes = new Set();
1709
1790
  graph.traverse(nodeId => {
1710
1791
  reachableNodes.add(nodeId);
@@ -9,8 +9,10 @@ import RequestTracker, {
9
9
  import {Graph} from '@atlaspack/graph';
10
10
  import WorkerFarm from '@atlaspack/workers';
11
11
  import {DEFAULT_OPTIONS} from './test-utils';
12
- import {INITIAL_BUILD} from '../src/constants';
12
+ import {FILE_CREATE, FILE_UPDATE, INITIAL_BUILD} from '../src/constants';
13
13
  import {makeDeferredWithPromise} from '@atlaspack/utils';
14
+ import {toProjectPath} from '../src/projectPath';
15
+ import {DEFAULT_FEATURE_FLAGS, setFeatureFlags} from '../../feature-flags/src';
14
16
 
15
17
  const options = DEFAULT_OPTIONS;
16
18
  const farm = new WorkerFarm({workerPath: require.resolve('../src/worker.js')});
@@ -449,6 +451,97 @@ describe('RequestTracker', () => {
449
451
 
450
452
  assert.equal(tracker.graph.getNode(nodeId)?.invalidateReason, 1);
451
453
  });
454
+
455
+ describe('respondToFSEvents', () => {
456
+ [true, false].forEach(value => {
457
+ beforeEach(() => {
458
+ setFeatureFlags({
459
+ ...DEFAULT_FEATURE_FLAGS,
460
+ fixQuadraticCacheInvalidation: value === true ? 'NEW' : 'OLD',
461
+ });
462
+ });
463
+
464
+ afterEach(() => {
465
+ setFeatureFlags({
466
+ ...DEFAULT_FEATURE_FLAGS,
467
+ });
468
+ });
469
+
470
+ describe(`optimizations feature-flag ${String(value)}`, () => {
471
+ it('should invalidate file requests on file changes', async () => {
472
+ let tracker = new RequestTracker({farm, options});
473
+ await tracker.runRequest({
474
+ id: 'abc',
475
+ type: 7,
476
+ // $FlowFixMe string isn't a valid result
477
+ run: async ({api}: {api: RunAPI<string | void>, ...}) => {
478
+ api.invalidateOnFileUpdate(toProjectPath('', 'my-file'));
479
+ let result = await Promise.resolve('hello');
480
+ api.storeResult(result);
481
+ },
482
+ input: null,
483
+ });
484
+ const requestId = tracker.graph.getNodeIdByContentKey('abc');
485
+ const invalidated = await tracker.respondToFSEvents(
486
+ [
487
+ {
488
+ type: 'update',
489
+ path: 'my-file',
490
+ },
491
+ ],
492
+ Number.MAX_VALUE,
493
+ );
494
+ assert.equal(invalidated, true);
495
+ assert.equal(
496
+ tracker.graph.getNode(requestId)?.invalidateReason,
497
+ FILE_UPDATE,
498
+ );
499
+ assert.deepEqual(Array.from(tracker.graph.invalidNodeIds), [
500
+ requestId,
501
+ ]);
502
+ });
503
+
504
+ it('should invalidate file name requests with invalidate above invalidations', async () => {
505
+ let tracker = new RequestTracker({farm, options});
506
+ await tracker.runRequest({
507
+ id: 'abc',
508
+ type: 7,
509
+ // $FlowFixMe string isn't a valid result
510
+ run: async ({api}: {api: RunAPI<string | void>, ...}) => {
511
+ api.invalidateOnFileCreate({
512
+ fileName: 'package.json',
513
+ aboveFilePath: toProjectPath(
514
+ '',
515
+ './node_modules/something/package.json',
516
+ ),
517
+ });
518
+ let result = await Promise.resolve('hello');
519
+ api.storeResult(result);
520
+ },
521
+ input: null,
522
+ });
523
+ const requestId = tracker.graph.getNodeIdByContentKey('abc');
524
+ const invalidated = await tracker.respondToFSEvents(
525
+ [
526
+ {
527
+ type: 'create',
528
+ path: './package.json',
529
+ },
530
+ ],
531
+ Number.MAX_VALUE,
532
+ );
533
+ assert.equal(invalidated, true);
534
+ assert.equal(
535
+ tracker.graph.getNode(requestId)?.invalidateReason,
536
+ FILE_CREATE,
537
+ );
538
+ assert.deepEqual(Array.from(tracker.graph.invalidNodeIds), [
539
+ requestId,
540
+ ]);
541
+ });
542
+ });
543
+ });
544
+ });
452
545
  });
453
546
 
454
547
  describe('cleanUpOrphans', () => {