@atlaspack/core 2.12.1-canary.3431 → 2.12.1-canary.3434
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 +12 -2
- package/lib/BundleGraph.js +59 -5
- package/lib/RequestTracker.js +81 -26
- package/lib/SymbolPropagation.js +1 -1
- package/lib/atlaspack-v3/worker/worker.js +2 -1
- package/lib/public/BundleGraph.js +68 -0
- package/lib/resolveOptions.js +2 -0
- package/lib/types.js +2 -1
- package/package.json +16 -16
- package/src/Atlaspack.js +15 -6
- package/src/BundleGraph.js +77 -1
- package/src/RequestTracker.js +116 -35
- package/src/SymbolPropagation.js +5 -2
- package/src/atlaspack-v3/worker/worker.js +1 -0
- package/src/public/BundleGraph.js +106 -0
- package/src/resolveOptions.js +2 -0
- package/src/types.js +10 -0
- package/test/PublicBundle.test.js +1 -0
- package/test/RequestTracker.test.js +94 -1
package/lib/Atlaspack.js
CHANGED
|
@@ -151,6 +151,11 @@ class Atlaspack {
|
|
|
151
151
|
if (this.#initialized) {
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
|
+
const featureFlags = {
|
|
155
|
+
..._featureFlags().DEFAULT_FEATURE_FLAGS,
|
|
156
|
+
...this.#initialOptions.featureFlags
|
|
157
|
+
};
|
|
158
|
+
(0, _featureFlags().setFeatureFlags)(featureFlags);
|
|
154
159
|
await _sourceMap().init;
|
|
155
160
|
await (_rust().init === null || _rust().init === void 0 ? void 0 : (0, _rust().init)());
|
|
156
161
|
try {
|
|
@@ -162,7 +167,10 @@ class Atlaspack {
|
|
|
162
167
|
// Fallthrough
|
|
163
168
|
_logger().default.warn(e);
|
|
164
169
|
}
|
|
165
|
-
let resolvedOptions = await (0, _resolveOptions.default)(
|
|
170
|
+
let resolvedOptions = await (0, _resolveOptions.default)({
|
|
171
|
+
...this.#initialOptions,
|
|
172
|
+
featureFlags
|
|
173
|
+
});
|
|
166
174
|
this.#resolvedOptions = resolvedOptions;
|
|
167
175
|
let rustAtlaspack;
|
|
168
176
|
if (resolvedOptions.featureFlags.atlaspackV3) {
|
|
@@ -192,7 +200,6 @@ class Atlaspack {
|
|
|
192
200
|
}
|
|
193
201
|
});
|
|
194
202
|
}
|
|
195
|
-
(0, _featureFlags().setFeatureFlags)(resolvedOptions.featureFlags);
|
|
196
203
|
let {
|
|
197
204
|
config
|
|
198
205
|
} = await (0, _AtlaspackConfigRequest.loadAtlaspackConfig)(resolvedOptions);
|
|
@@ -520,6 +527,9 @@ class Atlaspack {
|
|
|
520
527
|
});
|
|
521
528
|
return this.#farm.takeHeapSnapshot();
|
|
522
529
|
}
|
|
530
|
+
async unstable_invalidate() {
|
|
531
|
+
await this._init();
|
|
532
|
+
}
|
|
523
533
|
async unstable_transform(options) {
|
|
524
534
|
var _options$env;
|
|
525
535
|
if (!this.#initialized) {
|
package/lib/BundleGraph.js
CHANGED
|
@@ -44,6 +44,13 @@ var _utils2 = require("./utils");
|
|
|
44
44
|
var _Environment = require("./public/Environment");
|
|
45
45
|
var _projectPath = require("./projectPath");
|
|
46
46
|
var _constants = require("./constants");
|
|
47
|
+
function _featureFlags() {
|
|
48
|
+
const data = require("@atlaspack/feature-flags");
|
|
49
|
+
_featureFlags = function () {
|
|
50
|
+
return data;
|
|
51
|
+
};
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
47
54
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
48
55
|
const bundleGraphEdgeTypes = exports.bundleGraphEdgeTypes = {
|
|
49
56
|
// A lack of an edge type indicates to follow the edge while traversing
|
|
@@ -107,16 +114,19 @@ class BundleGraph {
|
|
|
107
114
|
/** The internal core Graph structure */
|
|
108
115
|
|
|
109
116
|
_bundlePublicIds /*: Set<string> */ = new Set();
|
|
117
|
+
_conditions /*: Map<string, Condition> */ = new Map();
|
|
110
118
|
constructor({
|
|
111
119
|
graph,
|
|
112
120
|
publicIdByAssetId,
|
|
113
121
|
assetPublicIds,
|
|
114
|
-
bundleContentHashes
|
|
122
|
+
bundleContentHashes,
|
|
123
|
+
conditions
|
|
115
124
|
}) {
|
|
116
125
|
this._graph = graph;
|
|
117
126
|
this._assetPublicIds = assetPublicIds;
|
|
118
127
|
this._publicIdByAssetId = publicIdByAssetId;
|
|
119
128
|
this._bundleContentHashes = bundleContentHashes;
|
|
129
|
+
this._conditions = conditions;
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
/**
|
|
@@ -128,6 +138,8 @@ class BundleGraph {
|
|
|
128
138
|
let assetGroupIds = new Map();
|
|
129
139
|
let dependencies = new Map();
|
|
130
140
|
let assetGraphNodeIdToBundleGraphNodeId = new Map();
|
|
141
|
+
let conditions = new Map();
|
|
142
|
+
let placeholderToDependency = new Map();
|
|
131
143
|
let assetGraphRootNode = assetGraph.rootNodeId != null ? assetGraph.getNode(assetGraph.rootNodeId) : null;
|
|
132
144
|
(0, _assert().default)(assetGraphRootNode != null && assetGraphRootNode.type === 'root');
|
|
133
145
|
assetGraph.dfsFast(nodeId => {
|
|
@@ -146,6 +158,17 @@ class BundleGraph {
|
|
|
146
158
|
}
|
|
147
159
|
} else if (node != null && node.type === 'asset_group') {
|
|
148
160
|
assetGroupIds.set(nodeId, assetGraph.getNodeIdsConnectedFrom(nodeId));
|
|
161
|
+
} else if ((0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node != null && node.type === 'dependency') {
|
|
162
|
+
var _dep$meta;
|
|
163
|
+
// The dependency placeholders in the `importCond` calls that will be in the transformed
|
|
164
|
+
// code need to be mapped to the "real" dependencies, so we need access to a map of placeholders
|
|
165
|
+
// to dependencies
|
|
166
|
+
const dep = node.value;
|
|
167
|
+
// $FlowFixMe[incompatible-type] Meta is untyped
|
|
168
|
+
const placeholder = (_dep$meta = dep.meta) === null || _dep$meta === void 0 ? void 0 : _dep$meta.placeholder;
|
|
169
|
+
if (placeholder != null) {
|
|
170
|
+
placeholderToDependency.set(placeholder, dep);
|
|
171
|
+
}
|
|
149
172
|
}
|
|
150
173
|
});
|
|
151
174
|
let walkVisited = new Set();
|
|
@@ -153,6 +176,34 @@ class BundleGraph {
|
|
|
153
176
|
if (walkVisited.has(nodeId)) return;
|
|
154
177
|
walkVisited.add(nodeId);
|
|
155
178
|
let node = (0, _nullthrows().default)(assetGraph.getNode(nodeId));
|
|
179
|
+
if ((0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.type === 'asset') {
|
|
180
|
+
const asset = node.value;
|
|
181
|
+
if (Array.isArray(asset.meta.conditions)) {
|
|
182
|
+
// $FlowFixMe
|
|
183
|
+
for (const condition of asset.meta.conditions) {
|
|
184
|
+
// Resolve the placeholders that were attached to the asset in JSTransformer to dependencies,
|
|
185
|
+
// as well as create a public id for the condition.
|
|
186
|
+
const {
|
|
187
|
+
key,
|
|
188
|
+
ifTruePlaceholder,
|
|
189
|
+
ifFalsePlaceholder
|
|
190
|
+
} = condition;
|
|
191
|
+
const condHash = (0, _rust().hashString)(`${key}:${ifTruePlaceholder}:${ifFalsePlaceholder}`);
|
|
192
|
+
const condPublicId = (0, _utils2.getPublicId)(condHash, v => conditions.has(v));
|
|
193
|
+
if (conditions.has(condHash)) {
|
|
194
|
+
throw new Error('Unexpected duplicate asset');
|
|
195
|
+
} else {
|
|
196
|
+
conditions.set(condHash, {
|
|
197
|
+
publicId: condPublicId,
|
|
198
|
+
assets: new Set([asset]),
|
|
199
|
+
key,
|
|
200
|
+
ifTrueDependency: (0, _nullthrows().default)(placeholderToDependency.get(ifTruePlaceholder), 'ifTruePlaceholder was undefined'),
|
|
201
|
+
ifFalseDependency: (0, _nullthrows().default)(placeholderToDependency.get(ifFalsePlaceholder), 'ifFalsePlaceholder was undefined')
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
156
207
|
if (node.type === 'dependency' && node.value.symbols != null && node.value.env.shouldScopeHoist &&
|
|
157
208
|
// Disable in dev mode because this feature is at odds with safeToIncrementallyBundle
|
|
158
209
|
isProduction) {
|
|
@@ -338,7 +389,8 @@ class BundleGraph {
|
|
|
338
389
|
graph,
|
|
339
390
|
assetPublicIds,
|
|
340
391
|
bundleContentHashes: new Map(),
|
|
341
|
-
publicIdByAssetId
|
|
392
|
+
publicIdByAssetId,
|
|
393
|
+
conditions
|
|
342
394
|
});
|
|
343
395
|
}
|
|
344
396
|
serialize() {
|
|
@@ -347,7 +399,8 @@ class BundleGraph {
|
|
|
347
399
|
graph: this._graph.serialize(),
|
|
348
400
|
assetPublicIds: this._assetPublicIds,
|
|
349
401
|
bundleContentHashes: this._bundleContentHashes,
|
|
350
|
-
publicIdByAssetId: this._publicIdByAssetId
|
|
402
|
+
publicIdByAssetId: this._publicIdByAssetId,
|
|
403
|
+
conditions: this._conditions
|
|
351
404
|
};
|
|
352
405
|
}
|
|
353
406
|
static deserialize(serialized) {
|
|
@@ -355,7 +408,8 @@ class BundleGraph {
|
|
|
355
408
|
graph: _graph().ContentGraph.deserialize(serialized.graph),
|
|
356
409
|
assetPublicIds: serialized.assetPublicIds,
|
|
357
410
|
bundleContentHashes: serialized.bundleContentHashes,
|
|
358
|
-
publicIdByAssetId: serialized.publicIdByAssetId
|
|
411
|
+
publicIdByAssetId: serialized.publicIdByAssetId,
|
|
412
|
+
conditions: serialized.conditions
|
|
359
413
|
});
|
|
360
414
|
}
|
|
361
415
|
createBundle(opts) {
|
|
@@ -768,7 +822,7 @@ class BundleGraph {
|
|
|
768
822
|
return true;
|
|
769
823
|
}
|
|
770
824
|
let assetNodeId = (0, _nullthrows().default)(this._graph.getNodeIdByContentKey(asset.id));
|
|
771
|
-
if (this._graph.getNodeIdsConnectedTo(assetNodeId, bundleGraphEdgeTypes.references).map(id => this._graph.getNode(id)).some(node => (node === null || node === void 0 ? void 0 : node.type) === 'dependency' && node.value.priority === _types.Priority.lazy && node.value.specifierType !== _types.SpecifierType.url)) {
|
|
825
|
+
if (this._graph.getNodeIdsConnectedTo(assetNodeId, bundleGraphEdgeTypes.references).map(id => this._graph.getNode(id)).some(node => (node === null || node === void 0 ? void 0 : node.type) === 'dependency' && (node.value.priority === _types.Priority.lazy || node.value.priority === _types.Priority.conditional) && node.value.specifierType !== _types.SpecifierType.url)) {
|
|
772
826
|
// If this asset is referenced by any async dependency, it's referenced.
|
|
773
827
|
return true;
|
|
774
828
|
}
|
package/lib/RequestTracker.js
CHANGED
|
@@ -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
|
-
|
|
472
|
+
|
|
473
473
|
let nodeId = this.getNodeIdByContentKey(node.id);
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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/lib/SymbolPropagation.js
CHANGED
|
@@ -108,7 +108,7 @@ function propagateSymbols({
|
|
|
108
108
|
namespaceReexportedSymbols.add('*');
|
|
109
109
|
} else {
|
|
110
110
|
for (let incomingDep of incomingDeps) {
|
|
111
|
-
if (incomingDep.value.symbols == null) {
|
|
111
|
+
if (incomingDep.value.symbols == null || incomingDep.value.priority === _types.Priority.conditional) {
|
|
112
112
|
if (incomingDep.value.sourceAssetId == null) {
|
|
113
113
|
// The root dependency on non-library builds
|
|
114
114
|
isEntry = true;
|
|
@@ -189,5 +189,73 @@ class BundleGraph {
|
|
|
189
189
|
getEntryRoot(target) {
|
|
190
190
|
return this.#graph.getEntryRoot(this.#options.projectRoot, (0, _Target.targetToInternalTarget)(target));
|
|
191
191
|
}
|
|
192
|
+
|
|
193
|
+
// Given a set of dependencies, return any conditions where those dependencies are either
|
|
194
|
+
// the true or false dependency for those conditions. This is currently used to work out which
|
|
195
|
+
// conditions belong to a bundle in packaging.
|
|
196
|
+
getConditionsForDependencies(deps) {
|
|
197
|
+
const conditions = new Set();
|
|
198
|
+
const depIds = deps.map(dep => dep.id);
|
|
199
|
+
for (const condition of this.#graph._conditions.values()) {
|
|
200
|
+
if (depIds.includes(condition.ifTrueDependency.id) || depIds.includes(condition.ifFalseDependency.id)) {
|
|
201
|
+
const [trueAsset, falseAsset] = [condition.ifTrueDependency, condition.ifFalseDependency].map(dep => {
|
|
202
|
+
const resolved = (0, _nullthrows().default)(this.#graph.resolveAsyncDependency(dep));
|
|
203
|
+
if (resolved.type === 'asset') {
|
|
204
|
+
return resolved.value;
|
|
205
|
+
} else {
|
|
206
|
+
return this.#graph.getAssetById(resolved.value.entryAssetId);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
conditions.add({
|
|
210
|
+
publicId: condition.publicId,
|
|
211
|
+
key: condition.key,
|
|
212
|
+
ifTrueDependency: (0, _nullthrows().default)(deps.find(dep => dep.id === condition.ifTrueDependency.id), 'ifTrueDependency was null'),
|
|
213
|
+
ifFalseDependency: (0, _nullthrows().default)(deps.find(dep => dep.id === condition.ifFalseDependency.id), 'ifFalseDependency was null'),
|
|
214
|
+
ifTrueAssetId: this.#graph.getAssetPublicId(trueAsset),
|
|
215
|
+
ifFalseAssetId: this.#graph.getAssetPublicId(falseAsset)
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return conditions;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// This is used to generate information for building a manifest that can
|
|
223
|
+
// be used by a webserver to understand which conditions are used by which bundles,
|
|
224
|
+
// and which bundles those conditions require depending on what they evaluate to.
|
|
225
|
+
getConditionalBundleMapping() {
|
|
226
|
+
let bundleConditions = new Map();
|
|
227
|
+
|
|
228
|
+
// Convert the internal references in conditions to public API references
|
|
229
|
+
for (const cond of this.#graph._conditions.values()) {
|
|
230
|
+
let assets = Array.from(cond.assets).map(asset => (0, _nullthrows().default)(this.getAssetById(asset.id)));
|
|
231
|
+
let bundles = new Set();
|
|
232
|
+
let ifTrueBundles = [];
|
|
233
|
+
let ifFalseBundles = [];
|
|
234
|
+
for (const asset of assets) {
|
|
235
|
+
const bundlesWithAsset = this.getBundlesWithAsset(asset);
|
|
236
|
+
for (const bundle of bundlesWithAsset) {
|
|
237
|
+
bundles.add(bundle);
|
|
238
|
+
}
|
|
239
|
+
const assetDeps = this.getDependencies(asset);
|
|
240
|
+
const depToBundles = dep => {
|
|
241
|
+
const publicDep = (0, _nullthrows().default)(assetDeps.find(assetDep => dep.id === assetDep.id));
|
|
242
|
+
const resolved = (0, _nullthrows().default)(this.resolveAsyncDependency(publicDep));
|
|
243
|
+
(0, _assert().default)(resolved.type === 'bundle_group');
|
|
244
|
+
return this.getBundlesInBundleGroup(resolved.value);
|
|
245
|
+
};
|
|
246
|
+
ifTrueBundles.push(...depToBundles(cond.ifTrueDependency));
|
|
247
|
+
ifFalseBundles.push(...depToBundles(cond.ifFalseDependency));
|
|
248
|
+
}
|
|
249
|
+
for (let bundle of bundles) {
|
|
250
|
+
const conditions = bundleConditions.get(bundle) ?? new Map();
|
|
251
|
+
conditions.set(cond.key, {
|
|
252
|
+
ifTrueBundles,
|
|
253
|
+
ifFalseBundles
|
|
254
|
+
});
|
|
255
|
+
bundleConditions.set(bundle, conditions);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return bundleConditions;
|
|
259
|
+
}
|
|
192
260
|
}
|
|
193
261
|
exports.default = BundleGraph;
|
package/lib/resolveOptions.js
CHANGED
|
@@ -197,6 +197,8 @@ async function resolveOptions(initialOptions) {
|
|
|
197
197
|
outputFormat: initialOptions === null || initialOptions === void 0 || (_initialOptions$defau8 = initialOptions.defaultTargetOptions) === null || _initialOptions$defau8 === void 0 ? void 0 : _initialOptions$defau8.outputFormat,
|
|
198
198
|
isLibrary: initialOptions === null || initialOptions === void 0 || (_initialOptions$defau9 = initialOptions.defaultTargetOptions) === null || _initialOptions$defau9 === void 0 ? void 0 : _initialOptions$defau9.isLibrary
|
|
199
199
|
},
|
|
200
|
+
// unused, feature-flags are set above this to allow this function to use
|
|
201
|
+
// feature-flags
|
|
200
202
|
featureFlags: {
|
|
201
203
|
..._featureFlags().DEFAULT_FEATURE_FLAGS,
|
|
202
204
|
...(initialOptions === null || initialOptions === void 0 ? void 0 : initialOptions.featureFlags)
|
package/lib/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaspack/core",
|
|
3
|
-
"version": "2.12.1-canary.
|
|
3
|
+
"version": "2.12.1-canary.3434+c61bf3cde",
|
|
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.
|
|
24
|
-
"@atlaspack/diagnostic": "2.12.1-canary.
|
|
25
|
-
"@atlaspack/events": "2.12.1-canary.
|
|
26
|
-
"@atlaspack/feature-flags": "2.12.1-canary.
|
|
27
|
-
"@atlaspack/fs": "2.12.1-canary.
|
|
28
|
-
"@atlaspack/graph": "3.2.1-canary.
|
|
29
|
-
"@atlaspack/logger": "2.12.1-canary.
|
|
30
|
-
"@atlaspack/package-manager": "2.12.1-canary.
|
|
31
|
-
"@atlaspack/plugin": "2.12.1-canary.
|
|
32
|
-
"@atlaspack/profiler": "2.12.1-canary.
|
|
33
|
-
"@atlaspack/rust": "2.12.1-canary.
|
|
34
|
-
"@atlaspack/types": "2.12.1-canary.
|
|
35
|
-
"@atlaspack/utils": "2.12.1-canary.
|
|
36
|
-
"@atlaspack/workers": "2.12.1-canary.
|
|
23
|
+
"@atlaspack/cache": "2.12.1-canary.3434+c61bf3cde",
|
|
24
|
+
"@atlaspack/diagnostic": "2.12.1-canary.3434+c61bf3cde",
|
|
25
|
+
"@atlaspack/events": "2.12.1-canary.3434+c61bf3cde",
|
|
26
|
+
"@atlaspack/feature-flags": "2.12.1-canary.3434+c61bf3cde",
|
|
27
|
+
"@atlaspack/fs": "2.12.1-canary.3434+c61bf3cde",
|
|
28
|
+
"@atlaspack/graph": "3.2.1-canary.3434+c61bf3cde",
|
|
29
|
+
"@atlaspack/logger": "2.12.1-canary.3434+c61bf3cde",
|
|
30
|
+
"@atlaspack/package-manager": "2.12.1-canary.3434+c61bf3cde",
|
|
31
|
+
"@atlaspack/plugin": "2.12.1-canary.3434+c61bf3cde",
|
|
32
|
+
"@atlaspack/profiler": "2.12.1-canary.3434+c61bf3cde",
|
|
33
|
+
"@atlaspack/rust": "2.12.1-canary.3434+c61bf3cde",
|
|
34
|
+
"@atlaspack/types": "2.12.1-canary.3434+c61bf3cde",
|
|
35
|
+
"@atlaspack/utils": "2.12.1-canary.3434+c61bf3cde",
|
|
36
|
+
"@atlaspack/workers": "2.12.1-canary.3434+c61bf3cde",
|
|
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": "
|
|
60
|
+
"gitHead": "c61bf3cde53d1cdb5ae13541c644a6e3ef5521e1"
|
|
61
61
|
}
|
package/src/Atlaspack.js
CHANGED
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
fromProjectPathRelative,
|
|
58
58
|
} from './projectPath';
|
|
59
59
|
import {tracer} from '@atlaspack/profiler';
|
|
60
|
-
import {setFeatureFlags} from '@atlaspack/feature-flags';
|
|
60
|
+
import {setFeatureFlags, DEFAULT_FEATURE_FLAGS} from '@atlaspack/feature-flags';
|
|
61
61
|
import {AtlaspackV3, toFileSystemV3} from './atlaspack-v3';
|
|
62
62
|
|
|
63
63
|
registerCoreWithSerializer();
|
|
@@ -105,6 +105,12 @@ export default class Atlaspack {
|
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
const featureFlags = {
|
|
109
|
+
...DEFAULT_FEATURE_FLAGS,
|
|
110
|
+
...this.#initialOptions.featureFlags,
|
|
111
|
+
};
|
|
112
|
+
setFeatureFlags(featureFlags);
|
|
113
|
+
|
|
108
114
|
await initSourcemaps;
|
|
109
115
|
await initRust?.();
|
|
110
116
|
try {
|
|
@@ -117,9 +123,10 @@ export default class Atlaspack {
|
|
|
117
123
|
logger.warn(e);
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
let resolvedOptions: AtlaspackOptions = await resolveOptions(
|
|
121
|
-
this.#initialOptions,
|
|
122
|
-
|
|
126
|
+
let resolvedOptions: AtlaspackOptions = await resolveOptions({
|
|
127
|
+
...this.#initialOptions,
|
|
128
|
+
featureFlags,
|
|
129
|
+
});
|
|
123
130
|
this.#resolvedOptions = resolvedOptions;
|
|
124
131
|
|
|
125
132
|
let rustAtlaspack: AtlaspackV3;
|
|
@@ -152,8 +159,6 @@ export default class Atlaspack {
|
|
|
152
159
|
});
|
|
153
160
|
}
|
|
154
161
|
|
|
155
|
-
setFeatureFlags(resolvedOptions.featureFlags);
|
|
156
|
-
|
|
157
162
|
let {config} = await loadAtlaspackConfig(resolvedOptions);
|
|
158
163
|
this.#config = new AtlaspackConfig(config, resolvedOptions);
|
|
159
164
|
|
|
@@ -544,6 +549,10 @@ export default class Atlaspack {
|
|
|
544
549
|
return this.#farm.takeHeapSnapshot();
|
|
545
550
|
}
|
|
546
551
|
|
|
552
|
+
async unstable_invalidate(): Promise<void> {
|
|
553
|
+
await this._init();
|
|
554
|
+
}
|
|
555
|
+
|
|
547
556
|
async unstable_transform(
|
|
548
557
|
options: AtlaspackTransformOptions,
|
|
549
558
|
): Promise<Array<Asset>> {
|
package/src/BundleGraph.js
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
Symbol,
|
|
7
7
|
TraversalActions,
|
|
8
8
|
BundleBehavior as IBundleBehavior,
|
|
9
|
+
ConditionMeta,
|
|
9
10
|
} from '@atlaspack/types';
|
|
10
11
|
import type {
|
|
11
12
|
ContentKey,
|
|
@@ -26,6 +27,7 @@ import type {
|
|
|
26
27
|
Environment,
|
|
27
28
|
InternalSourceLocation,
|
|
28
29
|
Target,
|
|
30
|
+
Condition,
|
|
29
31
|
} from './types';
|
|
30
32
|
import type AssetGraph from './AssetGraph';
|
|
31
33
|
import type {ProjectPath} from './projectPath';
|
|
@@ -46,6 +48,7 @@ import {getBundleGroupId, getPublicId} from './utils';
|
|
|
46
48
|
import {ISOLATED_ENVS} from './public/Environment';
|
|
47
49
|
import {fromProjectPath, fromProjectPathRelative} from './projectPath';
|
|
48
50
|
import {HASH_REF_PREFIX} from './constants';
|
|
51
|
+
import {getFeatureFlag} from '@atlaspack/feature-flags';
|
|
49
52
|
|
|
50
53
|
export const bundleGraphEdgeTypes = {
|
|
51
54
|
// A lack of an edge type indicates to follow the edge while traversing
|
|
@@ -91,6 +94,7 @@ type BundleGraphOpts = {|
|
|
|
91
94
|
bundleContentHashes: Map<string, string>,
|
|
92
95
|
assetPublicIds: Set<string>,
|
|
93
96
|
publicIdByAssetId: Map<string, string>,
|
|
97
|
+
conditions: Map<string, Condition>,
|
|
94
98
|
|};
|
|
95
99
|
|
|
96
100
|
type SerializedBundleGraph = {|
|
|
@@ -99,6 +103,7 @@ type SerializedBundleGraph = {|
|
|
|
99
103
|
bundleContentHashes: Map<string, string>,
|
|
100
104
|
assetPublicIds: Set<string>,
|
|
101
105
|
publicIdByAssetId: Map<string, string>,
|
|
106
|
+
conditions: Map<string, Condition>,
|
|
102
107
|
|};
|
|
103
108
|
|
|
104
109
|
function makeReadOnlySet<T>(set: Set<T>): $ReadOnlySet<T> {
|
|
@@ -139,22 +144,26 @@ export default class BundleGraph {
|
|
|
139
144
|
/** The internal core Graph structure */
|
|
140
145
|
_graph: ContentGraph<BundleGraphNode, BundleGraphEdgeType>;
|
|
141
146
|
_bundlePublicIds /*: Set<string> */ = new Set<string>();
|
|
147
|
+
_conditions /*: Map<string, Condition> */ = new Map<string, Condition>();
|
|
142
148
|
|
|
143
149
|
constructor({
|
|
144
150
|
graph,
|
|
145
151
|
publicIdByAssetId,
|
|
146
152
|
assetPublicIds,
|
|
147
153
|
bundleContentHashes,
|
|
154
|
+
conditions,
|
|
148
155
|
}: {|
|
|
149
156
|
graph: ContentGraph<BundleGraphNode, BundleGraphEdgeType>,
|
|
150
157
|
publicIdByAssetId: Map<string, string>,
|
|
151
158
|
assetPublicIds: Set<string>,
|
|
152
159
|
bundleContentHashes: Map<string, string>,
|
|
160
|
+
conditions: Map<string, Condition>,
|
|
153
161
|
|}) {
|
|
154
162
|
this._graph = graph;
|
|
155
163
|
this._assetPublicIds = assetPublicIds;
|
|
156
164
|
this._publicIdByAssetId = publicIdByAssetId;
|
|
157
165
|
this._bundleContentHashes = bundleContentHashes;
|
|
166
|
+
this._conditions = conditions;
|
|
158
167
|
}
|
|
159
168
|
|
|
160
169
|
/**
|
|
@@ -171,6 +180,9 @@ export default class BundleGraph {
|
|
|
171
180
|
let assetGroupIds = new Map();
|
|
172
181
|
let dependencies = new Map();
|
|
173
182
|
let assetGraphNodeIdToBundleGraphNodeId = new Map<NodeId, NodeId>();
|
|
183
|
+
let conditions = new Map<string, Condition>();
|
|
184
|
+
|
|
185
|
+
let placeholderToDependency = new Map<string, Dependency>();
|
|
174
186
|
|
|
175
187
|
let assetGraphRootNode =
|
|
176
188
|
assetGraph.rootNodeId != null
|
|
@@ -195,6 +207,20 @@ export default class BundleGraph {
|
|
|
195
207
|
}
|
|
196
208
|
} else if (node != null && node.type === 'asset_group') {
|
|
197
209
|
assetGroupIds.set(nodeId, assetGraph.getNodeIdsConnectedFrom(nodeId));
|
|
210
|
+
} else if (
|
|
211
|
+
getFeatureFlag('conditionalBundlingApi') &&
|
|
212
|
+
node != null &&
|
|
213
|
+
node.type === 'dependency'
|
|
214
|
+
) {
|
|
215
|
+
// The dependency placeholders in the `importCond` calls that will be in the transformed
|
|
216
|
+
// code need to be mapped to the "real" dependencies, so we need access to a map of placeholders
|
|
217
|
+
// to dependencies
|
|
218
|
+
const dep = node.value;
|
|
219
|
+
// $FlowFixMe[incompatible-type] Meta is untyped
|
|
220
|
+
const placeholder: string | void = dep.meta?.placeholder;
|
|
221
|
+
if (placeholder != null) {
|
|
222
|
+
placeholderToDependency.set(placeholder, dep);
|
|
223
|
+
}
|
|
198
224
|
}
|
|
199
225
|
});
|
|
200
226
|
|
|
@@ -204,6 +230,51 @@ export default class BundleGraph {
|
|
|
204
230
|
walkVisited.add(nodeId);
|
|
205
231
|
|
|
206
232
|
let node = nullthrows(assetGraph.getNode(nodeId));
|
|
233
|
+
|
|
234
|
+
if (getFeatureFlag('conditionalBundlingApi') && node.type === 'asset') {
|
|
235
|
+
const asset = node.value;
|
|
236
|
+
if (Array.isArray(asset.meta.conditions)) {
|
|
237
|
+
// $FlowFixMe
|
|
238
|
+
for (const condition of (asset.meta.conditions: ConditionMeta[])) {
|
|
239
|
+
// Resolve the placeholders that were attached to the asset in JSTransformer to dependencies,
|
|
240
|
+
// as well as create a public id for the condition.
|
|
241
|
+
const {
|
|
242
|
+
key,
|
|
243
|
+
ifTruePlaceholder,
|
|
244
|
+
ifFalsePlaceholder,
|
|
245
|
+
}: {
|
|
246
|
+
key: string,
|
|
247
|
+
ifTruePlaceholder: string,
|
|
248
|
+
ifFalsePlaceholder: string,
|
|
249
|
+
...
|
|
250
|
+
} = condition;
|
|
251
|
+
|
|
252
|
+
const condHash = hashString(
|
|
253
|
+
`${key}:${ifTruePlaceholder}:${ifFalsePlaceholder}`,
|
|
254
|
+
);
|
|
255
|
+
const condPublicId = getPublicId(condHash, v => conditions.has(v));
|
|
256
|
+
|
|
257
|
+
if (conditions.has(condHash)) {
|
|
258
|
+
throw new Error('Unexpected duplicate asset');
|
|
259
|
+
} else {
|
|
260
|
+
conditions.set(condHash, {
|
|
261
|
+
publicId: condPublicId,
|
|
262
|
+
assets: new Set([asset]),
|
|
263
|
+
key,
|
|
264
|
+
ifTrueDependency: nullthrows(
|
|
265
|
+
placeholderToDependency.get(ifTruePlaceholder),
|
|
266
|
+
'ifTruePlaceholder was undefined',
|
|
267
|
+
),
|
|
268
|
+
ifFalseDependency: nullthrows(
|
|
269
|
+
placeholderToDependency.get(ifFalsePlaceholder),
|
|
270
|
+
'ifFalsePlaceholder was undefined',
|
|
271
|
+
),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
207
278
|
if (
|
|
208
279
|
node.type === 'dependency' &&
|
|
209
280
|
node.value.symbols != null &&
|
|
@@ -440,11 +511,13 @@ export default class BundleGraph {
|
|
|
440
511
|
);
|
|
441
512
|
}
|
|
442
513
|
}
|
|
514
|
+
|
|
443
515
|
return new BundleGraph({
|
|
444
516
|
graph,
|
|
445
517
|
assetPublicIds,
|
|
446
518
|
bundleContentHashes: new Map(),
|
|
447
519
|
publicIdByAssetId,
|
|
520
|
+
conditions,
|
|
448
521
|
});
|
|
449
522
|
}
|
|
450
523
|
|
|
@@ -455,6 +528,7 @@ export default class BundleGraph {
|
|
|
455
528
|
assetPublicIds: this._assetPublicIds,
|
|
456
529
|
bundleContentHashes: this._bundleContentHashes,
|
|
457
530
|
publicIdByAssetId: this._publicIdByAssetId,
|
|
531
|
+
conditions: this._conditions,
|
|
458
532
|
};
|
|
459
533
|
}
|
|
460
534
|
|
|
@@ -464,6 +538,7 @@ export default class BundleGraph {
|
|
|
464
538
|
assetPublicIds: serialized.assetPublicIds,
|
|
465
539
|
bundleContentHashes: serialized.bundleContentHashes,
|
|
466
540
|
publicIdByAssetId: serialized.publicIdByAssetId,
|
|
541
|
+
conditions: serialized.conditions,
|
|
467
542
|
});
|
|
468
543
|
}
|
|
469
544
|
|
|
@@ -1215,7 +1290,8 @@ export default class BundleGraph {
|
|
|
1215
1290
|
.some(
|
|
1216
1291
|
node =>
|
|
1217
1292
|
node?.type === 'dependency' &&
|
|
1218
|
-
node.value.priority === Priority.lazy
|
|
1293
|
+
(node.value.priority === Priority.lazy ||
|
|
1294
|
+
node.value.priority === Priority.conditional) &&
|
|
1219
1295
|
node.value.specifierType !== SpecifierType.url,
|
|
1220
1296
|
)
|
|
1221
1297
|
) {
|
package/src/RequestTracker.js
CHANGED
|
@@ -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
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/src/SymbolPropagation.js
CHANGED
|
@@ -16,7 +16,7 @@ import nullthrows from 'nullthrows';
|
|
|
16
16
|
import {setEqual} from '@atlaspack/utils';
|
|
17
17
|
import logger from '@atlaspack/logger';
|
|
18
18
|
import {md, convertSourceLocationToHighlight} from '@atlaspack/diagnostic';
|
|
19
|
-
import {BundleBehavior} from './types';
|
|
19
|
+
import {BundleBehavior, Priority} from './types';
|
|
20
20
|
import {fromProjectPathRelative, fromProjectPath} from './projectPath';
|
|
21
21
|
|
|
22
22
|
export function propagateSymbols({
|
|
@@ -101,7 +101,10 @@ export function propagateSymbols({
|
|
|
101
101
|
namespaceReexportedSymbols.add('*');
|
|
102
102
|
} else {
|
|
103
103
|
for (let incomingDep of incomingDeps) {
|
|
104
|
-
if (
|
|
104
|
+
if (
|
|
105
|
+
incomingDep.value.symbols == null ||
|
|
106
|
+
incomingDep.value.priority === Priority.conditional
|
|
107
|
+
) {
|
|
105
108
|
if (incomingDep.value.sourceAssetId == null) {
|
|
106
109
|
// The root dependency on non-library builds
|
|
107
110
|
isEntry = true;
|
|
@@ -332,4 +332,110 @@ export default class BundleGraph<TBundle: IBundle>
|
|
|
332
332
|
targetToInternalTarget(target),
|
|
333
333
|
);
|
|
334
334
|
}
|
|
335
|
+
|
|
336
|
+
// Given a set of dependencies, return any conditions where those dependencies are either
|
|
337
|
+
// the true or false dependency for those conditions. This is currently used to work out which
|
|
338
|
+
// conditions belong to a bundle in packaging.
|
|
339
|
+
getConditionsForDependencies(deps: Array<IDependency>): Set<{|
|
|
340
|
+
publicId: string,
|
|
341
|
+
key: string,
|
|
342
|
+
ifTrueDependency: IDependency,
|
|
343
|
+
ifFalseDependency: IDependency,
|
|
344
|
+
ifTrueAssetId: string,
|
|
345
|
+
ifFalseAssetId: string,
|
|
346
|
+
|}> {
|
|
347
|
+
const conditions = new Set();
|
|
348
|
+
const depIds = deps.map(dep => dep.id);
|
|
349
|
+
for (const condition of this.#graph._conditions.values()) {
|
|
350
|
+
if (
|
|
351
|
+
depIds.includes(condition.ifTrueDependency.id) ||
|
|
352
|
+
depIds.includes(condition.ifFalseDependency.id)
|
|
353
|
+
) {
|
|
354
|
+
const [trueAsset, falseAsset] = [
|
|
355
|
+
condition.ifTrueDependency,
|
|
356
|
+
condition.ifFalseDependency,
|
|
357
|
+
].map(dep => {
|
|
358
|
+
const resolved = nullthrows(this.#graph.resolveAsyncDependency(dep));
|
|
359
|
+
if (resolved.type === 'asset') {
|
|
360
|
+
return resolved.value;
|
|
361
|
+
} else {
|
|
362
|
+
return this.#graph.getAssetById(resolved.value.entryAssetId);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
conditions.add({
|
|
367
|
+
publicId: condition.publicId,
|
|
368
|
+
key: condition.key,
|
|
369
|
+
ifTrueDependency: nullthrows(
|
|
370
|
+
deps.find(dep => dep.id === condition.ifTrueDependency.id),
|
|
371
|
+
'ifTrueDependency was null',
|
|
372
|
+
),
|
|
373
|
+
ifFalseDependency: nullthrows(
|
|
374
|
+
deps.find(dep => dep.id === condition.ifFalseDependency.id),
|
|
375
|
+
'ifFalseDependency was null',
|
|
376
|
+
),
|
|
377
|
+
ifTrueAssetId: this.#graph.getAssetPublicId(trueAsset),
|
|
378
|
+
ifFalseAssetId: this.#graph.getAssetPublicId(falseAsset),
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return conditions;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// This is used to generate information for building a manifest that can
|
|
387
|
+
// be used by a webserver to understand which conditions are used by which bundles,
|
|
388
|
+
// and which bundles those conditions require depending on what they evaluate to.
|
|
389
|
+
getConditionalBundleMapping(): Map<
|
|
390
|
+
TBundle,
|
|
391
|
+
Map<
|
|
392
|
+
string,
|
|
393
|
+
{|
|
|
394
|
+
ifTrueBundles: Array<TBundle>,
|
|
395
|
+
ifFalseBundles: Array<TBundle>,
|
|
396
|
+
|},
|
|
397
|
+
>,
|
|
398
|
+
> {
|
|
399
|
+
let bundleConditions = new Map();
|
|
400
|
+
|
|
401
|
+
// Convert the internal references in conditions to public API references
|
|
402
|
+
for (const cond of this.#graph._conditions.values()) {
|
|
403
|
+
let assets = Array.from(cond.assets).map(asset =>
|
|
404
|
+
nullthrows(this.getAssetById(asset.id)),
|
|
405
|
+
);
|
|
406
|
+
let bundles = new Set<TBundle>();
|
|
407
|
+
let ifTrueBundles = [];
|
|
408
|
+
let ifFalseBundles = [];
|
|
409
|
+
for (const asset of assets) {
|
|
410
|
+
const bundlesWithAsset = this.getBundlesWithAsset(asset);
|
|
411
|
+
for (const bundle of bundlesWithAsset) {
|
|
412
|
+
bundles.add(bundle);
|
|
413
|
+
}
|
|
414
|
+
const assetDeps = this.getDependencies(asset);
|
|
415
|
+
const depToBundles = dep => {
|
|
416
|
+
const publicDep = nullthrows(
|
|
417
|
+
assetDeps.find(assetDep => dep.id === assetDep.id),
|
|
418
|
+
);
|
|
419
|
+
const resolved = nullthrows(this.resolveAsyncDependency(publicDep));
|
|
420
|
+
invariant(resolved.type === 'bundle_group');
|
|
421
|
+
return this.getBundlesInBundleGroup(resolved.value);
|
|
422
|
+
};
|
|
423
|
+
ifTrueBundles.push(...depToBundles(cond.ifTrueDependency));
|
|
424
|
+
ifFalseBundles.push(...depToBundles(cond.ifFalseDependency));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
for (let bundle of bundles) {
|
|
428
|
+
const conditions = bundleConditions.get(bundle) ?? new Map();
|
|
429
|
+
|
|
430
|
+
conditions.set(cond.key, {
|
|
431
|
+
ifTrueBundles,
|
|
432
|
+
ifFalseBundles,
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
bundleConditions.set(bundle, conditions);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return bundleConditions;
|
|
440
|
+
}
|
|
335
441
|
}
|
package/src/resolveOptions.js
CHANGED
|
@@ -231,6 +231,8 @@ export default async function resolveOptions(
|
|
|
231
231
|
outputFormat: initialOptions?.defaultTargetOptions?.outputFormat,
|
|
232
232
|
isLibrary: initialOptions?.defaultTargetOptions?.isLibrary,
|
|
233
233
|
},
|
|
234
|
+
// unused, feature-flags are set above this to allow this function to use
|
|
235
|
+
// feature-flags
|
|
234
236
|
featureFlags: {...DEFAULT_FEATURE_FLAGS, ...initialOptions?.featureFlags},
|
|
235
237
|
parcelVersion: ATLASPACK_VERSION,
|
|
236
238
|
};
|
package/src/types.js
CHANGED
|
@@ -115,6 +115,7 @@ export const Priority = {
|
|
|
115
115
|
sync: 0,
|
|
116
116
|
parallel: 1,
|
|
117
117
|
lazy: 2,
|
|
118
|
+
conditional: 3,
|
|
118
119
|
};
|
|
119
120
|
|
|
120
121
|
// Must match package_json.rs in node-resolver-rs.
|
|
@@ -542,6 +543,7 @@ export type Bundle = {|
|
|
|
542
543
|
displayName: ?string,
|
|
543
544
|
pipeline: ?string,
|
|
544
545
|
manualSharedBundle?: ?string,
|
|
546
|
+
conditions?: Map<string, string>,
|
|
545
547
|
|};
|
|
546
548
|
|
|
547
549
|
export type BundleNode = {|
|
|
@@ -580,3 +582,11 @@ export type ValidationOpts = {|
|
|
|
580
582
|
|};
|
|
581
583
|
|
|
582
584
|
export type ReportFn = (event: ReporterEvent) => void | Promise<void>;
|
|
585
|
+
|
|
586
|
+
export type Condition = {|
|
|
587
|
+
publicId: string,
|
|
588
|
+
assets: Set<Asset>,
|
|
589
|
+
key: string,
|
|
590
|
+
ifTrueDependency: Dependency,
|
|
591
|
+
ifFalseDependency: Dependency,
|
|
592
|
+
|};
|
|
@@ -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', () => {
|