@atlaspack/core 2.16.2-canary.282 → 2.16.2-canary.284
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/dist/BundleGraph.js +105 -0
- package/dist/public/BundleGraph.js +12 -0
- package/lib/BundleGraph.js +107 -0
- package/lib/public/BundleGraph.js +13 -0
- package/lib/types/BundleGraph.d.ts +2 -0
- package/lib/types/public/BundleGraph.d.ts +2 -0
- package/package.json +17 -17
- package/src/BundleGraph.ts +152 -0
- package/src/public/BundleGraph.ts +21 -0
- package/tsconfig.tsbuildinfo +1 -1
package/dist/BundleGraph.js
CHANGED
|
@@ -920,6 +920,111 @@ class BundleGraph {
|
|
|
920
920
|
return isReferenced;
|
|
921
921
|
});
|
|
922
922
|
}
|
|
923
|
+
// New method: Fast checks only (no caching of results)
|
|
924
|
+
isAssetReferencedFastCheck(bundle, asset) {
|
|
925
|
+
// Fast Check #1: If asset is in multiple bundles in same target, it's referenced
|
|
926
|
+
let bundlesWithAsset = this.getBundlesWithAsset(asset).filter((b) => b.target.name === bundle.target.name &&
|
|
927
|
+
b.target.distDir === bundle.target.distDir);
|
|
928
|
+
if (bundlesWithAsset.length > 1) {
|
|
929
|
+
return true;
|
|
930
|
+
}
|
|
931
|
+
// Fast Check #2: If asset is referenced by any async/conditional dependency, it's referenced
|
|
932
|
+
let assetNodeId = (0, nullthrows_1.default)(this._graph.getNodeIdByContentKey(asset.id));
|
|
933
|
+
if (this._graph
|
|
934
|
+
.getNodeIdsConnectedTo(assetNodeId, exports.bundleGraphEdgeTypes.references)
|
|
935
|
+
.map((id) => this._graph.getNode(id))
|
|
936
|
+
.some((node) => node?.type === 'dependency' &&
|
|
937
|
+
(node.value.priority === types_1.Priority.lazy ||
|
|
938
|
+
node.value.priority === types_1.Priority.conditional) &&
|
|
939
|
+
node.value.specifierType !== types_1.SpecifierType.url)) {
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
// Fast checks failed - return null to indicate expensive computation needed
|
|
943
|
+
return null;
|
|
944
|
+
}
|
|
945
|
+
getReferencedAssets(bundle) {
|
|
946
|
+
let referencedAssets = new Set();
|
|
947
|
+
// Build a map of all assets in this bundle with their dependencies
|
|
948
|
+
// This allows us to check all assets in a single traversal
|
|
949
|
+
let assetDependenciesMap = new Map();
|
|
950
|
+
this.traverseAssets(bundle, (asset) => {
|
|
951
|
+
// Always do fast checks (no caching)
|
|
952
|
+
let fastCheckResult = this.isAssetReferencedFastCheck(bundle, asset);
|
|
953
|
+
if (fastCheckResult === true) {
|
|
954
|
+
referencedAssets.add(asset);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
// Fast checks failed (fastCheckResult === null), need expensive computation
|
|
958
|
+
// Check if it's actually referenced via traversal
|
|
959
|
+
// Store dependencies for later batch checking
|
|
960
|
+
let dependencies = this._graph
|
|
961
|
+
.getNodeIdsConnectedTo((0, nullthrows_1.default)(this._graph.getNodeIdByContentKey(asset.id)))
|
|
962
|
+
.map((id) => (0, nullthrows_1.default)(this._graph.getNode(id)))
|
|
963
|
+
.filter((node) => node.type === 'dependency')
|
|
964
|
+
.map((node) => {
|
|
965
|
+
(0, assert_2.default)(node.type === 'dependency');
|
|
966
|
+
return node.value;
|
|
967
|
+
});
|
|
968
|
+
if (dependencies.length > 0) {
|
|
969
|
+
assetDependenciesMap.set(asset, dependencies);
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
// If no assets need the expensive check, return early
|
|
973
|
+
if (assetDependenciesMap.size === 0) {
|
|
974
|
+
return referencedAssets;
|
|
975
|
+
}
|
|
976
|
+
// Get the assets we need to check once
|
|
977
|
+
let assetsToCheck = Array.from(assetDependenciesMap.keys());
|
|
978
|
+
// Helper function to check if all assets from assetDependenciesMap are in referencedAssets
|
|
979
|
+
const allAssetsReferenced = () => assetsToCheck.length <= referencedAssets.size &&
|
|
980
|
+
assetsToCheck.every((asset) => referencedAssets.has(asset));
|
|
981
|
+
// Do ONE traversal to check all remaining assets
|
|
982
|
+
// We can share visitedBundles across all assets because we check every asset
|
|
983
|
+
// against every visited bundle, which matches the original per-asset behavior
|
|
984
|
+
let siblingBundles = new Set(this.getBundleGroupsContainingBundle(bundle).flatMap((bundleGroup) => this.getBundlesInBundleGroup(bundleGroup, { includeInline: true })));
|
|
985
|
+
let visitedBundles = new Set();
|
|
986
|
+
// Single traversal from all referencers
|
|
987
|
+
for (let referencer of siblingBundles) {
|
|
988
|
+
this.traverseBundles((descendant, _, actions) => {
|
|
989
|
+
if (descendant.id === bundle.id) {
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
if (visitedBundles.has(descendant)) {
|
|
993
|
+
actions.skipChildren();
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
visitedBundles.add(descendant);
|
|
997
|
+
if (descendant.type !== bundle.type ||
|
|
998
|
+
(0, EnvironmentManager_1.fromEnvironmentId)(descendant.env).context !==
|
|
999
|
+
(0, EnvironmentManager_1.fromEnvironmentId)(bundle.env).context) {
|
|
1000
|
+
// Don't skip children - they might be the right type!
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
// Check ALL assets at once in this descendant bundle
|
|
1004
|
+
for (let [asset, dependencies] of assetDependenciesMap) {
|
|
1005
|
+
// Skip if already marked as referenced
|
|
1006
|
+
if (referencedAssets.has(asset)) {
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
// Check if this descendant bundle references the asset
|
|
1010
|
+
if (!this.bundleHasAsset(descendant, asset) &&
|
|
1011
|
+
dependencies.some((dependency) => this.bundleHasDependency(descendant, dependency))) {
|
|
1012
|
+
referencedAssets.add(asset);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
// If all assets from assetDependenciesMap are now marked as referenced, we can stop early
|
|
1016
|
+
if (allAssetsReferenced()) {
|
|
1017
|
+
actions.stop();
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
}, referencer);
|
|
1021
|
+
// If all assets from assetDependenciesMap are referenced, no need to check more sibling bundles
|
|
1022
|
+
if (allAssetsReferenced()) {
|
|
1023
|
+
break;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return referencedAssets;
|
|
1027
|
+
}
|
|
923
1028
|
hasParentBundleOfType(bundle, type) {
|
|
924
1029
|
let parents = this.getParentBundles(bundle);
|
|
925
1030
|
return (parents.length > 0 && parents.every((parent) => parent.type === type));
|
|
@@ -148,6 +148,18 @@ class BundleGraph {
|
|
|
148
148
|
isAssetReferenced(bundle, asset) {
|
|
149
149
|
return __classPrivateFieldGet(this, _BundleGraph_graph, "f").isAssetReferenced((0, Bundle_1.bundleToInternalBundle)(bundle), (0, Asset_1.assetToAssetValue)(asset));
|
|
150
150
|
}
|
|
151
|
+
isAssetReferencedFastCheck(bundle, asset) {
|
|
152
|
+
return __classPrivateFieldGet(this, _BundleGraph_graph, "f").isAssetReferencedFastCheck((0, Bundle_1.bundleToInternalBundle)(bundle), (0, Asset_1.assetToAssetValue)(asset));
|
|
153
|
+
}
|
|
154
|
+
getReferencedAssets(bundle) {
|
|
155
|
+
let internalReferencedAssets = __classPrivateFieldGet(this, _BundleGraph_graph, "f").getReferencedAssets((0, Bundle_1.bundleToInternalBundle)(bundle));
|
|
156
|
+
// Convert internal assets to public assets
|
|
157
|
+
let publicReferencedAssets = new Set();
|
|
158
|
+
for (let internalAsset of internalReferencedAssets) {
|
|
159
|
+
publicReferencedAssets.add((0, Asset_1.assetFromValue)(internalAsset, __classPrivateFieldGet(this, _BundleGraph_options, "f")));
|
|
160
|
+
}
|
|
161
|
+
return publicReferencedAssets;
|
|
162
|
+
}
|
|
151
163
|
hasParentBundleOfType(bundle, type) {
|
|
152
164
|
return __classPrivateFieldGet(this, _BundleGraph_graph, "f").hasParentBundleOfType((0, Bundle_1.bundleToInternalBundle)(bundle), type);
|
|
153
165
|
}
|
package/lib/BundleGraph.js
CHANGED
|
@@ -914,6 +914,113 @@ class BundleGraph {
|
|
|
914
914
|
return isReferenced;
|
|
915
915
|
});
|
|
916
916
|
}
|
|
917
|
+
|
|
918
|
+
// New method: Fast checks only (no caching of results)
|
|
919
|
+
isAssetReferencedFastCheck(bundle, asset) {
|
|
920
|
+
// Fast Check #1: If asset is in multiple bundles in same target, it's referenced
|
|
921
|
+
let bundlesWithAsset = this.getBundlesWithAsset(asset).filter(b => b.target.name === bundle.target.name && b.target.distDir === bundle.target.distDir);
|
|
922
|
+
if (bundlesWithAsset.length > 1) {
|
|
923
|
+
return true;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Fast Check #2: If asset is referenced by any async/conditional dependency, it's referenced
|
|
927
|
+
let assetNodeId = (0, _nullthrows().default)(this._graph.getNodeIdByContentKey(asset.id));
|
|
928
|
+
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)) {
|
|
929
|
+
return true;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Fast checks failed - return null to indicate expensive computation needed
|
|
933
|
+
return null;
|
|
934
|
+
}
|
|
935
|
+
getReferencedAssets(bundle) {
|
|
936
|
+
let referencedAssets = new Set();
|
|
937
|
+
|
|
938
|
+
// Build a map of all assets in this bundle with their dependencies
|
|
939
|
+
// This allows us to check all assets in a single traversal
|
|
940
|
+
let assetDependenciesMap = new Map();
|
|
941
|
+
this.traverseAssets(bundle, asset => {
|
|
942
|
+
// Always do fast checks (no caching)
|
|
943
|
+
let fastCheckResult = this.isAssetReferencedFastCheck(bundle, asset);
|
|
944
|
+
if (fastCheckResult === true) {
|
|
945
|
+
referencedAssets.add(asset);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Fast checks failed (fastCheckResult === null), need expensive computation
|
|
950
|
+
// Check if it's actually referenced via traversal
|
|
951
|
+
|
|
952
|
+
// Store dependencies for later batch checking
|
|
953
|
+
let dependencies = this._graph.getNodeIdsConnectedTo((0, _nullthrows().default)(this._graph.getNodeIdByContentKey(asset.id))).map(id => (0, _nullthrows().default)(this._graph.getNode(id))).filter(node => node.type === 'dependency').map(node => {
|
|
954
|
+
(0, _assert().default)(node.type === 'dependency');
|
|
955
|
+
return node.value;
|
|
956
|
+
});
|
|
957
|
+
if (dependencies.length > 0) {
|
|
958
|
+
assetDependenciesMap.set(asset, dependencies);
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
// If no assets need the expensive check, return early
|
|
963
|
+
if (assetDependenciesMap.size === 0) {
|
|
964
|
+
return referencedAssets;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Get the assets we need to check once
|
|
968
|
+
let assetsToCheck = Array.from(assetDependenciesMap.keys());
|
|
969
|
+
|
|
970
|
+
// Helper function to check if all assets from assetDependenciesMap are in referencedAssets
|
|
971
|
+
const allAssetsReferenced = () => assetsToCheck.length <= referencedAssets.size && assetsToCheck.every(asset => referencedAssets.has(asset));
|
|
972
|
+
|
|
973
|
+
// Do ONE traversal to check all remaining assets
|
|
974
|
+
// We can share visitedBundles across all assets because we check every asset
|
|
975
|
+
// against every visited bundle, which matches the original per-asset behavior
|
|
976
|
+
let siblingBundles = new Set(this.getBundleGroupsContainingBundle(bundle).flatMap(bundleGroup => this.getBundlesInBundleGroup(bundleGroup, {
|
|
977
|
+
includeInline: true
|
|
978
|
+
})));
|
|
979
|
+
let visitedBundles = new Set();
|
|
980
|
+
|
|
981
|
+
// Single traversal from all referencers
|
|
982
|
+
for (let referencer of siblingBundles) {
|
|
983
|
+
this.traverseBundles((descendant, _, actions) => {
|
|
984
|
+
if (descendant.id === bundle.id) {
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
if (visitedBundles.has(descendant)) {
|
|
988
|
+
actions.skipChildren();
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
visitedBundles.add(descendant);
|
|
992
|
+
if (descendant.type !== bundle.type || (0, _EnvironmentManager.fromEnvironmentId)(descendant.env).context !== (0, _EnvironmentManager.fromEnvironmentId)(bundle.env).context) {
|
|
993
|
+
// Don't skip children - they might be the right type!
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// Check ALL assets at once in this descendant bundle
|
|
998
|
+
for (let [asset, dependencies] of assetDependenciesMap) {
|
|
999
|
+
// Skip if already marked as referenced
|
|
1000
|
+
if (referencedAssets.has(asset)) {
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// Check if this descendant bundle references the asset
|
|
1005
|
+
if (!this.bundleHasAsset(descendant, asset) && dependencies.some(dependency => this.bundleHasDependency(descendant, dependency))) {
|
|
1006
|
+
referencedAssets.add(asset);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// If all assets from assetDependenciesMap are now marked as referenced, we can stop early
|
|
1011
|
+
if (allAssetsReferenced()) {
|
|
1012
|
+
actions.stop();
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
}, referencer);
|
|
1016
|
+
|
|
1017
|
+
// If all assets from assetDependenciesMap are referenced, no need to check more sibling bundles
|
|
1018
|
+
if (allAssetsReferenced()) {
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return referencedAssets;
|
|
1023
|
+
}
|
|
917
1024
|
hasParentBundleOfType(bundle, type) {
|
|
918
1025
|
let parents = this.getParentBundles(bundle);
|
|
919
1026
|
return parents.length > 0 && parents.every(parent => parent.type === type);
|
|
@@ -114,6 +114,19 @@ class BundleGraph {
|
|
|
114
114
|
isAssetReferenced(bundle, asset) {
|
|
115
115
|
return this.#graph.isAssetReferenced((0, _Bundle.bundleToInternalBundle)(bundle), (0, _Asset.assetToAssetValue)(asset));
|
|
116
116
|
}
|
|
117
|
+
isAssetReferencedFastCheck(bundle, asset) {
|
|
118
|
+
return this.#graph.isAssetReferencedFastCheck((0, _Bundle.bundleToInternalBundle)(bundle), (0, _Asset.assetToAssetValue)(asset));
|
|
119
|
+
}
|
|
120
|
+
getReferencedAssets(bundle) {
|
|
121
|
+
let internalReferencedAssets = this.#graph.getReferencedAssets((0, _Bundle.bundleToInternalBundle)(bundle));
|
|
122
|
+
|
|
123
|
+
// Convert internal assets to public assets
|
|
124
|
+
let publicReferencedAssets = new Set();
|
|
125
|
+
for (let internalAsset of internalReferencedAssets) {
|
|
126
|
+
publicReferencedAssets.add((0, _Asset.assetFromValue)(internalAsset, this.#options));
|
|
127
|
+
}
|
|
128
|
+
return publicReferencedAssets;
|
|
129
|
+
}
|
|
117
130
|
hasParentBundleOfType(bundle, type) {
|
|
118
131
|
return this.#graph.hasParentBundleOfType((0, _Bundle.bundleToInternalBundle)(bundle), type);
|
|
119
132
|
}
|
|
@@ -129,6 +129,8 @@ export default class BundleGraph {
|
|
|
129
129
|
getDependencies(asset: Asset): Array<Dependency>;
|
|
130
130
|
traverseAssets<TContext>(bundle: Bundle, visit: GraphVisitor<Asset, TContext>, startAsset?: Asset): TContext | null | undefined;
|
|
131
131
|
isAssetReferenced(bundle: Bundle, asset: Asset): boolean;
|
|
132
|
+
isAssetReferencedFastCheck(bundle: Bundle, asset: Asset): boolean | null;
|
|
133
|
+
getReferencedAssets(bundle: Bundle): Set<Asset>;
|
|
132
134
|
hasParentBundleOfType(bundle: Bundle, type: string): boolean;
|
|
133
135
|
getParentBundles(bundle: Bundle): Array<Bundle>;
|
|
134
136
|
isAssetReachableFromBundle(asset: Asset, bundle: Bundle): boolean;
|
|
@@ -30,6 +30,8 @@ export default class BundleGraph<TBundle extends IBundle> implements IBundleGrap
|
|
|
30
30
|
getDependencies(asset: IAsset): Array<IDependency>;
|
|
31
31
|
isAssetReachableFromBundle(asset: IAsset, bundle: IBundle): boolean;
|
|
32
32
|
isAssetReferenced(bundle: IBundle, asset: IAsset): boolean;
|
|
33
|
+
isAssetReferencedFastCheck(bundle: IBundle, asset: IAsset): boolean | null;
|
|
34
|
+
getReferencedAssets(bundle: IBundle): Set<IAsset>;
|
|
33
35
|
hasParentBundleOfType(bundle: IBundle, type: string): boolean;
|
|
34
36
|
getBundlesInBundleGroup(bundleGroup: IBundleGroup, opts?: {
|
|
35
37
|
includeInline: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaspack/core",
|
|
3
|
-
"version": "2.16.2-canary.
|
|
3
|
+
"version": "2.16.2-canary.284+c8a0e51b1",
|
|
4
4
|
"license": "(MIT OR Apache-2.0)",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -22,21 +22,21 @@
|
|
|
22
22
|
"build:lib": "gulp build --gulpfile ../../../gulpfile.js --cwd ."
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@atlaspack/build-cache": "2.13.3-canary.
|
|
26
|
-
"@atlaspack/cache": "3.1.1-canary.
|
|
27
|
-
"@atlaspack/diagnostic": "2.14.1-canary.
|
|
28
|
-
"@atlaspack/events": "2.14.1-canary.
|
|
29
|
-
"@atlaspack/feature-flags": "2.14.1-canary.
|
|
30
|
-
"@atlaspack/fs": "2.14.5-canary.
|
|
31
|
-
"@atlaspack/graph": "3.4.1-canary.
|
|
32
|
-
"@atlaspack/logger": "2.14.5-canary.
|
|
33
|
-
"@atlaspack/package-manager": "2.14.5-canary.
|
|
34
|
-
"@atlaspack/plugin": "2.14.5-canary.
|
|
35
|
-
"@atlaspack/profiler": "2.14.1-canary.
|
|
36
|
-
"@atlaspack/rust": "3.2.1-canary.
|
|
37
|
-
"@atlaspack/types": "2.14.5-canary.
|
|
38
|
-
"@atlaspack/utils": "2.14.5-canary.
|
|
39
|
-
"@atlaspack/workers": "2.14.5-canary.
|
|
25
|
+
"@atlaspack/build-cache": "2.13.3-canary.352+c8a0e51b1",
|
|
26
|
+
"@atlaspack/cache": "3.1.1-canary.284+c8a0e51b1",
|
|
27
|
+
"@atlaspack/diagnostic": "2.14.1-canary.352+c8a0e51b1",
|
|
28
|
+
"@atlaspack/events": "2.14.1-canary.352+c8a0e51b1",
|
|
29
|
+
"@atlaspack/feature-flags": "2.14.1-canary.352+c8a0e51b1",
|
|
30
|
+
"@atlaspack/fs": "2.14.5-canary.284+c8a0e51b1",
|
|
31
|
+
"@atlaspack/graph": "3.4.1-canary.352+c8a0e51b1",
|
|
32
|
+
"@atlaspack/logger": "2.14.5-canary.284+c8a0e51b1",
|
|
33
|
+
"@atlaspack/package-manager": "2.14.5-canary.284+c8a0e51b1",
|
|
34
|
+
"@atlaspack/plugin": "2.14.5-canary.284+c8a0e51b1",
|
|
35
|
+
"@atlaspack/profiler": "2.14.1-canary.352+c8a0e51b1",
|
|
36
|
+
"@atlaspack/rust": "3.2.1-canary.284+c8a0e51b1",
|
|
37
|
+
"@atlaspack/types": "2.14.5-canary.284+c8a0e51b1",
|
|
38
|
+
"@atlaspack/utils": "2.14.5-canary.284+c8a0e51b1",
|
|
39
|
+
"@atlaspack/workers": "2.14.5-canary.284+c8a0e51b1",
|
|
40
40
|
"@mischnic/json-sourcemap": "^0.1.0",
|
|
41
41
|
"@parcel/source-map": "^2.1.1",
|
|
42
42
|
"base-x": "^3.0.8",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"./src/serializerCore.js": "./src/serializerCore.browser.js"
|
|
62
62
|
},
|
|
63
63
|
"type": "commonjs",
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "c8a0e51b1096d038a64644ad02fe68383f58682a"
|
|
65
65
|
}
|
package/src/BundleGraph.ts
CHANGED
|
@@ -1435,6 +1435,158 @@ export default class BundleGraph {
|
|
|
1435
1435
|
});
|
|
1436
1436
|
}
|
|
1437
1437
|
|
|
1438
|
+
// New method: Fast checks only (no caching of results)
|
|
1439
|
+
isAssetReferencedFastCheck(bundle: Bundle, asset: Asset): boolean | null {
|
|
1440
|
+
// Fast Check #1: If asset is in multiple bundles in same target, it's referenced
|
|
1441
|
+
let bundlesWithAsset = this.getBundlesWithAsset(asset).filter(
|
|
1442
|
+
(b) =>
|
|
1443
|
+
b.target.name === bundle.target.name &&
|
|
1444
|
+
b.target.distDir === bundle.target.distDir,
|
|
1445
|
+
);
|
|
1446
|
+
|
|
1447
|
+
if (bundlesWithAsset.length > 1) {
|
|
1448
|
+
return true;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// Fast Check #2: If asset is referenced by any async/conditional dependency, it's referenced
|
|
1452
|
+
let assetNodeId = nullthrows(this._graph.getNodeIdByContentKey(asset.id));
|
|
1453
|
+
|
|
1454
|
+
if (
|
|
1455
|
+
this._graph
|
|
1456
|
+
.getNodeIdsConnectedTo(assetNodeId, bundleGraphEdgeTypes.references)
|
|
1457
|
+
.map((id) => this._graph.getNode(id))
|
|
1458
|
+
.some(
|
|
1459
|
+
(node) =>
|
|
1460
|
+
node?.type === 'dependency' &&
|
|
1461
|
+
(node.value.priority === Priority.lazy ||
|
|
1462
|
+
node.value.priority === Priority.conditional) &&
|
|
1463
|
+
node.value.specifierType !== SpecifierType.url,
|
|
1464
|
+
)
|
|
1465
|
+
) {
|
|
1466
|
+
return true;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// Fast checks failed - return null to indicate expensive computation needed
|
|
1470
|
+
return null;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
getReferencedAssets(bundle: Bundle): Set<Asset> {
|
|
1474
|
+
let referencedAssets = new Set<Asset>();
|
|
1475
|
+
|
|
1476
|
+
// Build a map of all assets in this bundle with their dependencies
|
|
1477
|
+
// This allows us to check all assets in a single traversal
|
|
1478
|
+
let assetDependenciesMap = new Map<Asset, Array<Dependency>>();
|
|
1479
|
+
|
|
1480
|
+
this.traverseAssets(bundle, (asset) => {
|
|
1481
|
+
// Always do fast checks (no caching)
|
|
1482
|
+
let fastCheckResult = this.isAssetReferencedFastCheck(bundle, asset);
|
|
1483
|
+
|
|
1484
|
+
if (fastCheckResult === true) {
|
|
1485
|
+
referencedAssets.add(asset);
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
// Fast checks failed (fastCheckResult === null), need expensive computation
|
|
1490
|
+
// Check if it's actually referenced via traversal
|
|
1491
|
+
|
|
1492
|
+
// Store dependencies for later batch checking
|
|
1493
|
+
let dependencies = this._graph
|
|
1494
|
+
.getNodeIdsConnectedTo(
|
|
1495
|
+
nullthrows(this._graph.getNodeIdByContentKey(asset.id)),
|
|
1496
|
+
)
|
|
1497
|
+
.map((id) => nullthrows(this._graph.getNode(id)))
|
|
1498
|
+
.filter((node) => node.type === 'dependency')
|
|
1499
|
+
.map((node) => {
|
|
1500
|
+
invariant(node.type === 'dependency');
|
|
1501
|
+
return node.value;
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
if (dependencies.length > 0) {
|
|
1505
|
+
assetDependenciesMap.set(asset, dependencies);
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
|
|
1509
|
+
// If no assets need the expensive check, return early
|
|
1510
|
+
if (assetDependenciesMap.size === 0) {
|
|
1511
|
+
return referencedAssets;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// Get the assets we need to check once
|
|
1515
|
+
let assetsToCheck = Array.from(assetDependenciesMap.keys());
|
|
1516
|
+
|
|
1517
|
+
// Helper function to check if all assets from assetDependenciesMap are in referencedAssets
|
|
1518
|
+
const allAssetsReferenced = (): boolean =>
|
|
1519
|
+
assetsToCheck.length <= referencedAssets.size &&
|
|
1520
|
+
assetsToCheck.every((asset) => referencedAssets.has(asset));
|
|
1521
|
+
|
|
1522
|
+
// Do ONE traversal to check all remaining assets
|
|
1523
|
+
// We can share visitedBundles across all assets because we check every asset
|
|
1524
|
+
// against every visited bundle, which matches the original per-asset behavior
|
|
1525
|
+
let siblingBundles = new Set(
|
|
1526
|
+
this.getBundleGroupsContainingBundle(bundle).flatMap((bundleGroup) =>
|
|
1527
|
+
this.getBundlesInBundleGroup(bundleGroup, {includeInline: true}),
|
|
1528
|
+
),
|
|
1529
|
+
);
|
|
1530
|
+
|
|
1531
|
+
let visitedBundles: Set<Bundle> = new Set();
|
|
1532
|
+
|
|
1533
|
+
// Single traversal from all referencers
|
|
1534
|
+
for (let referencer of siblingBundles) {
|
|
1535
|
+
this.traverseBundles((descendant, _, actions) => {
|
|
1536
|
+
if (descendant.id === bundle.id) {
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
if (visitedBundles.has(descendant)) {
|
|
1541
|
+
actions.skipChildren();
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
visitedBundles.add(descendant);
|
|
1546
|
+
|
|
1547
|
+
if (
|
|
1548
|
+
descendant.type !== bundle.type ||
|
|
1549
|
+
fromEnvironmentId(descendant.env).context !==
|
|
1550
|
+
fromEnvironmentId(bundle.env).context
|
|
1551
|
+
) {
|
|
1552
|
+
// Don't skip children - they might be the right type!
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// Check ALL assets at once in this descendant bundle
|
|
1557
|
+
for (let [asset, dependencies] of assetDependenciesMap) {
|
|
1558
|
+
// Skip if already marked as referenced
|
|
1559
|
+
if (referencedAssets.has(asset)) {
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
// Check if this descendant bundle references the asset
|
|
1564
|
+
if (
|
|
1565
|
+
!this.bundleHasAsset(descendant, asset) &&
|
|
1566
|
+
dependencies.some((dependency) =>
|
|
1567
|
+
this.bundleHasDependency(descendant, dependency),
|
|
1568
|
+
)
|
|
1569
|
+
) {
|
|
1570
|
+
referencedAssets.add(asset);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// If all assets from assetDependenciesMap are now marked as referenced, we can stop early
|
|
1575
|
+
if (allAssetsReferenced()) {
|
|
1576
|
+
actions.stop();
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
}, referencer);
|
|
1580
|
+
|
|
1581
|
+
// If all assets from assetDependenciesMap are referenced, no need to check more sibling bundles
|
|
1582
|
+
if (allAssetsReferenced()) {
|
|
1583
|
+
break;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
return referencedAssets;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1438
1590
|
hasParentBundleOfType(bundle: Bundle, type: string): boolean {
|
|
1439
1591
|
let parents = this.getParentBundles(bundle);
|
|
1440
1592
|
return (
|
|
@@ -198,6 +198,27 @@ export default class BundleGraph<TBundle extends IBundle>
|
|
|
198
198
|
);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
isAssetReferencedFastCheck(bundle: IBundle, asset: IAsset): boolean | null {
|
|
202
|
+
return this.#graph.isAssetReferencedFastCheck(
|
|
203
|
+
bundleToInternalBundle(bundle),
|
|
204
|
+
assetToAssetValue(asset),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
getReferencedAssets(bundle: IBundle): Set<IAsset> {
|
|
209
|
+
let internalReferencedAssets = this.#graph.getReferencedAssets(
|
|
210
|
+
bundleToInternalBundle(bundle),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// Convert internal assets to public assets
|
|
214
|
+
let publicReferencedAssets = new Set<IAsset>();
|
|
215
|
+
for (let internalAsset of internalReferencedAssets) {
|
|
216
|
+
publicReferencedAssets.add(assetFromValue(internalAsset, this.#options));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return publicReferencedAssets;
|
|
220
|
+
}
|
|
221
|
+
|
|
201
222
|
hasParentBundleOfType(bundle: IBundle, type: string): boolean {
|
|
202
223
|
return this.#graph.hasParentBundleOfType(
|
|
203
224
|
bundleToInternalBundle(bundle),
|