@atlaspack/bundler-default 2.12.1-dev.3478 → 2.12.1-dev.3520

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.
@@ -0,0 +1,39 @@
1
+ // @flow strict-local
2
+ import type {Asset, Dependency, MutableBundleGraph} from '@atlaspack/types';
3
+ import nullthrows from 'nullthrows';
4
+
5
+ export function addJSMonolithBundle(
6
+ bundleGraph: MutableBundleGraph,
7
+ entryAsset: Asset,
8
+ entryDep: Dependency,
9
+ ): void {
10
+ let target = nullthrows(
11
+ entryDep.target,
12
+ 'Expected dependency to have a valid target',
13
+ );
14
+
15
+ // Create a single bundle to hold all JS assets
16
+ let bundle = bundleGraph.createBundle({entryAsset, target});
17
+
18
+ bundleGraph.traverse(
19
+ (node) => {
20
+ // JS assets can be added to the bundle, but the rest are ignored
21
+ if (node.type === 'asset' && node.value.type === 'js') {
22
+ bundleGraph.addAssetToBundle(node.value, bundle);
23
+ return;
24
+ }
25
+
26
+ if (node.type === 'dependency' && node.value.priority === 'lazy') {
27
+ // Any async dependencies need to be internalized into the bundle, and will
28
+ // be included by the asset check above
29
+ bundleGraph.internalizeAsyncDependency(bundle, node.value);
30
+ }
31
+ },
32
+ entryAsset,
33
+ {skipUnusedDependencies: true},
34
+ );
35
+
36
+ let bundleGroup = bundleGraph.createBundleGroup(entryDep, target);
37
+
38
+ bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);
39
+ }
@@ -0,0 +1,234 @@
1
+ // @flow strict-local
2
+
3
+ import {encodeJSONKeyComponent} from '@atlaspack/diagnostic';
4
+ import type {
5
+ Config,
6
+ PluginOptions,
7
+ BuildMode,
8
+ PluginLogger,
9
+ } from '@atlaspack/types';
10
+ import {type SchemaEntity, validateSchema} from '@atlaspack/utils';
11
+ import invariant from 'assert';
12
+
13
+ type Glob = string;
14
+
15
+ type ManualSharedBundles = Array<{|
16
+ name: string,
17
+ assets: Array<Glob>,
18
+ types?: Array<string>,
19
+ root?: string,
20
+ split?: number,
21
+ |}>;
22
+
23
+ type BaseBundlerConfig = {|
24
+ http?: number,
25
+ minBundles?: number,
26
+ minBundleSize?: number,
27
+ maxParallelRequests?: number,
28
+ disableSharedBundles?: boolean,
29
+ manualSharedBundles?: ManualSharedBundles,
30
+ loadConditionalBundlesInParallel?: boolean,
31
+ |};
32
+
33
+ type BundlerConfig = {|
34
+ [mode: BuildMode]: BaseBundlerConfig,
35
+ |} & BaseBundlerConfig;
36
+
37
+ export type ResolvedBundlerConfig = {|
38
+ minBundles: number,
39
+ minBundleSize: number,
40
+ maxParallelRequests: number,
41
+ projectRoot: string,
42
+ disableSharedBundles: boolean,
43
+ manualSharedBundles: ManualSharedBundles,
44
+ loadConditionalBundlesInParallel?: boolean,
45
+ |};
46
+
47
+ function resolveModeConfig(
48
+ config: BundlerConfig,
49
+ mode: BuildMode,
50
+ ): BaseBundlerConfig {
51
+ let generalConfig = {};
52
+ let modeConfig = {};
53
+
54
+ for (const key of Object.keys(config)) {
55
+ if (key === 'development' || key === 'production') {
56
+ if (key === mode) {
57
+ modeConfig = config[key];
58
+ }
59
+ } else {
60
+ generalConfig[key] = config[key];
61
+ }
62
+ }
63
+
64
+ // $FlowFixMe Not sure how to convince flow here...
65
+ return {
66
+ ...generalConfig,
67
+ ...modeConfig,
68
+ };
69
+ }
70
+
71
+ // Default options by http version.
72
+ const HTTP_OPTIONS = {
73
+ '1': {
74
+ minBundles: 1,
75
+ manualSharedBundles: [],
76
+ minBundleSize: 30000,
77
+ maxParallelRequests: 6,
78
+ disableSharedBundles: false,
79
+ },
80
+ '2': {
81
+ minBundles: 1,
82
+ manualSharedBundles: [],
83
+ minBundleSize: 20000,
84
+ maxParallelRequests: 25,
85
+ disableSharedBundles: false,
86
+ },
87
+ };
88
+
89
+ const CONFIG_SCHEMA: SchemaEntity = {
90
+ type: 'object',
91
+ properties: {
92
+ http: {
93
+ type: 'number',
94
+ enum: Object.keys(HTTP_OPTIONS).map((k) => Number(k)),
95
+ },
96
+ manualSharedBundles: {
97
+ type: 'array',
98
+ items: {
99
+ type: 'object',
100
+ properties: {
101
+ name: {
102
+ type: 'string',
103
+ },
104
+ assets: {
105
+ type: 'array',
106
+ items: {
107
+ type: 'string',
108
+ },
109
+ },
110
+ types: {
111
+ type: 'array',
112
+ items: {
113
+ type: 'string',
114
+ },
115
+ },
116
+ root: {
117
+ type: 'string',
118
+ },
119
+ split: {
120
+ type: 'number',
121
+ },
122
+ },
123
+ required: ['name', 'assets'],
124
+ additionalProperties: false,
125
+ },
126
+ },
127
+ minBundles: {
128
+ type: 'number',
129
+ },
130
+ minBundleSize: {
131
+ type: 'number',
132
+ },
133
+ maxParallelRequests: {
134
+ type: 'number',
135
+ },
136
+ disableSharedBundles: {
137
+ type: 'boolean',
138
+ },
139
+ },
140
+ additionalProperties: false,
141
+ };
142
+
143
+ export async function loadBundlerConfig(
144
+ config: Config,
145
+ options: PluginOptions,
146
+ logger: PluginLogger,
147
+ ): Promise<ResolvedBundlerConfig> {
148
+ let conf = await config.getConfig<BundlerConfig>([], {
149
+ packageKey: '@atlaspack/bundler-default',
150
+ });
151
+
152
+ if (!conf) {
153
+ const modDefault = {
154
+ ...HTTP_OPTIONS['2'],
155
+ projectRoot: options.projectRoot,
156
+ };
157
+ return modDefault;
158
+ }
159
+
160
+ invariant(conf?.contents != null);
161
+
162
+ let modeConfig = resolveModeConfig(conf.contents, options.mode);
163
+
164
+ // minBundles will be ignored if shared bundles are disabled
165
+ if (
166
+ modeConfig.minBundles != null &&
167
+ modeConfig.disableSharedBundles === true
168
+ ) {
169
+ logger.warn({
170
+ origin: '@atlaspack/bundler-default',
171
+ message: `The value of "${modeConfig.minBundles}" set for minBundles will not be used as shared bundles have been disabled`,
172
+ });
173
+ }
174
+
175
+ // minBundleSize will be ignored if shared bundles are disabled
176
+ if (
177
+ modeConfig.minBundleSize != null &&
178
+ modeConfig.disableSharedBundles === true
179
+ ) {
180
+ logger.warn({
181
+ origin: '@atlaspack/bundler-default',
182
+ message: `The value of "${modeConfig.minBundleSize}" set for minBundleSize will not be used as shared bundles have been disabled`,
183
+ });
184
+ }
185
+
186
+ // maxParallelRequests will be ignored if shared bundles are disabled
187
+ if (
188
+ modeConfig.maxParallelRequests != null &&
189
+ modeConfig.disableSharedBundles === true
190
+ ) {
191
+ logger.warn({
192
+ origin: '@atlaspack/bundler-default',
193
+ message: `The value of "${modeConfig.maxParallelRequests}" set for maxParallelRequests will not be used as shared bundles have been disabled`,
194
+ });
195
+ }
196
+
197
+ if (modeConfig.manualSharedBundles) {
198
+ let nameArray = modeConfig.manualSharedBundles.map((a) => a.name);
199
+ let nameSet = new Set(nameArray);
200
+ invariant(
201
+ nameSet.size == nameArray.length,
202
+ 'The name field must be unique for property manualSharedBundles',
203
+ );
204
+ }
205
+
206
+ validateSchema.diagnostic(
207
+ CONFIG_SCHEMA,
208
+ {
209
+ data: modeConfig,
210
+ source: await options.inputFS.readFile(conf.filePath, 'utf8'),
211
+ filePath: conf.filePath,
212
+ prependKey: `/${encodeJSONKeyComponent('@atlaspack/bundler-default')}`,
213
+ },
214
+ '@atlaspack/bundler-default',
215
+ 'Invalid config for @atlaspack/bundler-default',
216
+ );
217
+
218
+ let http = modeConfig.http ?? 2;
219
+ let defaults = HTTP_OPTIONS[http];
220
+
221
+ return {
222
+ minBundles: modeConfig.minBundles ?? defaults.minBundles,
223
+ minBundleSize: modeConfig.minBundleSize ?? defaults.minBundleSize,
224
+ maxParallelRequests:
225
+ modeConfig.maxParallelRequests ?? defaults.maxParallelRequests,
226
+ projectRoot: options.projectRoot,
227
+ disableSharedBundles:
228
+ modeConfig.disableSharedBundles ?? defaults.disableSharedBundles,
229
+ manualSharedBundles:
230
+ modeConfig.manualSharedBundles ?? defaults.manualSharedBundles,
231
+ loadConditionalBundlesInParallel:
232
+ modeConfig.loadConditionalBundlesInParallel,
233
+ };
234
+ }
@@ -0,0 +1,217 @@
1
+ // @flow strict-local
2
+
3
+ import {ALL_EDGE_TYPES, type NodeId} from '@atlaspack/graph';
4
+ import type {
5
+ Bundle as LegacyBundle,
6
+ BundleGroup,
7
+ MutableBundleGraph,
8
+ } from '@atlaspack/types';
9
+ import invariant from 'assert';
10
+ import nullthrows from 'nullthrows';
11
+
12
+ import type {Bundle, IdealGraph} from './idealGraph';
13
+
14
+ export function decorateLegacyGraph(
15
+ idealGraph: IdealGraph,
16
+ bundleGraph: MutableBundleGraph,
17
+ ): void {
18
+ let idealBundleToLegacyBundle: Map<Bundle, LegacyBundle> = new Map();
19
+
20
+ let {
21
+ bundleGraph: idealBundleGraph,
22
+ dependencyBundleGraph,
23
+ bundleGroupBundleIds,
24
+ manualAssetToBundle,
25
+ } = idealGraph;
26
+ let entryBundleToBundleGroup: Map<NodeId, BundleGroup> = new Map();
27
+ // Step Create Bundles: Create bundle groups, bundles, and shared bundles and add assets to them
28
+ for (let [bundleNodeId, idealBundle] of idealBundleGraph.nodes.entries()) {
29
+ if (!idealBundle || idealBundle === 'root') continue;
30
+ let entryAsset = idealBundle.mainEntryAsset;
31
+ let bundleGroup;
32
+ let bundle;
33
+
34
+ if (bundleGroupBundleIds.has(bundleNodeId)) {
35
+ invariant(
36
+ idealBundle.manualSharedBundle == null,
37
+ 'Processing a manualSharedBundle as a BundleGroup',
38
+ );
39
+ let dependencies = dependencyBundleGraph
40
+ .getNodeIdsConnectedTo(
41
+ dependencyBundleGraph.getNodeIdByContentKey(String(bundleNodeId)),
42
+ ALL_EDGE_TYPES,
43
+ )
44
+ .map((nodeId) => {
45
+ let dependency = nullthrows(dependencyBundleGraph.getNode(nodeId));
46
+ invariant(dependency.type === 'dependency');
47
+ return dependency.value;
48
+ });
49
+ invariant(
50
+ entryAsset != null,
51
+ 'Processing a bundleGroup with no entry asset',
52
+ );
53
+ for (let dependency of dependencies) {
54
+ bundleGroup = bundleGraph.createBundleGroup(
55
+ dependency,
56
+ idealBundle.target,
57
+ );
58
+ }
59
+ invariant(bundleGroup);
60
+ entryBundleToBundleGroup.set(bundleNodeId, bundleGroup);
61
+
62
+ bundle = nullthrows(
63
+ bundleGraph.createBundle({
64
+ entryAsset: nullthrows(entryAsset),
65
+ needsStableName: idealBundle.needsStableName,
66
+ bundleBehavior: idealBundle.bundleBehavior,
67
+ target: idealBundle.target,
68
+ manualSharedBundle: idealBundle.manualSharedBundle,
69
+ }),
70
+ );
71
+
72
+ bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);
73
+ } else if (
74
+ idealBundle.sourceBundles.size > 0 &&
75
+ !idealBundle.mainEntryAsset
76
+ ) {
77
+ let uniqueKey =
78
+ idealBundle.uniqueKey != null
79
+ ? idealBundle.uniqueKey
80
+ : [...idealBundle.assets].map((asset) => asset.id).join(',');
81
+
82
+ bundle = nullthrows(
83
+ bundleGraph.createBundle({
84
+ uniqueKey,
85
+ needsStableName: idealBundle.needsStableName,
86
+ bundleBehavior: idealBundle.bundleBehavior,
87
+ type: idealBundle.type,
88
+ target: idealBundle.target,
89
+ env: idealBundle.env,
90
+ manualSharedBundle: idealBundle.manualSharedBundle,
91
+ }),
92
+ );
93
+ } else if (idealBundle.uniqueKey != null) {
94
+ bundle = nullthrows(
95
+ bundleGraph.createBundle({
96
+ uniqueKey: idealBundle.uniqueKey,
97
+ needsStableName: idealBundle.needsStableName,
98
+ bundleBehavior: idealBundle.bundleBehavior,
99
+ type: idealBundle.type,
100
+ target: idealBundle.target,
101
+ env: idealBundle.env,
102
+ manualSharedBundle: idealBundle.manualSharedBundle,
103
+ }),
104
+ );
105
+ } else {
106
+ invariant(entryAsset != null);
107
+ bundle = nullthrows(
108
+ bundleGraph.createBundle({
109
+ entryAsset,
110
+ needsStableName: idealBundle.needsStableName,
111
+ bundleBehavior: idealBundle.bundleBehavior,
112
+ target: idealBundle.target,
113
+ manualSharedBundle: idealBundle.manualSharedBundle,
114
+ }),
115
+ );
116
+ }
117
+
118
+ idealBundleToLegacyBundle.set(idealBundle, bundle);
119
+
120
+ for (let asset of idealBundle.assets) {
121
+ bundleGraph.addAssetToBundle(asset, bundle);
122
+ }
123
+ }
124
+ // Step Internalization: Internalize dependencies for bundles
125
+ for (let idealBundle of idealBundleGraph.nodes) {
126
+ if (!idealBundle || idealBundle === 'root') continue;
127
+ let bundle = nullthrows(idealBundleToLegacyBundle.get(idealBundle));
128
+ if (idealBundle.internalizedAssets) {
129
+ idealBundle.internalizedAssets.forEach((internalized) => {
130
+ let incomingDeps = bundleGraph.getIncomingDependencies(
131
+ idealGraph.assets[internalized],
132
+ );
133
+ for (let incomingDep of incomingDeps) {
134
+ if (
135
+ incomingDep.priority === 'lazy' &&
136
+ incomingDep.specifierType !== 'url' &&
137
+ bundle.hasDependency(incomingDep)
138
+ ) {
139
+ bundleGraph.internalizeAsyncDependency(bundle, incomingDep);
140
+ }
141
+ }
142
+ });
143
+ }
144
+ }
145
+ // Manual Shared Bundles
146
+ // NOTE: This only works under the assumption that manual shared bundles would have
147
+ // always already been loaded before the bundle that requires internalization.
148
+ for (let manualSharedAsset of manualAssetToBundle.keys()) {
149
+ let incomingDeps = bundleGraph.getIncomingDependencies(manualSharedAsset);
150
+ for (let incomingDep of incomingDeps) {
151
+ if (
152
+ incomingDep.priority === 'lazy' &&
153
+ incomingDep.specifierType !== 'url'
154
+ ) {
155
+ let bundles = bundleGraph.getBundlesWithDependency(incomingDep);
156
+ for (let bundle of bundles) {
157
+ bundleGraph.internalizeAsyncDependency(bundle, incomingDep);
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ // Step Add to BundleGroups: Add bundles to their bundle groups
164
+ idealBundleGraph.traverse((nodeId, _, actions) => {
165
+ let node = idealBundleGraph.getNode(nodeId);
166
+ if (node === 'root') {
167
+ return;
168
+ }
169
+ actions.skipChildren();
170
+
171
+ let outboundNodeIds = idealBundleGraph.getNodeIdsConnectedFrom(nodeId);
172
+ let entryBundle = nullthrows(idealBundleGraph.getNode(nodeId));
173
+ invariant(entryBundle !== 'root');
174
+ let legacyEntryBundle = nullthrows(
175
+ idealBundleToLegacyBundle.get(entryBundle),
176
+ );
177
+
178
+ for (let id of outboundNodeIds) {
179
+ let siblingBundle = nullthrows(idealBundleGraph.getNode(id));
180
+ invariant(siblingBundle !== 'root');
181
+ let legacySiblingBundle = nullthrows(
182
+ idealBundleToLegacyBundle.get(siblingBundle),
183
+ );
184
+ bundleGraph.createBundleReference(legacyEntryBundle, legacySiblingBundle);
185
+ }
186
+ });
187
+
188
+ // Step References: Add references to all bundles
189
+ for (let [asset, references] of idealGraph.assetReference) {
190
+ for (let [dependency, bundle] of references) {
191
+ let legacyBundle = nullthrows(idealBundleToLegacyBundle.get(bundle));
192
+ bundleGraph.createAssetReference(dependency, asset, legacyBundle);
193
+ }
194
+ }
195
+
196
+ for (let {from, to} of idealBundleGraph.getAllEdges()) {
197
+ let sourceBundle = nullthrows(idealBundleGraph.getNode(from));
198
+ if (sourceBundle === 'root') {
199
+ continue;
200
+ }
201
+ invariant(sourceBundle !== 'root');
202
+
203
+ let legacySourceBundle = nullthrows(
204
+ idealBundleToLegacyBundle.get(sourceBundle),
205
+ );
206
+
207
+ let targetBundle = nullthrows(idealBundleGraph.getNode(to));
208
+ if (targetBundle === 'root') {
209
+ continue;
210
+ }
211
+ invariant(targetBundle !== 'root');
212
+ let legacyTargetBundle = nullthrows(
213
+ idealBundleToLegacyBundle.get(targetBundle),
214
+ );
215
+ bundleGraph.createBundleReference(legacySourceBundle, legacyTargetBundle);
216
+ }
217
+ }