"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; function _graph() { const data = require("@parcel/graph"); _graph = function () { return data; }; return data; } function _assert() { const data = _interopRequireDefault(require("assert")); _assert = function () { return data; }; return data; } function _plugin() { const data = require("@parcel/plugin"); _plugin = function () { return data; }; return data; } function _utils() { const data = require("@parcel/utils"); _utils = function () { return data; }; return data; } function _nullthrows() { const data = _interopRequireDefault(require("nullthrows")); _nullthrows = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _diagnostic() { const data = require("@parcel/diagnostic"); _diagnostic = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // Default options by http version. const HTTP_OPTIONS = { '1': { minBundles: 1, manualSharedBundles: [], minBundleSize: 30000, maxParallelRequests: 6, disableSharedBundles: false }, '2': { minBundles: 1, manualSharedBundles: [], minBundleSize: 20000, maxParallelRequests: 25, disableSharedBundles: false } }; /* BundleRoot - An asset that is the main entry of a Bundle. */ const dependencyPriorityEdges = { sync: 1, parallel: 2, lazy: 3 }; // IdealGraph is the structure we will pass to decorate, // which mutates the assetGraph into the bundleGraph we would // expect from default bundler /** * * The Bundler works by creating an IdealGraph, which contains a BundleGraph that models bundles * connected to other bundles by what references them, and thus models BundleGroups. * * First, we enter `bundle({bundleGraph, config})`. Here, "bundleGraph" is actually just the * assetGraph turned into a type `MutableBundleGraph`, which will then be mutated in decorate, * and turned into what we expect the bundleGraph to be as per the old (default) bundler structure * & what the rest of Parcel expects a BundleGraph to be. * * `bundle({bundleGraph, config})` First gets a Mapping of target to entries, In most cases there is * only one target, and one or more entries. (Targets are pertinent in monorepos or projects where you * will have two or more distDirs, or output folders.) Then calls create IdealGraph and Decorate per target. * */ var _default = exports.default = new (_plugin().Bundler)({ loadConfig({ config, options, logger }) { return loadBundlerConfig(config, options, logger); }, bundle({ bundleGraph, config, logger }) { let targetMap = getEntryByTarget(bundleGraph); // Organize entries by target output folder/ distDir let graphs = []; for (let entries of targetMap.values()) { // Create separate bundleGraphs per distDir graphs.push(createIdealGraph(bundleGraph, config, entries, logger)); } for (let g of graphs) { decorateLegacyGraph(g, bundleGraph); //mutate original graph } }, optimize() {} }); function decorateLegacyGraph(idealGraph, bundleGraph) { let idealBundleToLegacyBundle = new Map(); let { bundleGraph: idealBundleGraph, dependencyBundleGraph, bundleGroupBundleIds, manualAssetToBundle } = idealGraph; let entryBundleToBundleGroup = new Map(); // Step Create Bundles: Create bundle groups, bundles, and shared bundles and add assets to them for (let [bundleNodeId, idealBundle] of idealBundleGraph.nodes.entries()) { if (!idealBundle || idealBundle === 'root') continue; let entryAsset = idealBundle.mainEntryAsset; let bundleGroups = []; let bundleGroup; let bundle; if (bundleGroupBundleIds.has(bundleNodeId)) { (0, _assert().default)(idealBundle.manualSharedBundle == null, 'Unstable Manual Shared Bundle feature is processing a manualSharedBundle as a BundleGroup'); let dependencies = dependencyBundleGraph.getNodeIdsConnectedTo(dependencyBundleGraph.getNodeIdByContentKey(String(bundleNodeId)), _graph().ALL_EDGE_TYPES).map(nodeId => { let dependency = (0, _nullthrows().default)(dependencyBundleGraph.getNode(nodeId)); (0, _assert().default)(dependency.type === 'dependency'); return dependency.value; }); (0, _assert().default)(entryAsset != null, 'Processing a bundleGroup with no entry asset'); for (let dependency of dependencies) { bundleGroup = bundleGraph.createBundleGroup(dependency, idealBundle.target); bundleGroups.push(bundleGroup); } (0, _assert().default)(bundleGroup); entryBundleToBundleGroup.set(bundleNodeId, bundleGroup); bundle = (0, _nullthrows().default)(bundleGraph.createBundle({ entryAsset: (0, _nullthrows().default)(entryAsset), needsStableName: idealBundle.needsStableName, bundleBehavior: idealBundle.bundleBehavior, target: idealBundle.target, manualSharedBundle: idealBundle.manualSharedBundle })); bundleGraph.addBundleToBundleGroup(bundle, bundleGroup); } else if (idealBundle.sourceBundles.size > 0 && !idealBundle.mainEntryAsset) { let uniqueKey = idealBundle.uniqueKey != null ? idealBundle.uniqueKey : [...idealBundle.assets].map(asset => asset.id).join(','); bundle = (0, _nullthrows().default)(bundleGraph.createBundle({ uniqueKey, needsStableName: idealBundle.needsStableName, bundleBehavior: idealBundle.bundleBehavior, type: idealBundle.type, target: idealBundle.target, env: idealBundle.env, manualSharedBundle: idealBundle.manualSharedBundle })); } else if (idealBundle.uniqueKey != null) { bundle = (0, _nullthrows().default)(bundleGraph.createBundle({ uniqueKey: idealBundle.uniqueKey, needsStableName: idealBundle.needsStableName, bundleBehavior: idealBundle.bundleBehavior, type: idealBundle.type, target: idealBundle.target, env: idealBundle.env, manualSharedBundle: idealBundle.manualSharedBundle })); } else { (0, _assert().default)(entryAsset != null); bundle = (0, _nullthrows().default)(bundleGraph.createBundle({ entryAsset, needsStableName: idealBundle.needsStableName, bundleBehavior: idealBundle.bundleBehavior, target: idealBundle.target, manualSharedBundle: idealBundle.manualSharedBundle })); } idealBundleToLegacyBundle.set(idealBundle, bundle); for (let asset of idealBundle.assets) { bundleGraph.addAssetToBundle(asset, bundle); } } // Step Internalization: Internalize dependencies for bundles for (let idealBundle of idealBundleGraph.nodes) { if (!idealBundle || idealBundle === 'root') continue; let bundle = (0, _nullthrows().default)(idealBundleToLegacyBundle.get(idealBundle)); if (idealBundle.internalizedAssets) { idealBundle.internalizedAssets.forEach(internalized => { let incomingDeps = bundleGraph.getIncomingDependencies(idealGraph.assets[internalized]); for (let incomingDep of incomingDeps) { if (incomingDep.priority === 'lazy' && incomingDep.specifierType !== 'url' && bundle.hasDependency(incomingDep)) { bundleGraph.internalizeAsyncDependency(bundle, incomingDep); } } }); } } // Unstable Manual Shared Bundles // NOTE: This only works under the assumption that manual shared bundles would have // always already been loaded before the bundle that requires internalization. for (let manualSharedAsset of manualAssetToBundle.keys()) { let incomingDeps = bundleGraph.getIncomingDependencies(manualSharedAsset); for (let incomingDep of incomingDeps) { if (incomingDep.priority === 'lazy' && incomingDep.specifierType !== 'url') { let bundles = bundleGraph.getBundlesWithDependency(incomingDep); for (let bundle of bundles) { bundleGraph.internalizeAsyncDependency(bundle, incomingDep); } } } } // Step Add to BundleGroups: Add bundles to their bundle groups idealBundleGraph.traverse((nodeId, _, actions) => { let node = idealBundleGraph.getNode(nodeId); if (node === 'root') { return; } actions.skipChildren(); let outboundNodeIds = idealBundleGraph.getNodeIdsConnectedFrom(nodeId); let entryBundle = (0, _nullthrows().default)(idealBundleGraph.getNode(nodeId)); (0, _assert().default)(entryBundle !== 'root'); let legacyEntryBundle = (0, _nullthrows().default)(idealBundleToLegacyBundle.get(entryBundle)); for (let id of outboundNodeIds) { let siblingBundle = (0, _nullthrows().default)(idealBundleGraph.getNode(id)); (0, _assert().default)(siblingBundle !== 'root'); let legacySiblingBundle = (0, _nullthrows().default)(idealBundleToLegacyBundle.get(siblingBundle)); bundleGraph.createBundleReference(legacyEntryBundle, legacySiblingBundle); } }); // Step References: Add references to all bundles for (let [asset, references] of idealGraph.assetReference) { for (let [dependency, bundle] of references) { let legacyBundle = (0, _nullthrows().default)(idealBundleToLegacyBundle.get(bundle)); bundleGraph.createAssetReference(dependency, asset, legacyBundle); } } for (let { from, to } of idealBundleGraph.getAllEdges()) { let sourceBundle = (0, _nullthrows().default)(idealBundleGraph.getNode(from)); if (sourceBundle === 'root') { continue; } (0, _assert().default)(sourceBundle !== 'root'); let legacySourceBundle = (0, _nullthrows().default)(idealBundleToLegacyBundle.get(sourceBundle)); let targetBundle = (0, _nullthrows().default)(idealBundleGraph.getNode(to)); if (targetBundle === 'root') { continue; } (0, _assert().default)(targetBundle !== 'root'); let legacyTargetBundle = (0, _nullthrows().default)(idealBundleToLegacyBundle.get(targetBundle)); bundleGraph.createBundleReference(legacySourceBundle, legacyTargetBundle); } } function createIdealGraph(assetGraph, config, entries, logger) { // Asset to the bundle and group it's an entry of let bundleRoots = new Map(); let bundles = new Map(); let dependencyBundleGraph = new (_graph().ContentGraph)(); let assetReference = new (_utils().DefaultMap)(() => []); // A Graph of Bundles and a root node (dummy string), which models only Bundles, and connections to their // referencing Bundle. There are no actual BundleGroup nodes, just bundles that take on that role. let bundleGraph = new (_graph().Graph)(); let stack = []; let bundleRootEdgeTypes = { parallel: 1, lazy: 2 }; // Graph that models bundleRoots, with parallel & async deps only to inform reachability let bundleRootGraph = new (_graph().Graph)(); let assetToBundleRootNodeId = new Map(); let bundleGroupBundleIds = new Set(); let bundleGraphRootNodeId = (0, _nullthrows().default)(bundleGraph.addNode('root')); bundleGraph.setRootNodeId(bundleGraphRootNodeId); // Step Create Entry Bundles for (let [asset, dependency] of entries) { let bundle = createBundle({ asset, target: (0, _nullthrows().default)(dependency.target), needsStableName: dependency.isEntry }); let nodeId = bundleGraph.addNode(bundle); bundles.set(asset.id, nodeId); bundleRoots.set(asset, [nodeId, nodeId]); bundleGraph.addEdge(bundleGraphRootNodeId, nodeId); dependencyBundleGraph.addEdge(dependencyBundleGraph.addNodeByContentKeyIfNeeded(dependency.id, { value: dependency, type: 'dependency' }), dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(nodeId), { value: bundle, type: 'bundle' }), dependencyPriorityEdges[dependency.priority]); bundleGroupBundleIds.add(nodeId); } let assets = []; let assetToIndex = new Map(); //Manual is a map of the user-given name to the bundle node Id that corresponds to ALL the assets that match any glob in that user-specified array let manualSharedMap = new Map(); // May need a map to be able to look up NON- bundle root assets which need special case instructions // Use this when placing assets into bundles, to avoid duplication let manualAssetToBundle = new Map(); let { manualAssetToConfig, constantModuleToMSB } = function makeManualAssetToConfigLookup() { let manualAssetToConfig = new Map(); let constantModuleToMSB = new (_utils().DefaultMap)(() => []); if (config.manualSharedBundles.length === 0) { return { manualAssetToConfig, constantModuleToMSB }; } let parentsToConfig = new (_utils().DefaultMap)(() => []); for (let c of config.manualSharedBundles) { if (c.root != null) { parentsToConfig.get(_path().default.join(config.projectRoot, c.root)).push(c); } } let numParentsToFind = parentsToConfig.size; let configToParentAsset = new Map(); assetGraph.traverse((node, _, actions) => { if (node.type === 'asset' && parentsToConfig.has(node.value.filePath)) { for (let c of parentsToConfig.get(node.value.filePath)) { configToParentAsset.set(c, node.value); } numParentsToFind--; if (numParentsToFind === 0) { // If we've found all parents we can stop traversal actions.stop(); } } }); // Process in reverse order so earlier configs take precedence for (let c of config.manualSharedBundles.reverse()) { if (c.root != null && !configToParentAsset.has(c)) { logger.warn({ origin: '@parcel/bundler-default', message: `Manual shared bundle "${c.name}" skipped, no root asset found` }); continue; } let parentAsset = configToParentAsset.get(c); let assetRegexes = c.assets.map(glob => (0, _utils().globToRegex)(glob)); assetGraph.traverse((node, _, actions) => { if (node.type === 'asset' && (!Array.isArray(c.types) || c.types.includes(node.value.type))) { // +1 accounts for leading slash let projectRelativePath = node.value.filePath.slice(config.projectRoot.length + 1); if (!assetRegexes.some(regex => regex.test(projectRelativePath))) { return; } // We track all matching MSB's for constant modules as they are never duplicated // and need to be assigned to all matching bundles if (node.value.meta.isConstantModule === true) { constantModuleToMSB.get(node.value).push(c); } manualAssetToConfig.set(node.value, c); return; } if (node.type === 'dependency' && node.value.priority === 'lazy' && parentAsset) { // Don't walk past the bundle group assets actions.skipChildren(); } }, parentAsset); } return { manualAssetToConfig, constantModuleToMSB }; }(); let manualBundleToInternalizedAsset = new (_utils().DefaultMap)(() => []); /** * Step Create Bundles: Traverse the assetGraph (aka MutableBundleGraph) and create bundles * for asset type changes, parallel, inline, and async or lazy dependencies, * adding only that asset to each bundle, not its entire subgraph. */ assetGraph.traverse({ enter(node, context, actions) { if (node.type === 'asset') { if ((context === null || context === void 0 ? void 0 : context.type) === 'dependency' && context !== null && context !== void 0 && context.value.isEntry && !entries.has(node.value)) { // Skip whole subtrees of other targets by skipping those entries actions.skipChildren(); return node; } assetToIndex.set(node.value, assets.length); assets.push(node.value); let bundleIdTuple = bundleRoots.get(node.value); if (bundleIdTuple && bundleIdTuple[0] === bundleIdTuple[1]) { // Push to the stack (only) when a new bundle is created stack.push([node.value, bundleIdTuple[0]]); } else if (bundleIdTuple) { // Otherwise, push on the last bundle that marks the start of a BundleGroup stack.push([node.value, stack[stack.length - 1][1]]); } } else if (node.type === 'dependency') { if (context == null) { return node; } let dependency = node.value; (0, _assert().default)((context === null || context === void 0 ? void 0 : context.type) === 'asset'); let assets = assetGraph.getDependencyAssets(dependency); if (assets.length === 0) { return node; } for (let childAsset of assets) { let bundleId = bundles.get(childAsset.id); let bundle; // MSB Step 1: Match glob on filepath and type for any asset let manualSharedBundleKey; let manualSharedObject = manualAssetToConfig.get(childAsset); if (manualSharedObject) { // MSB Step 2: Generate a key for which to look up this manual bundle with manualSharedBundleKey = manualSharedObject.name + ',' + childAsset.type; } if ( // MSB Step 3: If a bundle for these globs already exsits, use it manualSharedBundleKey != null && manualSharedMap.has(manualSharedBundleKey)) { bundleId = (0, _nullthrows().default)(manualSharedMap.get(manualSharedBundleKey)); } if (dependency.priority === 'lazy' || childAsset.bundleBehavior === 'isolated' // An isolated Dependency, or Bundle must contain all assets it needs to load. ) { if (bundleId == null) { var _dependency$bundleBeh; let firstBundleGroup = (0, _nullthrows().default)(bundleGraph.getNode(stack[0][1])); (0, _assert().default)(firstBundleGroup !== 'root'); bundle = createBundle({ asset: childAsset, target: firstBundleGroup.target, needsStableName: dependency.bundleBehavior === 'inline' || childAsset.bundleBehavior === 'inline' ? false : dependency.isEntry || dependency.needsStableName, bundleBehavior: (_dependency$bundleBeh = dependency.bundleBehavior) !== null && _dependency$bundleBeh !== void 0 ? _dependency$bundleBeh : childAsset.bundleBehavior }); bundleId = bundleGraph.addNode(bundle); bundles.set(childAsset.id, bundleId); bundleRoots.set(childAsset, [bundleId, bundleId]); bundleGroupBundleIds.add(bundleId); bundleGraph.addEdge(bundleGraphRootNodeId, bundleId); if (manualSharedObject) { // MSB Step 4: If this was the first instance of a match, mark mainAsset for internalization // since MSBs should not have main entry assets manualBundleToInternalizedAsset.get(bundleId).push(childAsset); } } else { bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId)); (0, _assert().default)(bundle !== 'root'); if ( // If this dependency requests isolated, but the bundle is not, // make the bundle isolated for all uses. dependency.bundleBehavior === 'isolated' && bundle.bundleBehavior == null) { bundle.bundleBehavior = dependency.bundleBehavior; } } dependencyBundleGraph.addEdge(dependencyBundleGraph.addNodeByContentKeyIfNeeded(dependency.id, { value: dependency, type: 'dependency' }), dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), { value: bundle, type: 'bundle' }), dependencyPriorityEdges[dependency.priority]); } else if (dependency.priority === 'parallel' || childAsset.bundleBehavior === 'inline') { // The referencing bundleRoot is the root of a Bundle that first brings in another bundle (essentially the FIRST parent of a bundle, this may or may not be a bundleGroup) let [referencingBundleRoot, bundleGroupNodeId] = (0, _nullthrows().default)(stack[stack.length - 1]); let bundleGroup = (0, _nullthrows().default)(bundleGraph.getNode(bundleGroupNodeId)); (0, _assert().default)(bundleGroup !== 'root'); let referencingBundleId = (0, _nullthrows().default)(bundleRoots.get(referencingBundleRoot))[0]; let referencingBundle = (0, _nullthrows().default)(bundleGraph.getNode(referencingBundleId)); (0, _assert().default)(referencingBundle !== 'root'); if (bundleId == null) { var _dependency$bundleBeh2; bundle = createBundle({ // Bundles created from type changes shouldn't have an entry asset. asset: childAsset, type: childAsset.type, env: childAsset.env, bundleBehavior: (_dependency$bundleBeh2 = dependency.bundleBehavior) !== null && _dependency$bundleBeh2 !== void 0 ? _dependency$bundleBeh2 : childAsset.bundleBehavior, target: referencingBundle.target, needsStableName: childAsset.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inline' || dependency.priority === 'parallel' && !dependency.needsStableName ? false : referencingBundle.needsStableName }); bundleId = bundleGraph.addNode(bundle); } else { bundle = bundleGraph.getNode(bundleId); (0, _assert().default)(bundle != null && bundle !== 'root'); if ( // If this dependency requests isolated, but the bundle is not, // make the bundle isolated for all uses. dependency.bundleBehavior === 'isolated' && bundle.bundleBehavior == null) { bundle.bundleBehavior = dependency.bundleBehavior; } } bundles.set(childAsset.id, bundleId); // A bundle can belong to multiple bundlegroups, all the bundle groups of it's // ancestors, and all async and entry bundles before it are "bundle groups" // TODO: We may need to track bundles to all bundleGroups it belongs to in the future. bundleRoots.set(childAsset, [bundleId, bundleGroupNodeId]); bundleGraph.addEdge(referencingBundleId, bundleId); if (bundleId != bundleGroupNodeId) { dependencyBundleGraph.addEdge(dependencyBundleGraph.addNodeByContentKeyIfNeeded(dependency.id, { value: dependency, type: 'dependency' }), dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), { value: bundle, type: 'bundle' }), dependencyPriorityEdges.parallel); } assetReference.get(childAsset).push([dependency, bundle]); } else { bundleId = null; } if (manualSharedObject && bundleId != null) { // MSB Step 5: At this point we've either created or found an existing MSB bundle // add the asset if it doesn't already have it and set key (0, _assert().default)(bundle !== 'root' && bundle != null && bundleId != null); manualAssetToBundle.set(childAsset, bundleId); if (!bundle.assets.has(childAsset)) { // Add asset to bundle bundle.assets.add(childAsset); bundle.size += childAsset.stats.size; } bundles.set(childAsset.id, bundleId); bundleRoots.set(childAsset, [bundleId, bundleId]); (0, _assert().default)(manualSharedBundleKey != null); // Ensure we set key to BundleId so the next glob match uses the appropriate bundle if (!manualSharedMap.has(manualSharedBundleKey)) { manualSharedMap.set(manualSharedBundleKey, bundleId); } bundle.manualSharedBundle = manualSharedObject.name; bundle.uniqueKey = manualSharedObject.name + childAsset.type; } } } return node; }, exit(node) { var _stack; if (((_stack = stack[stack.length - 1]) === null || _stack === void 0 ? void 0 : _stack[0]) === node.value) { stack.pop(); } } }, null, { skipUnusedDependencies: true }); // Strip MSBs of entries for (let [nodeId, internalizedAssets] of manualBundleToInternalizedAsset.entries()) { let bundle = bundleGraph.getNode(nodeId); (0, _assert().default)(bundle != null && bundle !== 'root'); if (!bundle.internalizedAssets) { bundle.internalizedAssets = new (_graph().BitSet)(assets.length); } for (let asset of internalizedAssets) { bundle.internalizedAssets.add((0, _nullthrows().default)(assetToIndex.get(asset))); } bundle.mainEntryAsset = null; bundleGroupBundleIds.delete(nodeId); // manual bundles can now act as shared, non-bundle group, should they be non-bundleRoots as well? } /** * Step Determine Reachability: Determine reachability for every asset from each bundleRoot. * This is later used to determine which bundles to place each asset in. We build up two * structures, one traversal each. ReachableRoots to store sync relationships, * and bundleRootGraph to store the minimal availability through `parallel` and `async` relationships. * The two graphs, are used to build up ancestorAssets, a structure which holds all availability by * all means for each asset. */ let rootNodeId = bundleRootGraph.addNode(-1); bundleRootGraph.setRootNodeId(rootNodeId); for (let [root] of bundleRoots) { let nodeId = bundleRootGraph.addNode((0, _nullthrows().default)(assetToIndex.get(root))); assetToBundleRootNodeId.set(root, nodeId); if (entries.has(root)) { bundleRootGraph.addEdge(rootNodeId, nodeId); } } // reachableRoots is an array of bit sets for each asset. Each bit set // indicates which bundle roots are reachable from that asset synchronously. let reachableRoots = []; for (let i = 0; i < assets.length; i++) { reachableRoots.push(new (_graph().BitSet)(bundleRootGraph.nodes.length)); } // reachableAssets is the inverse mapping of reachableRoots. For each bundle root, // it contains a bit set that indicates which assets are reachable from it. let reachableAssets = []; // ancestorAssets maps bundle roots to the set of all assets available to it at runtime, // including in earlier parallel bundles. These are intersected through all paths to // the bundle to ensure that the available assets are always present no matter in which // order the bundles are loaded. let ancestorAssets = []; let inlineConstantDeps = new (_utils().DefaultMap)(() => new Set()); for (let [bundleRootId, assetId] of bundleRootGraph.nodes.entries()) { let reachable = new (_graph().BitSet)(assets.length); reachableAssets.push(reachable); ancestorAssets.push(null); if (bundleRootId == rootNodeId || assetId == null) continue; // Add sync relationships to ReachableRoots let root = assets[assetId]; assetGraph.traverse((node, _, actions) => { if (node.value === root) { return; } if (node.type === 'dependency') { let dependency = node.value; if (dependency.priority !== 'sync' && dependencyBundleGraph.hasContentKey(dependency.id)) { let assets = assetGraph.getDependencyAssets(dependency); if (assets.length === 0) { return; } (0, _assert().default)(assets.length === 1); let bundleRoot = assets[0]; let bundle = (0, _nullthrows().default)(bundleGraph.getNode((0, _nullthrows().default)(bundles.get(bundleRoot.id)))); if (bundle !== 'root' && bundle.bundleBehavior == null && !bundle.env.isIsolated() && bundle.env.context === root.env.context) { bundleRootGraph.addEdge(bundleRootId, (0, _nullthrows().default)(assetToBundleRootNodeId.get(bundleRoot)), dependency.priority === 'parallel' ? bundleRootEdgeTypes.parallel : bundleRootEdgeTypes.lazy); } } if (dependency.priority !== 'sync') { actions.skipChildren(); } return; } //asset node type let asset = node.value; if (asset.bundleBehavior != null) { actions.skipChildren(); return; } let assetIndex = (0, _nullthrows().default)(assetToIndex.get(node.value)); reachable.add(assetIndex); reachableRoots[assetIndex].add(bundleRootId); if (asset.meta.isConstantModule === true) { let parents = assetGraph.getIncomingDependencies(asset).map(dep => (0, _nullthrows().default)(assetGraph.getAssetWithDependency(dep))); for (let parent of parents) { inlineConstantDeps.get(parent).add(asset); } } return; }, root, { skipUnusedDependencies: true }); } for (let entry of entries.keys()) { // Initialize an empty set of ancestors available to entries let entryId = (0, _nullthrows().default)(assetToBundleRootNodeId.get(entry)); ancestorAssets[entryId] = new (_graph().BitSet)(assets.length); } // Step Determine Availability // Visit nodes in a topological order, visiting parent nodes before child nodes. // This allows us to construct an understanding of which assets will already be // loaded and available when a bundle runs, by pushing available assets downwards and // computing the intersection of assets available through all possible paths to a bundle. // We call this structure ancestorAssets, a Map that tracks a bundleRoot, // to all assets available to it (meaning they will exist guaranteed when the bundleRoot is loaded) // The topological sort ensures all parents are visited before the node we want to process. for (let nodeId of bundleRootGraph.topoSort(_graph().ALL_EDGE_TYPES)) { if (nodeId === rootNodeId) continue; const bundleRoot = assets[(0, _nullthrows().default)(bundleRootGraph.getNode(nodeId))]; let bundleGroupId = (0, _nullthrows().default)(bundleRoots.get(bundleRoot))[1]; // At a BundleRoot, we access it's available assets (via ancestorAssets), // and add to that all assets within the bundles in that BundleGroup. // This set is available to all bundles in a particular bundleGroup because // bundleGroups are just bundles loaded at the same time. However it is // not true that a bundle's available assets = all assets of all the bundleGroups // it belongs to. It's the intersection of those sets. let available; if (bundleRoot.bundleBehavior === 'isolated') { available = new (_graph().BitSet)(assets.length); } else { available = (0, _nullthrows().default)(ancestorAssets[nodeId]).clone(); for (let bundleIdInGroup of [bundleGroupId, ...bundleGraph.getNodeIdsConnectedFrom(bundleGroupId)]) { let bundleInGroup = (0, _nullthrows().default)(bundleGraph.getNode(bundleIdInGroup)); (0, _assert().default)(bundleInGroup !== 'root'); if (bundleInGroup.bundleBehavior != null) { continue; } for (let bundleRoot of bundleInGroup.assets) { // Assets directly connected to current bundleRoot available.add((0, _nullthrows().default)(assetToIndex.get(bundleRoot))); available.union(reachableAssets[(0, _nullthrows().default)(assetToBundleRootNodeId.get(bundleRoot))]); } } } // Now that we have bundleGroup availability, we will propagate that down to all the children // of this bundleGroup. For a child, we also must maintain parallel availability. If it has // parallel siblings that come before it, those, too, are available to it. Add those parallel // available assets to the set of available assets for this child as well. let children = bundleRootGraph.getNodeIdsConnectedFrom(nodeId, _graph().ALL_EDGE_TYPES); let parallelAvailability = new (_graph().BitSet)(assets.length); for (let childId of children) { let assetId = (0, _nullthrows().default)(bundleRootGraph.getNode(childId)); let child = assets[assetId]; let bundleBehavior = getBundleFromBundleRoot(child).bundleBehavior; if (bundleBehavior != null) { continue; } let isParallel = bundleRootGraph.hasEdge(nodeId, childId, bundleRootEdgeTypes.parallel); // Most of the time, a child will have many parent bundleGroups, // so the next time we peek at a child from another parent, we will // intersect the availability built there with the previously computed // availability. this ensures no matter which bundleGroup loads a particular bundle, // it will only assume availability of assets it has under any circumstance const childAvailableAssets = ancestorAssets[childId]; let currentChildAvailable = isParallel ? _graph().BitSet.union(parallelAvailability, available) : available; if (childAvailableAssets != null) { childAvailableAssets.intersect(currentChildAvailable); } else { ancestorAssets[childId] = currentChildAvailable.clone(); } if (isParallel) { parallelAvailability.union(reachableAssets[childId]); parallelAvailability.add(assetId); //The next sibling should have older sibling available via parallel } } } // Step Internalize async bundles - internalize Async bundles if and only if, // the bundle is synchronously available elsewhere. // We can query sync assets available via reachableRoots. If the parent has // the bundleRoot by reachableRoots AND ancestorAssets, internalize it. for (let [id, bundleRootId] of bundleRootGraph.nodes.entries()) { if (bundleRootId == null || id === rootNodeId) continue; let bundleRoot = assets[bundleRootId]; if (manualAssetToConfig.has(bundleRoot)) { // We internalize for MSBs later, we should never delete MSBs continue; } let parentRoots = bundleRootGraph.getNodeIdsConnectedTo(id, _graph().ALL_EDGE_TYPES); let canDelete = getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated'; if (parentRoots.length === 0) continue; for (let parentId of parentRoots) { var _ancestorAssets$paren; if (parentId === rootNodeId) { // connected to root. canDelete = false; continue; } if (reachableAssets[parentId].has(bundleRootId) || (_ancestorAssets$paren = ancestorAssets[parentId]) !== null && _ancestorAssets$paren !== void 0 && _ancestorAssets$paren.has(bundleRootId)) { let parentAssetId = (0, _nullthrows().default)(bundleRootGraph.getNode(parentId)); let parent = assets[parentAssetId]; let parentBundle = bundleGraph.getNode((0, _nullthrows().default)(bundles.get(parent.id))); (0, _assert().default)(parentBundle != null && parentBundle !== 'root'); if (!parentBundle.internalizedAssets) { parentBundle.internalizedAssets = new (_graph().BitSet)(assets.length); } parentBundle.internalizedAssets.add(bundleRootId); } else { canDelete = false; } } if (canDelete) { deleteBundle(bundleRoot); } } function assignInlineConstants(parentAsset, bundle) { for (let inlineConstant of inlineConstantDeps.get(parentAsset)) { if (!bundle.assets.has(inlineConstant)) { bundle.assets.add(inlineConstant); bundle.size += inlineConstant.stats.size; } } } // Step Insert Or Share: Place all assets into bundles or create shared bundles. Each asset // is placed into a single bundle based on the bundle entries it is reachable from. // This creates a maximally code split bundle graph with no duplication. let reachable = new (_graph().BitSet)(assets.length); let reachableNonEntries = new (_graph().BitSet)(assets.length); let reachableIntersection = new (_graph().BitSet)(assets.length); for (let i = 0; i < assets.length; i++) { let asset = assets[i]; let manualSharedObject = manualAssetToConfig.get(asset); if (bundleRoots.has(asset) && inlineConstantDeps.get(asset).size > 0) { let entryBundleId = (0, _nullthrows().default)(bundleRoots.get(asset))[0]; let entryBundle = (0, _nullthrows().default)(bundleGraph.getNode(entryBundleId)); (0, _assert().default)(entryBundle !== 'root'); assignInlineConstants(asset, entryBundle); } if (asset.meta.isConstantModule === true) { // Ignore constant modules as they are placed with their direct parents continue; } // Unreliable bundleRoot assets which need to pulled in by shared bundles or other means. // Filter out entries, since they can't have shared bundles. // Neither can non-splittable, isolated, or needing of stable name bundles. // Reserve those filtered out bundles since we add the asset back into them. reachableNonEntries.clear(); reachableRoots[i].forEach(nodeId => { var _ancestorAssets$nodeI; let assetId = bundleRootGraph.getNode(nodeId); if (assetId == null) return; // deleted let a = assets[assetId]; if (entries.has(a) || !a.isBundleSplittable || bundleRoots.get(a) && (getBundleFromBundleRoot(a).needsStableName || getBundleFromBundleRoot(a).bundleBehavior === 'isolated')) { // Add asset to non-splittable bundles. addAssetToBundleRoot(asset, a); } else if (!((_ancestorAssets$nodeI = ancestorAssets[nodeId]) !== null && _ancestorAssets$nodeI !== void 0 && _ancestorAssets$nodeI.has(i))) { // Filter out bundles from this asset's reachable array if // bundle does not contain the asset in its ancestry reachableNonEntries.add(assetId); } }); reachable.bits.set(reachableNonEntries.bits); // If we encounter a "manual" asset, draw an edge from reachable to its MSB if (manualSharedObject && !reachable.empty()) { let bundle; let bundleId; let manualSharedBundleKey = manualSharedObject.name + ',' + asset.type; let sourceBundles = []; reachable.forEach(id => { sourceBundles.push((0, _nullthrows().default)(bundleRoots.get(assets[id]))[0]); }); if (!manualSharedMap.has(manualSharedBundleKey)) { let firstSourceBundle = (0, _nullthrows().default)(bundleGraph.getNode(sourceBundles[0])); (0, _assert().default)(firstSourceBundle !== 'root'); bundle = createBundle({ uniqueKey: manualSharedBundleKey, target: firstSourceBundle.target, type: asset.type, env: firstSourceBundle.env, manualSharedBundle: manualSharedObject === null || manualSharedObject === void 0 ? void 0 : manualSharedObject.name }); bundle.sourceBundles = new Set(sourceBundles); bundle.assets.add(asset); bundleId = bundleGraph.addNode(bundle); manualSharedMap.set(manualSharedBundleKey, bundleId); } else { bundleId = (0, _nullthrows().default)(manualSharedMap.get(manualSharedBundleKey)); bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId)); (0, _assert().default)(bundle != null && bundle !== 'root', 'We tried to use the root incorrectly'); if (!bundle.assets.has(asset)) { bundle.assets.add(asset); bundle.size += asset.stats.size; } for (let s of sourceBundles) { if (s != bundleId) { bundle.sourceBundles.add(s); } } } for (let sourceBundleId of sourceBundles) { if (bundleId !== sourceBundleId) { bundleGraph.addEdge(sourceBundleId, bundleId); } } dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), { value: bundle, type: 'bundle' }); continue; } // Finally, filter out bundleRoots (bundles) from this assets // reachable if they are subgraphs, and reuse that subgraph bundle // by drawing an edge. Essentially, if two bundles within an asset's // reachable array, have an ancestor-subgraph relationship, draw that edge. // This allows for us to reuse a bundle instead of making a shared bundle if // a bundle represents the exact set of assets a set of bundles would share // if a bundle b is a subgraph of another bundle f, reuse it, drawing an edge between the two if (config.disableSharedBundles === false) { reachableNonEntries.forEach(candidateId => { let candidateSourceBundleRoot = assets[candidateId]; let candidateSourceBundleId = (0, _nullthrows().default)(bundleRoots.get(candidateSourceBundleRoot))[0]; if (candidateSourceBundleRoot.env.isIsolated()) { return; } let reuseableBundleId = bundles.get(asset.id); if (reuseableBundleId != null) { reachable.delete(candidateId); bundleGraph.addEdge(candidateSourceBundleId, reuseableBundleId); let reusableBundle = bundleGraph.getNode(reuseableBundleId); (0, _assert().default)(reusableBundle !== 'root' && reusableBundle != null); reusableBundle.sourceBundles.add(candidateSourceBundleId); } else { // Asset is not a bundleRoot, but if its ancestor bundle (in the asset's reachable) can be // reused as a subgraph of another bundleRoot in its reachable, reuse it reachableIntersection.bits.set(reachableNonEntries.bits); reachableIntersection.intersect(reachableAssets[(0, _nullthrows().default)(assetToBundleRootNodeId.get(candidateSourceBundleRoot))]); reachableIntersection.forEach(otherCandidateId => { let otherReuseCandidate = assets[otherCandidateId]; if (candidateSourceBundleRoot === otherReuseCandidate) return; let reusableBundleId = (0, _nullthrows().default)(bundles.get(otherReuseCandidate.id)); reachable.delete(candidateId); bundleGraph.addEdge((0, _nullthrows().default)(bundles.get(candidateSourceBundleRoot.id)), reusableBundleId); let reusableBundle = bundleGraph.getNode(reusableBundleId); (0, _assert().default)(reusableBundle !== 'root' && reusableBundle != null); reusableBundle.sourceBundles.add(candidateSourceBundleId); }); } }); } let reachableArray = []; reachable.forEach(id => { reachableArray.push(assets[id]); }); // Create shared bundles for splittable bundles. if (config.disableSharedBundles === false && reachableArray.length > config.minBundles) { let sourceBundles = reachableArray.map(a => (0, _nullthrows().default)(bundleRoots.get(a))[0]); let key = reachableArray.map(a => a.id).join(',') + '.' + asset.type; let bundleId = bundles.get(key); let bundle; if (bundleId == null) { let firstSourceBundle = (0, _nullthrows().default)(bundleGraph.getNode(sourceBundles[0])); (0, _assert().default)(firstSourceBundle !== 'root'); bundle = createBundle({ target: firstSourceBundle.target, type: asset.type, env: firstSourceBundle.env }); bundle.sourceBundles = new Set(sourceBundles); let sharedInternalizedAssets = firstSourceBundle.internalizedAssets ? firstSourceBundle.internalizedAssets.clone() : new (_graph().BitSet)(assets.length); for (let p of sourceBundles) { let parentBundle = (0, _nullthrows().default)(bundleGraph.getNode(p)); (0, _assert().default)(parentBundle !== 'root'); if (parentBundle === firstSourceBundle) continue; if (parentBundle.internalizedAssets) { sharedInternalizedAssets.intersect(parentBundle.internalizedAssets); } else { sharedInternalizedAssets.clear(); } } bundle.internalizedAssets = sharedInternalizedAssets; bundleId = bundleGraph.addNode(bundle); bundles.set(key, bundleId); } else { bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId)); (0, _assert().default)(bundle !== 'root'); } bundle.assets.add(asset); bundle.size += asset.stats.size; assignInlineConstants(asset, bundle); for (let sourceBundleId of sourceBundles) { if (bundleId !== sourceBundleId) { bundleGraph.addEdge(sourceBundleId, bundleId); } } dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), { value: bundle, type: 'bundle' }); } else if (config.disableSharedBundles === true || reachableArray.length <= config.minBundles) { for (let root of reachableArray) { addAssetToBundleRoot(asset, root); } } } let manualSharedBundleIds = new Set([...manualSharedMap.values()]); // Step split manual shared bundles for those that have the "split" property set let remainderMap = new (_utils().DefaultMap)(() => []); for (let id of manualSharedMap.values()) { let manualBundle = bundleGraph.getNode(id); (0, _assert().default)(manualBundle !== 'root' && manualBundle != null); if (manualBundle.sourceBundles.size > 0) { var _manualAssetToConfig$; let firstSourceBundle = (0, _nullthrows().default)(bundleGraph.getNode([...manualBundle.sourceBundles][0])); (0, _assert().default)(firstSourceBundle !== 'root'); let firstAsset = [...manualBundle.assets][0]; let manualSharedObject = manualAssetToConfig.get(firstAsset); (0, _assert().default)(manualSharedObject != null); let modNum = (_manualAssetToConfig$ = manualAssetToConfig.get(firstAsset)) === null || _manualAssetToConfig$ === void 0 ? void 0 : _manualAssetToConfig$.split; if (modNum != null) { for (let a of [...manualBundle.assets]) { let numRep = getBigIntFromContentKey(a.id); // $FlowFixMe Flow doesn't know about BigInt let r = Number(numRep % BigInt(modNum)); remainderMap.get(r).push(a); } for (let i = 1; i < [...remainderMap.keys()].length; i++) { let bundle = createBundle({ uniqueKey: manualSharedObject.name + firstSourceBundle.type + i, target: firstSourceBundle.target, type: firstSourceBundle.type, env: firstSourceBundle.env, manualSharedBundle: manualSharedObject.name }); bundle.sourceBundles = manualBundle.sourceBundles; bundle.internalizedAssets = manualBundle.internalizedAssets; let bundleId = bundleGraph.addNode(bundle); manualSharedBundleIds.add(bundleId); for (let sourceBundleId of manualBundle.sourceBundles) { if (bundleId !== sourceBundleId) { bundleGraph.addEdge(sourceBundleId, bundleId); } } for (let sp of remainderMap.get(i)) { bundle.assets.add(sp); bundle.size += sp.stats.size; manualBundle.assets.delete(sp); manualBundle.size -= sp.stats.size; } } } } } // Step insert constant modules into manual shared bundles. // We have to do this separately as they're the only case where a single asset can // match multiple MSB's for (let [asset, msbs] of constantModuleToMSB.entries()) { for (let manualSharedObject of msbs) { let bundleId = manualSharedMap.get(manualSharedObject.name + ',js'); if (bundleId == null) continue; let bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId)); (0, _assert().default)(bundle != null && bundle !== 'root', 'We tried to use the root incorrectly'); if (!bundle.assets.has(asset)) { bundle.assets.add(asset); bundle.size += asset.stats.size; } } } // Step Merge Share Bundles: Merge any shared bundles under the minimum bundle size back into // their source bundles, and remove the bundle. // We should include "bundle reuse" as shared bundles that may be removed but the bundle itself would have to be retained for (let [bundleNodeId, bundle] of bundleGraph.nodes.entries()) { if (!bundle || bundle === 'root') continue; if (bundle.sourceBundles.size > 0 && bundle.mainEntryAsset == null && bundle.size < config.minBundleSize && !manualSharedBundleIds.has(bundleNodeId)) { removeBundle(bundleGraph, bundleNodeId, assetReference); } } let modifiedSourceBundles = new Set(); // Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit. if (config.disableSharedBundles === false) { for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) { // Find shared bundles in this bundle group. let bundleId = bundleGroupId; // We should include "bundle reuse" as shared bundles that may be removed but the bundle itself would have to be retained let bundleIdsInGroup = getBundlesForBundleGroup(bundleId); //get all bundlegrups this bundle is an ancestor of // Filter out inline assests as they should not contribute to PRL let numBundlesContributingToPRL = bundleIdsInGroup.reduce((count, b) => { let bundle = (0, _nullthrows().default)(bundleGraph.getNode(b)); (0, _assert().default)(bundle !== 'root'); return count + (bundle.bundleBehavior !== 'inline'); }, 0); if (numBundlesContributingToPRL > config.maxParallelRequests) { let sharedBundleIdsInBundleGroup = bundleIdsInGroup.filter(b => { let bundle = (0, _nullthrows().default)(bundleGraph.getNode(b)); // shared bundles must have source bundles, we could have a bundle // connected to another bundle that isnt a shared bundle, so check return bundle !== 'root' && bundle.sourceBundles.size > 0 && bundleId != b && !manualSharedBundleIds.has(b); }); // Sort the bundles so the smallest ones are removed first. let sharedBundlesInGroup = sharedBundleIdsInBundleGroup.map(id => ({ id, bundle: (0, _nullthrows().default)(bundleGraph.getNode(id)) })).map(({ id, bundle }) => { // For Flow (0, _assert().default)(bundle !== 'root'); return { id, bundle }; }).sort((a, b) => b.bundle.size - a.bundle.size); // Remove bundles until the bundle group is within the parallel request limit. while (sharedBundlesInGroup.length > 0 && numBundlesContributingToPRL > config.maxParallelRequests) { let bundleTuple = sharedBundlesInGroup.pop(); let bundleToRemove = bundleTuple.bundle; let bundleIdToRemove = bundleTuple.id; //TODO add integration test where bundles in bunlde group > max parallel request limit & only remove a couple shared bundles // but total # bundles still exceeds limit due to non shared bundles // Add all assets in the shared bundle into the source bundles that are within this bundle group. let sourceBundles = [...bundleToRemove.sourceBundles].filter(b => bundleIdsInGroup.includes(b)); for (let sourceBundleId of sourceBundles) { let sourceBundle = (0, _nullthrows().default)(bundleGraph.getNode(sourceBundleId)); (0, _assert().default)(sourceBundle !== 'root'); modifiedSourceBundles.add(sourceBundle); bundleToRemove.sourceBundles.delete(sourceBundleId); for (let asset of bundleToRemove.assets) { addAssetToBundleRoot(asset, (0, _nullthrows().default)(sourceBundle.mainEntryAsset)); } //This case is specific to reused bundles, which can have shared bundles attached to it for (let childId of bundleGraph.getNodeIdsConnectedFrom(bundleIdToRemove)) { let child = bundleGraph.getNode(childId); (0, _assert().default)(child !== 'root' && child != null); child.sourceBundles.add(sourceBundleId); bundleGraph.addEdge(sourceBundleId, childId); } // needs to add test case where shared bundle is removed from ONE bundlegroup but not from the whole graph! // Remove the edge from this bundle group to the shared bundle. // If there is now only a single bundle group that contains this bundle, // merge it into the remaining source bundles. If it is orphaned entirely, remove it. let incomingNodeCount = bundleGraph.getNodeIdsConnectedTo(bundleIdToRemove).length; if (incomingNodeCount <= 2 && //Never fully remove reused bundles bundleToRemove.mainEntryAsset == null) { // If one bundle group removes a shared bundle, but the other *can* keep it, still remove because that shared bundle is pointless (only one source bundle) removeBundle(bundleGraph, bundleIdToRemove, assetReference); // Stop iterating through bundleToRemove's sourceBundles as the bundle has been removed. break; } else { bundleGraph.removeEdge(sourceBundleId, bundleIdToRemove); } } numBundlesContributingToPRL--; } } } } function getBigIntFromContentKey(contentKey) { let b = Buffer.alloc(64); b.write(contentKey); // $FlowFixMe Flow doesn't have BigInt types in this version return b.readBigInt64BE(); } // Fix asset order in source bundles as they are likely now incorrect after shared bundle deletion if (modifiedSourceBundles.size > 0) { let assetOrderMap = new Map(assets.map((a, index) => [a, index])); for (let bundle of modifiedSourceBundles) { bundle.assets = new Set([...bundle.assets].sort((a, b) => { let aIndex = (0, _nullthrows().default)(assetOrderMap.get(a)); let bIndex = (0, _nullthrows().default)(assetOrderMap.get(b)); return aIndex - bIndex; })); } } function deleteBundle(bundleRoot) { bundleGraph.removeNode((0, _nullthrows().default)(bundles.get(bundleRoot.id))); bundleRoots.delete(bundleRoot); bundles.delete(bundleRoot.id); let bundleRootId = assetToBundleRootNodeId.get(bundleRoot); if (bundleRootId != null && bundleRootGraph.hasNode(bundleRootId)) { bundleRootGraph.removeNode(bundleRootId); } } function getBundlesForBundleGroup(bundleGroupId) { let bundlesInABundleGroup = []; bundleGraph.traverse(nodeId => { bundlesInABundleGroup.push(nodeId); }, bundleGroupId); return bundlesInABundleGroup; } function getBundleFromBundleRoot(bundleRoot) { let bundle = bundleGraph.getNode((0, _nullthrows().default)(bundleRoots.get(bundleRoot))[0]); (0, _assert().default)(bundle !== 'root' && bundle != null); return bundle; } function addAssetToBundleRoot(asset, bundleRoot) { let [bundleId, bundleGroupId] = (0, _nullthrows().default)(bundleRoots.get(bundleRoot)); let bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId)); (0, _assert().default)(bundle !== 'root'); if (asset.type !== bundle.type) { let bundleGroup = (0, _nullthrows().default)(bundleGraph.getNode(bundleGroupId)); (0, _assert().default)(bundleGroup !== 'root'); let key = (0, _nullthrows().default)(bundleGroup.mainEntryAsset).id + '.' + asset.type; let typeChangeBundleId = bundles.get(key); if (typeChangeBundleId == null) { let typeChangeBundle = createBundle({ uniqueKey: key, needsStableName: bundle.needsStableName, bundleBehavior: bundle.bundleBehavior, type: asset.type, target: bundle.target, env: bundle.env }); typeChangeBundleId = bundleGraph.addNode(typeChangeBundle); bundleGraph.addEdge(bundleId, typeChangeBundleId); bundles.set(key, typeChangeBundleId); bundle = typeChangeBundle; } else { bundle = (0, _nullthrows().default)(bundleGraph.getNode(typeChangeBundleId)); (0, _assert().default)(bundle !== 'root'); } } bundle.assets.add(asset); bundle.size += asset.stats.size; assignInlineConstants(asset, bundle); } function removeBundle(bundleGraph, bundleId, assetReference) { let bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId)); (0, _assert().default)(bundle !== 'root'); for (let asset of bundle.assets) { assetReference.set(asset, assetReference.get(asset).filter(t => !t.includes(bundle))); for (let sourceBundleId of bundle.sourceBundles) { let sourceBundle = (0, _nullthrows().default)(bundleGraph.getNode(sourceBundleId)); (0, _assert().default)(sourceBundle !== 'root'); addAssetToBundleRoot(asset, (0, _nullthrows().default)(sourceBundle.mainEntryAsset)); } } bundleGraph.removeNode(bundleId); } return { assets, bundleGraph, dependencyBundleGraph, bundleGroupBundleIds, assetReference, manualAssetToBundle }; } const CONFIG_SCHEMA = { type: 'object', properties: { http: { type: 'number', enum: Object.keys(HTTP_OPTIONS).map(k => Number(k)) }, manualSharedBundles: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, assets: { type: 'array', items: { type: 'string' } }, types: { type: 'array', items: { type: 'string' } }, root: { type: 'string' }, split: { type: 'number' } }, required: ['name', 'assets'], additionalProperties: false } }, minBundles: { type: 'number' }, minBundleSize: { type: 'number' }, maxParallelRequests: { type: 'number' }, disableSharedBundles: { type: 'boolean' } }, additionalProperties: false }; function createBundle(opts) { var _opts$type, _opts$env, _opts$bundleBehavior; if (opts.asset == null) { return { uniqueKey: opts.uniqueKey, assets: new Set(), mainEntryAsset: null, size: 0, sourceBundles: new Set(), target: opts.target, type: (0, _nullthrows().default)(opts.type), env: (0, _nullthrows().default)(opts.env), needsStableName: Boolean(opts.needsStableName), bundleBehavior: opts.bundleBehavior, manualSharedBundle: opts.manualSharedBundle }; } let asset = (0, _nullthrows().default)(opts.asset); return { uniqueKey: opts.uniqueKey, assets: new Set([asset]), mainEntryAsset: asset, size: asset.stats.size, sourceBundles: new Set(), target: opts.target, type: (_opts$type = opts.type) !== null && _opts$type !== void 0 ? _opts$type : asset.type, env: (_opts$env = opts.env) !== null && _opts$env !== void 0 ? _opts$env : asset.env, needsStableName: Boolean(opts.needsStableName), bundleBehavior: (_opts$bundleBehavior = opts.bundleBehavior) !== null && _opts$bundleBehavior !== void 0 ? _opts$bundleBehavior : asset.bundleBehavior, manualSharedBundle: opts.manualSharedBundle }; } function resolveModeConfig(config, mode) { let generalConfig = {}; let modeConfig = {}; for (const key of Object.keys(config)) { if (key === 'development' || key === 'production') { if (key === mode) { modeConfig = config[key]; } } else { generalConfig[key] = config[key]; } } // $FlowFixMe Not sure how to convince flow here... return { ...generalConfig, ...modeConfig }; } async function loadBundlerConfig(config, options, logger) { var _modeConfig$http, _modeConfig$minBundle, _modeConfig$minBundle2, _modeConfig$maxParall, _modeConfig$disableSh, _modeConfig$manualSha; let conf = await config.getConfig([], { packageKey: '@parcel/bundler-default' }); if (!conf) { const modDefault = { ...HTTP_OPTIONS['2'], projectRoot: options.projectRoot }; return modDefault; } (0, _assert().default)((conf === null || conf === void 0 ? void 0 : conf.contents) != null); let modeConfig = resolveModeConfig(conf.contents, options.mode); // minBundles will be ignored if shared bundles are disabled if (modeConfig.minBundles != null && modeConfig.disableSharedBundles === true) { logger.warn({ origin: '@parcel/bundler-default', message: `The value of "${modeConfig.minBundles}" set for minBundles will not be used as shared bundles have been disabled` }); } // minBundleSize will be ignored if shared bundles are disabled if (modeConfig.minBundleSize != null && modeConfig.disableSharedBundles === true) { logger.warn({ origin: '@parcel/bundler-default', message: `The value of "${modeConfig.minBundleSize}" set for minBundleSize will not be used as shared bundles have been disabled` }); } // maxParallelRequests will be ignored if shared bundles are disabled if (modeConfig.maxParallelRequests != null && modeConfig.disableSharedBundles === true) { logger.warn({ origin: '@parcel/bundler-default', message: `The value of "${modeConfig.maxParallelRequests}" set for maxParallelRequests will not be used as shared bundles have been disabled` }); } if (modeConfig.manualSharedBundles) { let nameArray = modeConfig.manualSharedBundles.map(a => a.name); let nameSet = new Set(nameArray); (0, _assert().default)(nameSet.size == nameArray.length, 'The name field must be unique for property manualSharedBundles'); } _utils().validateSchema.diagnostic(CONFIG_SCHEMA, { data: modeConfig, source: await options.inputFS.readFile(conf.filePath, 'utf8'), filePath: conf.filePath, prependKey: `/${(0, _diagnostic().encodeJSONKeyComponent)('@parcel/bundler-default')}` }, '@parcel/bundler-default', 'Invalid config for @parcel/bundler-default'); let http = (_modeConfig$http = modeConfig.http) !== null && _modeConfig$http !== void 0 ? _modeConfig$http : 2; let defaults = HTTP_OPTIONS[http]; return { minBundles: (_modeConfig$minBundle = modeConfig.minBundles) !== null && _modeConfig$minBundle !== void 0 ? _modeConfig$minBundle : defaults.minBundles, minBundleSize: (_modeConfig$minBundle2 = modeConfig.minBundleSize) !== null && _modeConfig$minBundle2 !== void 0 ? _modeConfig$minBundle2 : defaults.minBundleSize, maxParallelRequests: (_modeConfig$maxParall = modeConfig.maxParallelRequests) !== null && _modeConfig$maxParall !== void 0 ? _modeConfig$maxParall : defaults.maxParallelRequests, projectRoot: options.projectRoot, disableSharedBundles: (_modeConfig$disableSh = modeConfig.disableSharedBundles) !== null && _modeConfig$disableSh !== void 0 ? _modeConfig$disableSh : defaults.disableSharedBundles, manualSharedBundles: (_modeConfig$manualSha = modeConfig.manualSharedBundles) !== null && _modeConfig$manualSha !== void 0 ? _modeConfig$manualSha : defaults.manualSharedBundles }; } function getEntryByTarget(bundleGraph) { // Find entries from assetGraph per target let targets = new (_utils().DefaultMap)(() => new Map()); bundleGraph.traverse({ enter(node, context, actions) { if (node.type !== 'asset') { return node; } (0, _assert().default)(context != null && context.type === 'dependency' && context.value.isEntry && context.value.target != null); targets.get(context.value.target.distDir).set(node.value, context.value); actions.skipChildren(); return node; } }); return targets; }