@parcel/core
Version:
153 lines (151 loc) • 5.64 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = createWriteBundlesRequest;
var _constants = require("../constants");
var _projectPath = require("../projectPath");
function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _hash() {
const data = require("@parcel/hash");
_hash = function () {
return data;
};
return data;
}
var _PackageRequest = require("./PackageRequest");
var _WriteBundleRequest = _interopRequireDefault(require("./WriteBundleRequest"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Packages, optimizes, and writes all bundles to the dist directory.
*/
function createWriteBundlesRequest(input) {
return {
type: '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).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(bundleGraph.getHash(b))).length === 1;
try {
await Promise.all(bundles.map(async bundle => {
let request = (0, _PackageRequest.createPackageRequest)({
bundle,
bundleGraph,
bundleGraphReference: ref,
optionsRef,
useMainThread
});
let info = await api.runRequest(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] = info;
if (!info.hashReferences.length) {
hashRefToNameHash.set(bundle.hashReference, options.shouldContentHash ? info.hash.slice(-8) : bundle.id.slice(-8));
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[bundle.id] = promise;
}
}));
assignComplexNameHashes(hashRefToNameHash, bundles, bundleInfoMap, options);
await Promise.all(bundles.map(bundle => {
var _writeEarlyPromises$b;
let promise = (_writeEarlyPromises$b = writeEarlyPromises[bundle.id]) !== null && _writeEarlyPromises$b !== void 0 ? _writeEarlyPromises$b : api.runRequest((0, _WriteBundleRequest.default)({
bundle,
info: bundleInfoMap[bundle.id],
hashRefToNameHash,
bundleGraph
}));
return promise.then(r => res.set(bundle.id, r));
}));
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, _hash().hashString)([...getBundlesIncludedInHash(bundle.id, bundleInfoMap)].map(bundleId => bundleInfoMap[bundleId].hash).join(':')).slice(-8) : bundle.id.slice(-8));
}
}
function getBundlesIncludedInHash(bundleId, bundleInfoMap, included = new Set()) {
included.add(bundleId);
for (let hashRef of bundleInfoMap[bundleId].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);
}