UNPKG

@parcel/core

Version:
163 lines (161 loc) • 6.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = createWriteBundlesRequest; var _RequestTracker = require("../RequestTracker"); var _constants = require("../constants"); var _projectPath = require("../projectPath"); function _nullthrows() { const data = _interopRequireDefault(require("nullthrows")); _nullthrows = function () { return data; }; return data; } function _rust() { const data = require("@parcel/rust"); _rust = function () { return data; }; return data; } var _PackageRequest = require("./PackageRequest"); var _WriteBundleRequest = _interopRequireDefault(require("./WriteBundleRequest")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } /** * Packages, optimizes, and writes all bundles to the dist directory. */ function createWriteBundlesRequest(input) { return { type: _RequestTracker.requestTypes.write_bundles_request, id: 'write_bundles:' + input.bundleGraph.getBundleGraphHash(), run, input }; } async function run({ input, api, farm, options }) { let { bundleGraph, optionsRef } = input; let { ref, dispose } = await farm.createSharedReference(bundleGraph); api.invalidateOnOptionChange('shouldContentHash'); let res = new Map(); let bundleInfoMap = {}; let writeEarlyPromises = {}; let hashRefToNameHash = new Map(); let bundles = bundleGraph.getBundles().filter(bundle => { // Do not package and write placeholder bundles to disk. We just // need to update the name so other bundles can reference it. if (bundle.isPlaceholder) { let hash = bundle.id.slice(-8); hashRefToNameHash.set(bundle.hashReference, hash); let name = (0, _nullthrows().default)(bundle.name, `Expected ${bundle.type} bundle to have a name`).replace(bundle.hashReference, hash); res.set(bundle.id, [{ filePath: (0, _projectPath.joinProjectPath)(bundle.target.distDir, name), type: bundle.type, // FIXME: this is wrong if the packager changes the type... stats: { time: 0, size: 0 } }]); return false; } return true; }); // Package on the main thread if there is only one bundle to package. // This avoids the cost of serializing the bundle graph for single file change builds. let useMainThread = bundles.length === 1 || bundles.filter(b => !api.canSkipSubrequest((0, _PackageRequest.getPackageRequestId)(b))).length === 1; try { await Promise.all(bundles.map(async bundle => { let request = (0, _PackageRequest.createPackageRequest)({ bundle, bundleGraph, bundleGraphReference: ref, optionsRef, useMainThread }); let { info: infos } = await api.runRequest(request, { force: !(await (0, _PackageRequest.canSkipPackageRequest)(api, request)) }); if (!useMainThread) { // Force a refresh of the cache to avoid a race condition // between threaded reads and writes that can result in an LMDB cache miss: // 1. The main thread has read some value from cache, necessitating a read transaction. // 2. Concurrently, Thread A finishes a packaging request. // 3. Subsequently, the main thread is tasked with this request, but fails because the read transaction is stale. // This only occurs if the reading thread has a transaction that was created before the writing thread committed, // and the transaction is still live when the reading thread attempts to get the written value. // See https://github.com/parcel-bundler/parcel/issues/9121 options.cache.refresh(); } bundleInfoMap[bundle.id] = infos; if (infos.every(info => info.hashReferences.length === 0)) { hashRefToNameHash.set(bundle.hashReference, options.shouldContentHash ? infos.length === 1 ? infos[0].hash.slice(-8) : (0, _rust().hashString)(infos.map(i => i.hash).join(':')).slice(-8) : bundle.id.slice(-8)); for (let info of infos) { let writeBundleRequest = (0, _WriteBundleRequest.default)({ bundle, info, hashRefToNameHash, bundleGraph }); let promise = api.runRequest(writeBundleRequest); // If the promise rejects before we await it (below), we don't want to crash the build. promise.catch(() => {}); writeEarlyPromises[info.cacheKeys.content] = promise; } } })); assignComplexNameHashes(hashRefToNameHash, bundles, bundleInfoMap, options); await Promise.all(bundles.map(bundle => { let promise = Promise.all(bundleInfoMap[bundle.id].map(info => { return writeEarlyPromises[info.cacheKeys.content] ?? api.runRequest((0, _WriteBundleRequest.default)({ bundle, info, hashRefToNameHash, bundleGraph })); })); return promise.then(r => res.set(bundle.id, r.flat())); })); api.storeResult(res); return res; } finally { await dispose(); } } function assignComplexNameHashes(hashRefToNameHash, bundles, bundleInfoMap, options) { for (let bundle of bundles) { if (hashRefToNameHash.get(bundle.hashReference) != null) { continue; } hashRefToNameHash.set(bundle.hashReference, options.shouldContentHash ? (0, _rust().hashString)([...getBundlesIncludedInHash(bundle.id, bundleInfoMap)].flatMap(bundleId => bundleInfoMap[bundleId].map(i => i.hash)).join(':')).slice(-8) : bundle.id.slice(-8)); } } function getBundlesIncludedInHash(bundleId, bundleInfoMap, included = new Set()) { included.add(bundleId); for (let info of bundleInfoMap[bundleId]) { for (let hashRef of info.hashReferences) { let referencedId = getIdFromHashRef(hashRef); if (!included.has(referencedId)) { getBundlesIncludedInHash(referencedId, bundleInfoMap, included); } } } return included; } function getIdFromHashRef(hashRef) { return hashRef.slice(_constants.HASH_REF_PREFIX.length); }