@atlaspack/bundler-default 2.14.5-canary.136 → 2.14.5-canary.138

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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @atlaspack/bundler-default
2
2
 
3
+ ## 3.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#721](https://github.com/atlassian-labs/atlaspack/pull/721) [`069de47`](https://github.com/atlassian-labs/atlaspack/commit/069de478e64fb5889f6f2ce023eb510782767fbd) Thanks [@benjervis](https://github.com/benjervis)! - Add support for bundle merging based on `webpackChunkName` comments.
8
+
9
+ Adding a `webpackChunkName` comment to an import will allow the bundler to merge multiple imports into a single bundle.
10
+
11
+ e.g.:
12
+
13
+ ```ts
14
+ import(/* webpackChunkName: "my-chunk" */ './my-module');
15
+ import(/* webpackChunkName: "my-chunk" */ './another-module');
16
+ ```
17
+
18
+ This can be enabled with the feature flag `supportWebpackChunkName`.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies [[`069de47`](https://github.com/atlassian-labs/atlaspack/commit/069de478e64fb5889f6f2ce023eb510782767fbd)]:
23
+ - @atlaspack/feature-flags@2.20.0
24
+ - @atlaspack/graph@3.5.10
25
+ - @atlaspack/utils@2.17.3
26
+ - @atlaspack/plugin@2.14.21
27
+
3
28
  ## 3.1.2
4
29
 
5
30
  ### Patch Changes
@@ -42,11 +42,13 @@ function decorateLegacyGraph(idealGraph, bundleGraph) {
42
42
  bundleGroupBundleIds,
43
43
  manualAssetToBundle
44
44
  } = idealGraph;
45
+ // This line can be deleted once supportWebpackChunkName feature flag is removed.
45
46
  let entryBundleToBundleGroup = new Map();
46
47
  // Step Create Bundles: Create bundle groups, bundles, and shared bundles and add assets to them
47
48
  for (let [bundleNodeId, idealBundle] of idealBundleGraph.nodes.entries()) {
48
49
  if (!idealBundle || idealBundle === 'root') continue;
49
50
  let entryAsset = idealBundle.mainEntryAsset;
51
+ // This line can be deleted once supportWebpackChunkName feature flag is removed.
50
52
  let bundleGroup;
51
53
  let bundle;
52
54
  if (bundleGroupBundleIds.has(bundleNodeId)) {
@@ -57,19 +59,33 @@ function decorateLegacyGraph(idealGraph, bundleGraph) {
57
59
  return dependency.value;
58
60
  });
59
61
  (0, _assert().default)(entryAsset != null, 'Processing a bundleGroup with no entry asset');
62
+ let bundleGroups = new Map();
60
63
  for (let dependency of dependencies) {
61
64
  bundleGroup = bundleGraph.createBundleGroup(dependency, idealBundle.target);
65
+ bundleGroups.set(bundleGroup.entryAssetId, bundleGroup);
66
+ }
67
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
68
+ (0, _assert().default)(bundleGroups.size > 0, 'No bundle groups created');
69
+ } else {
70
+ (0, _assert().default)(bundleGroup);
71
+ entryBundleToBundleGroup.set(bundleNodeId, bundleGroup);
62
72
  }
63
- (0, _assert().default)(bundleGroup);
64
- entryBundleToBundleGroup.set(bundleNodeId, bundleGroup);
65
73
  bundle = (0, _nullthrows().default)(bundleGraph.createBundle({
66
74
  entryAsset: (0, _nullthrows().default)(entryAsset),
75
+ bundleRoots: Array.from(idealBundle.bundleRoots),
67
76
  needsStableName: idealBundle.needsStableName,
68
77
  bundleBehavior: idealBundle.bundleBehavior,
69
78
  target: idealBundle.target,
70
79
  manualSharedBundle: idealBundle.manualSharedBundle
71
80
  }));
72
- bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);
81
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
82
+ for (let bundleGroup of bundleGroups.values()) {
83
+ bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);
84
+ }
85
+ } else {
86
+ (0, _assert().default)(bundleGroup);
87
+ bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);
88
+ }
73
89
  } else if (idealBundle.sourceBundles.size > 0 && !idealBundle.mainEntryAsset) {
74
90
  let uniqueKey = idealBundle.uniqueKey != null ? idealBundle.uniqueKey : [...idealBundle.assets].map(asset => asset.id).join(',');
75
91
  bundle = (0, _nullthrows().default)(bundleGraph.createBundle({
@@ -95,6 +111,7 @@ function decorateLegacyGraph(idealGraph, bundleGraph) {
95
111
  (0, _assert().default)(entryAsset != null);
96
112
  bundle = (0, _nullthrows().default)(bundleGraph.createBundle({
97
113
  entryAsset,
114
+ bundleRoots: Array.from(idealBundle.bundleRoots),
98
115
  needsStableName: idealBundle.needsStableName,
99
116
  bundleBehavior: idealBundle.bundleBehavior,
100
117
  target: idealBundle.target,
package/lib/idealGraph.js CHANGED
@@ -65,6 +65,11 @@ const idealBundleGraphEdges = exports.idealBundleGraphEdges = Object.freeze({
65
65
  // which mutates the assetGraph into the bundleGraph we would
66
66
  // expect from default bundler
67
67
 
68
+ function isNonRootBundle(bundle, message) {
69
+ let existingBundle = (0, _nullthrows().default)(bundle, message);
70
+ (0, _assert().default)(existingBundle !== 'root', "Bundle cannot be 'root'");
71
+ return existingBundle;
72
+ }
68
73
  function createIdealGraph(assetGraph, config, entries, logger) {
69
74
  var _config$sharedBundleM;
70
75
  // Asset to the bundle and group it's an entry of
@@ -796,6 +801,8 @@ function createIdealGraph(assetGraph, config, entries, logger) {
796
801
  }
797
802
  }
798
803
  let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
804
+ let modifiedSourceBundles = new Set();
805
+
799
806
  // Step split manual shared bundles for those that have the "split" property set
800
807
  let remainderMap = new (_utils().DefaultMap)(() => []);
801
808
  for (let id of manualSharedMap.values()) {
@@ -859,6 +866,56 @@ function createIdealGraph(assetGraph, config, entries, logger) {
859
866
  }
860
867
  }
861
868
  }
869
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
870
+ // Merge webpack chunk name bundles
871
+ let chunkNameBundles = new (_utils().DefaultMap)(() => new Set());
872
+ for (let [nodeId, node] of dependencyBundleGraph.nodes.entries()) {
873
+ var _bundleNode$value$mai;
874
+ // meta.chunkName is set by the Rust transformer, so we just need to find
875
+ // bundles that have a chunkName set.
876
+ if (!node || node.type !== 'dependency' || node.value.meta.chunkName == null) {
877
+ continue;
878
+ }
879
+ let connectedBundles = dependencyBundleGraph.getNodeIdsConnectedFrom(nodeId, dependencyPriorityEdges[node.value.priority]);
880
+ if (connectedBundles.length === 0) {
881
+ continue;
882
+ }
883
+ (0, _assert().default)(connectedBundles.length === 1, 'Expected webpackChunkName dependency to be connected to no more than one bundle');
884
+ let bundleId = connectedBundles[0];
885
+ let bundleNode = dependencyBundleGraph.getNode(bundleId);
886
+ (0, _assert().default)(bundleNode != null && bundleNode.type === 'bundle');
887
+
888
+ // If a bundle does not have a main entry asset, it's somehow just a
889
+ // shared bundle, and will be merged/deleted by other means.
890
+ if (bundleNode.value.mainEntryAsset == null) {
891
+ continue;
892
+ }
893
+ let bundleNodeId = null;
894
+ let mainEntryAssetId = (_bundleNode$value$mai = bundleNode.value.mainEntryAsset) === null || _bundleNode$value$mai === void 0 ? void 0 : _bundleNode$value$mai.id;
895
+ if (mainEntryAssetId != null) {
896
+ bundleNodeId = bundles.get(mainEntryAssetId);
897
+ }
898
+ if (bundleNodeId == null) {
899
+ continue;
900
+ }
901
+ chunkNameBundles.get(node.value.meta.chunkName)
902
+ // DependencyBundleGraph uses content keys as node ids, so we can use that
903
+ // to get the bundle id.
904
+ .add(bundleNodeId);
905
+ }
906
+ for (let [chunkName, bundleIds] of chunkNameBundles.entries()) {
907
+ // The `[request]` placeholder is not yet supported
908
+ if (bundleIds.size <= 1 || typeof chunkName === 'string' && chunkName.includes('[request]')) {
909
+ continue; // Nothing to merge
910
+ }
911
+
912
+ // Merge all bundles with the same chunk name into the first one.
913
+ let [firstBundleId, ...rest] = Array.from(bundleIds);
914
+ for (let bundleId of rest) {
915
+ mergeBundles(firstBundleId, bundleId);
916
+ }
917
+ }
918
+ }
862
919
 
863
920
  // Step merge shared bundles that meet the overlap threshold
864
921
  // This step is skipped by default as the threshold defaults to 1
@@ -877,7 +934,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
877
934
  }
878
935
 
879
936
  // Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
880
- let modifiedSourceBundles = new Set();
937
+
881
938
  if (config.disableSharedBundles === false) {
882
939
  for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
883
940
  // Find shared bundles in this bundle group.
@@ -962,10 +1019,10 @@ function createIdealGraph(assetGraph, config, entries, logger) {
962
1019
  }
963
1020
  }
964
1021
  }
965
- function mergeBundles(bundleGraph, bundleToKeepId, bundleToRemoveId, assetReference) {
966
- let bundleToKeep = (0, _nullthrows().default)(bundleGraph.getNode(bundleToKeepId));
967
- let bundleToRemove = (0, _nullthrows().default)(bundleGraph.getNode(bundleToRemoveId));
968
- (0, _assert().default)(bundleToKeep !== 'root' && bundleToRemove !== 'root');
1022
+ function mergeBundles(bundleToKeepId, bundleToRemoveId) {
1023
+ let bundleToKeep = isNonRootBundle(bundleGraph.getNode(bundleToKeepId), `Bundle ${bundleToKeepId} not found`);
1024
+ let bundleToRemove = isNonRootBundle(bundleGraph.getNode(bundleToRemoveId), `Bundle ${bundleToRemoveId} not found`);
1025
+ modifiedSourceBundles.add(bundleToKeep);
969
1026
  for (let asset of bundleToRemove.assets) {
970
1027
  bundleToKeep.assets.add(asset);
971
1028
  bundleToKeep.size += asset.stats.size;
@@ -974,8 +1031,20 @@ function createIdealGraph(assetGraph, config, entries, logger) {
974
1031
  }
975
1032
 
976
1033
  // Merge any internalized assets
977
- (0, _assert().default)(bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets, 'All shared bundles should have internalized assets');
978
- bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
1034
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
1035
+ if (bundleToKeep.internalizedAssets != null) {
1036
+ if (bundleToRemove.internalizedAssets != null) {
1037
+ bundleToKeep.internalizedAssets.intersect(bundleToRemove.internalizedAssets);
1038
+ } else {
1039
+ bundleToKeep.internalizedAssets.clear();
1040
+ }
1041
+ }
1042
+ } else {
1043
+ (0, _assert().default)(bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets, 'All shared bundles should have internalized assets');
1044
+ bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
1045
+ }
1046
+
1047
+ // Merge and clean up source bundles
979
1048
  for (let sourceBundleId of bundleToRemove.sourceBundles) {
980
1049
  if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
981
1050
  continue;
@@ -983,6 +1052,66 @@ function createIdealGraph(assetGraph, config, entries, logger) {
983
1052
  bundleToKeep.sourceBundles.add(sourceBundleId);
984
1053
  bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
985
1054
  }
1055
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
1056
+ bundleToKeep.sourceBundles.delete(bundleToRemoveId);
1057
+ for (let bundle of bundleGraph.getNodeIdsConnectedFrom(bundleToRemoveId)) {
1058
+ let bundleNode = (0, _nullthrows().default)(bundleGraph.getNode(bundle));
1059
+ if (bundleNode === 'root') {
1060
+ continue;
1061
+ }
1062
+
1063
+ // If the bundle is a source bundle, add it to the bundle to keep
1064
+ if (bundleNode.sourceBundles.has(bundleToRemoveId)) {
1065
+ bundleNode.sourceBundles.add(bundleToKeepId);
1066
+ bundleNode.sourceBundles.delete(bundleToRemoveId);
1067
+ bundleGraph.addEdge(bundleToKeepId, bundle);
1068
+ }
1069
+ }
1070
+
1071
+ // Merge bundle roots
1072
+ for (let bundleRoot of bundleToRemove.bundleRoots) {
1073
+ bundleToKeep.bundleRoots.add(bundleRoot);
1074
+ }
1075
+ if (bundleToRemove.mainEntryAsset != null) {
1076
+ (0, _assert().default)(bundleToKeep.mainEntryAsset != null);
1077
+
1078
+ // Merge the bundles in bundle group
1079
+ let bundlesInRemoveBundleGroup = getBundlesForBundleGroup(bundleToRemoveId);
1080
+ for (let bundleIdInGroup of bundlesInRemoveBundleGroup) {
1081
+ if (bundleIdInGroup === bundleToRemoveId) {
1082
+ continue;
1083
+ }
1084
+ bundleGraph.addEdge(bundleToKeepId, bundleIdInGroup);
1085
+ }
1086
+
1087
+ // Remove old bundle group
1088
+ bundleGroupBundleIds.delete(bundleToRemoveId);
1089
+
1090
+ // Clean up bundle roots
1091
+ let bundleRootToRemoveNodeId = (0, _nullthrows().default)(assetToBundleRootNodeId.get((0, _nullthrows().default)(bundleToRemove.mainEntryAsset)));
1092
+ let bundleRootToKeepNodeId = (0, _nullthrows().default)(assetToBundleRootNodeId.get((0, _nullthrows().default)(bundleToKeep.mainEntryAsset)));
1093
+ for (let nodeId of bundleRootGraph.getNodeIdsConnectedTo(bundleRootToRemoveNodeId)) {
1094
+ bundleRootGraph.addEdge(nodeId, bundleRootToKeepNodeId);
1095
+ bundleRootGraph.removeEdge(nodeId, bundleRootToRemoveNodeId);
1096
+ }
1097
+ for (let nodeId of bundleRootGraph.getNodeIdsConnectedFrom(bundleRootToRemoveNodeId)) {
1098
+ bundleRootGraph.addEdge(bundleRootToKeepNodeId, nodeId);
1099
+ bundleRootGraph.removeEdge(bundleRootToRemoveNodeId, nodeId);
1100
+ }
1101
+ bundleRoots.set((0, _nullthrows().default)(bundleToRemove.mainEntryAsset), [bundleToKeepId, bundleToKeepId]);
1102
+
1103
+ // Merge dependency bundle graph
1104
+ for (let dependencyNodeId of dependencyBundleGraph.getNodeIdsConnectedTo(dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)), _graph().ALL_EDGE_TYPES)) {
1105
+ let dependencyNode = (0, _nullthrows().default)(dependencyBundleGraph.getNode(dependencyNodeId));
1106
+ (0, _assert().default)(dependencyNode.type === 'dependency');
1107
+
1108
+ // Add dependency to the bundle to keep
1109
+ dependencyBundleGraph.addEdge(dependencyNodeId, dependencyBundleGraph.getNodeIdByContentKey(String(bundleToKeepId)), dependencyPriorityEdges[dependencyNode.value.priority]);
1110
+ // Remove dependency from the bundle to remove
1111
+ dependencyBundleGraph.removeEdge(dependencyNodeId, dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)), dependencyPriorityEdges[dependencyNode.value.priority]);
1112
+ }
1113
+ }
1114
+ }
986
1115
  bundleGraph.removeNode(bundleToRemoveId);
987
1116
  }
988
1117
  function mergeOverlapBundles(mergeConfig) {
@@ -1017,11 +1146,16 @@ function createIdealGraph(assetGraph, config, entries, logger) {
1017
1146
  })
1018
1147
  };
1019
1148
  }));
1149
+ let mergedBundles = new Set();
1020
1150
  for (let cluster of clusters) {
1021
1151
  let [mergeTarget, ...rest] = cluster;
1022
1152
  for (let bundleIdToMerge of rest) {
1023
- mergeBundles(bundleGraph, mergeTarget, bundleIdToMerge, assetReference);
1153
+ mergeBundles(mergeTarget, bundleIdToMerge);
1024
1154
  }
1155
+ mergedBundles.add(mergeTarget);
1156
+ }
1157
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
1158
+ return mergedBundles;
1025
1159
  }
1026
1160
  }
1027
1161
  function getBigIntFromContentKey(contentKey) {
@@ -1122,6 +1256,7 @@ function createBundle(opts) {
1122
1256
  bundleBehavior: opts.bundleBehavior,
1123
1257
  env: (0, _nullthrows().default)(opts.env),
1124
1258
  mainEntryAsset: null,
1259
+ bundleRoots: new Set(),
1125
1260
  manualSharedBundle: opts.manualSharedBundle,
1126
1261
  needsStableName: Boolean(opts.needsStableName),
1127
1262
  size: 0,
@@ -1137,6 +1272,7 @@ function createBundle(opts) {
1137
1272
  bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
1138
1273
  env: opts.env ?? asset.env,
1139
1274
  mainEntryAsset: asset,
1275
+ bundleRoots: new Set([asset]),
1140
1276
  manualSharedBundle: opts.manualSharedBundle,
1141
1277
  needsStableName: Boolean(opts.needsStableName),
1142
1278
  size: asset.stats.size,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaspack/bundler-default",
3
- "version": "2.14.5-canary.136+d90b6d22d",
3
+ "version": "2.14.5-canary.138+eda07caaf",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "type": "commonjs",
6
6
  "publishConfig": {
@@ -16,14 +16,14 @@
16
16
  "node": ">= 16.0.0"
17
17
  },
18
18
  "dependencies": {
19
- "@atlaspack/diagnostic": "2.14.1-canary.204+d90b6d22d",
20
- "@atlaspack/feature-flags": "2.14.1-canary.204+d90b6d22d",
21
- "@atlaspack/graph": "3.4.1-canary.204+d90b6d22d",
22
- "@atlaspack/plugin": "2.14.5-canary.136+d90b6d22d",
23
- "@atlaspack/rust": "3.2.1-canary.136+d90b6d22d",
24
- "@atlaspack/utils": "2.14.5-canary.136+d90b6d22d",
19
+ "@atlaspack/diagnostic": "2.14.1-canary.206+eda07caaf",
20
+ "@atlaspack/feature-flags": "2.14.1-canary.206+eda07caaf",
21
+ "@atlaspack/graph": "3.4.1-canary.206+eda07caaf",
22
+ "@atlaspack/plugin": "2.14.5-canary.138+eda07caaf",
23
+ "@atlaspack/rust": "3.2.1-canary.138+eda07caaf",
24
+ "@atlaspack/utils": "2.14.5-canary.138+eda07caaf",
25
25
  "many-keys-map": "^1.0.3",
26
26
  "nullthrows": "^1.1.1"
27
27
  },
28
- "gitHead": "d90b6d22db4f44b6831c33933ddd31d4a77800a6"
28
+ "gitHead": "eda07caafd2ebb814bbdbfd0ec12fa63124e213f"
29
29
  }
@@ -25,11 +25,13 @@ export function decorateLegacyGraph(
25
25
  bundleGroupBundleIds,
26
26
  manualAssetToBundle,
27
27
  } = idealGraph;
28
+ // This line can be deleted once supportWebpackChunkName feature flag is removed.
28
29
  let entryBundleToBundleGroup: Map<NodeId, BundleGroup> = new Map();
29
30
  // Step Create Bundles: Create bundle groups, bundles, and shared bundles and add assets to them
30
31
  for (let [bundleNodeId, idealBundle] of idealBundleGraph.nodes.entries()) {
31
32
  if (!idealBundle || idealBundle === 'root') continue;
32
33
  let entryAsset = idealBundle.mainEntryAsset;
34
+ // This line can be deleted once supportWebpackChunkName feature flag is removed.
33
35
  let bundleGroup;
34
36
  let bundle;
35
37
 
@@ -52,18 +54,26 @@ export function decorateLegacyGraph(
52
54
  entryAsset != null,
53
55
  'Processing a bundleGroup with no entry asset',
54
56
  );
57
+
58
+ let bundleGroups = new Map();
55
59
  for (let dependency of dependencies) {
56
60
  bundleGroup = bundleGraph.createBundleGroup(
57
61
  dependency,
58
62
  idealBundle.target,
59
63
  );
64
+ bundleGroups.set(bundleGroup.entryAssetId, bundleGroup);
65
+ }
66
+ if (getFeatureFlag('supportWebpackChunkName')) {
67
+ invariant(bundleGroups.size > 0, 'No bundle groups created');
68
+ } else {
69
+ invariant(bundleGroup);
70
+ entryBundleToBundleGroup.set(bundleNodeId, bundleGroup);
60
71
  }
61
- invariant(bundleGroup);
62
- entryBundleToBundleGroup.set(bundleNodeId, bundleGroup);
63
72
 
64
73
  bundle = nullthrows(
65
74
  bundleGraph.createBundle({
66
75
  entryAsset: nullthrows(entryAsset),
76
+ bundleRoots: Array.from(idealBundle.bundleRoots),
67
77
  needsStableName: idealBundle.needsStableName,
68
78
  bundleBehavior: idealBundle.bundleBehavior,
69
79
  target: idealBundle.target,
@@ -71,7 +81,14 @@ export function decorateLegacyGraph(
71
81
  }),
72
82
  );
73
83
 
74
- bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);
84
+ if (getFeatureFlag('supportWebpackChunkName')) {
85
+ for (let bundleGroup of bundleGroups.values()) {
86
+ bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);
87
+ }
88
+ } else {
89
+ invariant(bundleGroup);
90
+ bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);
91
+ }
75
92
  } else if (
76
93
  idealBundle.sourceBundles.size > 0 &&
77
94
  !idealBundle.mainEntryAsset
@@ -109,6 +126,7 @@ export function decorateLegacyGraph(
109
126
  bundle = nullthrows(
110
127
  bundleGraph.createBundle({
111
128
  entryAsset,
129
+ bundleRoots: Array.from(idealBundle.bundleRoots),
112
130
  needsStableName: idealBundle.needsStableName,
113
131
  bundleBehavior: idealBundle.bundleBehavior,
114
132
  target: idealBundle.target,
package/src/idealGraph.js CHANGED
@@ -36,6 +36,7 @@ export type Bundle = {|
36
36
  bundleBehavior?: ?BundleBehavior,
37
37
  needsStableName: boolean,
38
38
  mainEntryAsset: ?Asset,
39
+ bundleRoots: Set<Asset>,
39
40
  size: number,
40
41
  sourceBundles: Set<NodeId>,
41
42
  target: Target,
@@ -85,6 +86,16 @@ export type IdealGraph = {|
85
86
  manualAssetToBundle: Map<Asset, NodeId>,
86
87
  |};
87
88
 
89
+ function isNonRootBundle(
90
+ bundle?: Bundle | 'root' | null,
91
+ message?: string,
92
+ ): Bundle {
93
+ let existingBundle = nullthrows(bundle, message);
94
+ invariant(existingBundle !== 'root', "Bundle cannot be 'root'");
95
+
96
+ return existingBundle;
97
+ }
98
+
88
99
  export function createIdealGraph(
89
100
  assetGraph: MutableBundleGraph,
90
101
  config: ResolvedBundlerConfig,
@@ -1074,6 +1085,8 @@ export function createIdealGraph(
1074
1085
  }
1075
1086
 
1076
1087
  let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
1088
+ let modifiedSourceBundles = new Set<Bundle>();
1089
+
1077
1090
  // Step split manual shared bundles for those that have the "split" property set
1078
1091
  let remainderMap = new DefaultMap(() => []);
1079
1092
  for (let id of manualSharedMap.values()) {
@@ -1146,6 +1159,79 @@ export function createIdealGraph(
1146
1159
  }
1147
1160
  }
1148
1161
 
1162
+ if (getFeatureFlag('supportWebpackChunkName')) {
1163
+ // Merge webpack chunk name bundles
1164
+ let chunkNameBundles = new DefaultMap(() => new Set());
1165
+ for (let [nodeId, node] of dependencyBundleGraph.nodes.entries()) {
1166
+ // meta.chunkName is set by the Rust transformer, so we just need to find
1167
+ // bundles that have a chunkName set.
1168
+ if (
1169
+ !node ||
1170
+ node.type !== 'dependency' ||
1171
+ node.value.meta.chunkName == null
1172
+ ) {
1173
+ continue;
1174
+ }
1175
+
1176
+ let connectedBundles = dependencyBundleGraph.getNodeIdsConnectedFrom(
1177
+ nodeId,
1178
+ dependencyPriorityEdges[node.value.priority],
1179
+ );
1180
+
1181
+ if (connectedBundles.length === 0) {
1182
+ continue;
1183
+ }
1184
+
1185
+ invariant(
1186
+ connectedBundles.length === 1,
1187
+ 'Expected webpackChunkName dependency to be connected to no more than one bundle',
1188
+ );
1189
+
1190
+ let bundleId = connectedBundles[0];
1191
+ let bundleNode = dependencyBundleGraph.getNode(bundleId);
1192
+ invariant(bundleNode != null && bundleNode.type === 'bundle');
1193
+
1194
+ // If a bundle does not have a main entry asset, it's somehow just a
1195
+ // shared bundle, and will be merged/deleted by other means.
1196
+ if (bundleNode.value.mainEntryAsset == null) {
1197
+ continue;
1198
+ }
1199
+
1200
+ let bundleNodeId = null;
1201
+ let mainEntryAssetId = bundleNode.value.mainEntryAsset?.id;
1202
+
1203
+ if (mainEntryAssetId != null) {
1204
+ bundleNodeId = bundles.get(mainEntryAssetId);
1205
+ }
1206
+
1207
+ if (bundleNodeId == null) {
1208
+ continue;
1209
+ }
1210
+
1211
+ chunkNameBundles
1212
+ .get(node.value.meta.chunkName)
1213
+ // DependencyBundleGraph uses content keys as node ids, so we can use that
1214
+ // to get the bundle id.
1215
+ .add(bundleNodeId);
1216
+ }
1217
+
1218
+ for (let [chunkName, bundleIds] of chunkNameBundles.entries()) {
1219
+ // The `[request]` placeholder is not yet supported
1220
+ if (
1221
+ bundleIds.size <= 1 ||
1222
+ (typeof chunkName === 'string' && chunkName.includes('[request]'))
1223
+ ) {
1224
+ continue; // Nothing to merge
1225
+ }
1226
+
1227
+ // Merge all bundles with the same chunk name into the first one.
1228
+ let [firstBundleId, ...rest] = Array.from(bundleIds);
1229
+ for (let bundleId of rest) {
1230
+ mergeBundles(firstBundleId, bundleId);
1231
+ }
1232
+ }
1233
+ }
1234
+
1149
1235
  // Step merge shared bundles that meet the overlap threshold
1150
1236
  // This step is skipped by default as the threshold defaults to 1
1151
1237
  if (config.sharedBundleMerge && config.sharedBundleMerge.length > 0) {
@@ -1168,7 +1254,6 @@ export function createIdealGraph(
1168
1254
  }
1169
1255
 
1170
1256
  // Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
1171
- let modifiedSourceBundles = new Set();
1172
1257
 
1173
1258
  if (config.disableSharedBundles === false) {
1174
1259
  for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
@@ -1273,15 +1358,16 @@ export function createIdealGraph(
1273
1358
  }
1274
1359
  }
1275
1360
 
1276
- function mergeBundles(
1277
- bundleGraph: IdealBundleGraph,
1278
- bundleToKeepId: NodeId,
1279
- bundleToRemoveId: NodeId,
1280
- assetReference: DefaultMap<Asset, Array<[Dependency, Bundle]>>,
1281
- ) {
1282
- let bundleToKeep = nullthrows(bundleGraph.getNode(bundleToKeepId));
1283
- let bundleToRemove = nullthrows(bundleGraph.getNode(bundleToRemoveId));
1284
- invariant(bundleToKeep !== 'root' && bundleToRemove !== 'root');
1361
+ function mergeBundles(bundleToKeepId: NodeId, bundleToRemoveId: NodeId) {
1362
+ let bundleToKeep = isNonRootBundle(
1363
+ bundleGraph.getNode(bundleToKeepId),
1364
+ `Bundle ${bundleToKeepId} not found`,
1365
+ );
1366
+ let bundleToRemove = isNonRootBundle(
1367
+ bundleGraph.getNode(bundleToRemoveId),
1368
+ `Bundle ${bundleToRemoveId} not found`,
1369
+ );
1370
+ modifiedSourceBundles.add(bundleToKeep);
1285
1371
  for (let asset of bundleToRemove.assets) {
1286
1372
  bundleToKeep.assets.add(asset);
1287
1373
  bundleToKeep.size += asset.stats.size;
@@ -1296,12 +1382,25 @@ export function createIdealGraph(
1296
1382
  }
1297
1383
 
1298
1384
  // Merge any internalized assets
1299
- invariant(
1300
- bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets,
1301
- 'All shared bundles should have internalized assets',
1302
- );
1303
- bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
1385
+ if (getFeatureFlag('supportWebpackChunkName')) {
1386
+ if (bundleToKeep.internalizedAssets != null) {
1387
+ if (bundleToRemove.internalizedAssets != null) {
1388
+ bundleToKeep.internalizedAssets.intersect(
1389
+ bundleToRemove.internalizedAssets,
1390
+ );
1391
+ } else {
1392
+ bundleToKeep.internalizedAssets.clear();
1393
+ }
1394
+ }
1395
+ } else {
1396
+ invariant(
1397
+ bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets,
1398
+ 'All shared bundles should have internalized assets',
1399
+ );
1400
+ bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
1401
+ }
1304
1402
 
1403
+ // Merge and clean up source bundles
1305
1404
  for (let sourceBundleId of bundleToRemove.sourceBundles) {
1306
1405
  if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
1307
1406
  continue;
@@ -1311,6 +1410,104 @@ export function createIdealGraph(
1311
1410
  bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
1312
1411
  }
1313
1412
 
1413
+ if (getFeatureFlag('supportWebpackChunkName')) {
1414
+ bundleToKeep.sourceBundles.delete(bundleToRemoveId);
1415
+
1416
+ for (let bundle of bundleGraph.getNodeIdsConnectedFrom(
1417
+ bundleToRemoveId,
1418
+ )) {
1419
+ let bundleNode = nullthrows(bundleGraph.getNode(bundle));
1420
+ if (bundleNode === 'root') {
1421
+ continue;
1422
+ }
1423
+
1424
+ // If the bundle is a source bundle, add it to the bundle to keep
1425
+ if (bundleNode.sourceBundles.has(bundleToRemoveId)) {
1426
+ bundleNode.sourceBundles.add(bundleToKeepId);
1427
+ bundleNode.sourceBundles.delete(bundleToRemoveId);
1428
+ bundleGraph.addEdge(bundleToKeepId, bundle);
1429
+ }
1430
+ }
1431
+
1432
+ // Merge bundle roots
1433
+ for (let bundleRoot of bundleToRemove.bundleRoots) {
1434
+ bundleToKeep.bundleRoots.add(bundleRoot);
1435
+ }
1436
+
1437
+ if (bundleToRemove.mainEntryAsset != null) {
1438
+ invariant(bundleToKeep.mainEntryAsset != null);
1439
+
1440
+ // Merge the bundles in bundle group
1441
+ let bundlesInRemoveBundleGroup =
1442
+ getBundlesForBundleGroup(bundleToRemoveId);
1443
+
1444
+ for (let bundleIdInGroup of bundlesInRemoveBundleGroup) {
1445
+ if (bundleIdInGroup === bundleToRemoveId) {
1446
+ continue;
1447
+ }
1448
+ bundleGraph.addEdge(bundleToKeepId, bundleIdInGroup);
1449
+ }
1450
+
1451
+ // Remove old bundle group
1452
+ bundleGroupBundleIds.delete(bundleToRemoveId);
1453
+
1454
+ // Clean up bundle roots
1455
+ let bundleRootToRemoveNodeId = nullthrows(
1456
+ assetToBundleRootNodeId.get(
1457
+ nullthrows(bundleToRemove.mainEntryAsset),
1458
+ ),
1459
+ );
1460
+ let bundleRootToKeepNodeId = nullthrows(
1461
+ assetToBundleRootNodeId.get(nullthrows(bundleToKeep.mainEntryAsset)),
1462
+ );
1463
+
1464
+ for (let nodeId of bundleRootGraph.getNodeIdsConnectedTo(
1465
+ bundleRootToRemoveNodeId,
1466
+ )) {
1467
+ bundleRootGraph.addEdge(nodeId, bundleRootToKeepNodeId);
1468
+ bundleRootGraph.removeEdge(nodeId, bundleRootToRemoveNodeId);
1469
+ }
1470
+
1471
+ for (let nodeId of bundleRootGraph.getNodeIdsConnectedFrom(
1472
+ bundleRootToRemoveNodeId,
1473
+ )) {
1474
+ bundleRootGraph.addEdge(bundleRootToKeepNodeId, nodeId);
1475
+ bundleRootGraph.removeEdge(bundleRootToRemoveNodeId, nodeId);
1476
+ }
1477
+
1478
+ bundleRoots.set(nullthrows(bundleToRemove.mainEntryAsset), [
1479
+ bundleToKeepId,
1480
+ bundleToKeepId,
1481
+ ]);
1482
+
1483
+ // Merge dependency bundle graph
1484
+ for (let dependencyNodeId of dependencyBundleGraph.getNodeIdsConnectedTo(
1485
+ dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)),
1486
+ ALL_EDGE_TYPES,
1487
+ )) {
1488
+ let dependencyNode = nullthrows(
1489
+ dependencyBundleGraph.getNode(dependencyNodeId),
1490
+ );
1491
+ invariant(dependencyNode.type === 'dependency');
1492
+
1493
+ // Add dependency to the bundle to keep
1494
+ dependencyBundleGraph.addEdge(
1495
+ dependencyNodeId,
1496
+ dependencyBundleGraph.getNodeIdByContentKey(String(bundleToKeepId)),
1497
+ dependencyPriorityEdges[dependencyNode.value.priority],
1498
+ );
1499
+ // Remove dependency from the bundle to remove
1500
+ dependencyBundleGraph.removeEdge(
1501
+ dependencyNodeId,
1502
+ dependencyBundleGraph.getNodeIdByContentKey(
1503
+ String(bundleToRemoveId),
1504
+ ),
1505
+ dependencyPriorityEdges[dependencyNode.value.priority],
1506
+ );
1507
+ }
1508
+ }
1509
+ }
1510
+
1314
1511
  bundleGraph.removeNode(bundleToRemoveId);
1315
1512
  }
1316
1513
 
@@ -1360,12 +1557,20 @@ export function createIdealGraph(
1360
1557
  })),
1361
1558
  );
1362
1559
 
1560
+ let mergedBundles = new Set();
1561
+
1363
1562
  for (let cluster of clusters) {
1364
1563
  let [mergeTarget, ...rest] = cluster;
1365
1564
 
1366
1565
  for (let bundleIdToMerge of rest) {
1367
- mergeBundles(bundleGraph, mergeTarget, bundleIdToMerge, assetReference);
1566
+ mergeBundles(mergeTarget, bundleIdToMerge);
1368
1567
  }
1568
+
1569
+ mergedBundles.add(mergeTarget);
1570
+ }
1571
+
1572
+ if (getFeatureFlag('supportWebpackChunkName')) {
1573
+ return mergedBundles;
1369
1574
  }
1370
1575
  }
1371
1576
 
@@ -1498,6 +1703,7 @@ function createBundle(opts: {|
1498
1703
  bundleBehavior: opts.bundleBehavior,
1499
1704
  env: nullthrows(opts.env),
1500
1705
  mainEntryAsset: null,
1706
+ bundleRoots: new Set(),
1501
1707
  manualSharedBundle: opts.manualSharedBundle,
1502
1708
  needsStableName: Boolean(opts.needsStableName),
1503
1709
  size: 0,
@@ -1514,6 +1720,7 @@ function createBundle(opts: {|
1514
1720
  bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
1515
1721
  env: opts.env ?? asset.env,
1516
1722
  mainEntryAsset: asset,
1723
+ bundleRoots: new Set([asset]),
1517
1724
  manualSharedBundle: opts.manualSharedBundle,
1518
1725
  needsStableName: Boolean(opts.needsStableName),
1519
1726
  size: asset.stats.size,