Son CV dans un terminal web en Javascript! https://terminal-cv.gregandev.fr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
6.7 KiB

// @flow strict-local
import type {BundleGraph, PluginOptions, NamedBundle} from '@parcel/types';
import {PromiseQueue, relativeBundlePath, countLines} from '@parcel/utils';
import SourceMap from '@parcel/source-map';
import invariant from 'assert';
import path from 'path';
import fs from 'fs';
import {replaceScriptDependencies, getSpecifier} from './utils';
const PRELUDE = fs
.readFileSync(path.join(__dirname, 'dev-prelude.js'), 'utf8')
.trim()
.replace(/;$/, '');
export class DevPackager {
options: PluginOptions;
bundleGraph: BundleGraph<NamedBundle>;
bundle: NamedBundle;
parcelRequireName: string;
constructor(
options: PluginOptions,
bundleGraph: BundleGraph<NamedBundle>,
bundle: NamedBundle,
parcelRequireName: string,
) {
this.options = options;
this.bundleGraph = bundleGraph;
this.bundle = bundle;
this.parcelRequireName = parcelRequireName;
}
async package(): Promise<{|contents: string, map: ?SourceMap|}> {
// Load assets
let queue = new PromiseQueue({maxConcurrent: 32});
this.bundle.traverse(node => {
if (node.type === 'asset') {
queue.add(async () => {
let [code, mapBuffer] = await Promise.all([
node.value.getCode(),
this.bundle.env.sourceMap && node.value.getMapBuffer(),
]);
return {code, mapBuffer};
});
}
});
let results = await queue.run();
let assets = '';
let i = 0;
let first = true;
let map = new SourceMap(this.options.projectRoot);
let prefix = this.getPrefix();
let lineOffset = countLines(prefix);
let script: ?{|code: string, mapBuffer: ?Buffer|} = null;
this.bundle.traverse(node => {
let wrapped = first ? '' : ',';
if (node.type === 'dependency') {
let resolved = this.bundleGraph.getResolvedAsset(
node.value,
this.bundle,
);
if (resolved && resolved.type !== 'js') {
// if this is a reference to another javascript asset, we should not include
// its output, as its contents should already be loaded.
invariant(!this.bundle.hasAsset(resolved));
wrapped +=
JSON.stringify(this.bundleGraph.getAssetPublicId(resolved)) +
':[function() {},{}]';
} else {
return;
}
}
if (node.type === 'asset') {
let asset = node.value;
invariant(
asset.type === 'js',
'all assets in a js bundle must be js assets',
);
// If this is the main entry of a script rather than a module, we need to hoist it
// outside the bundle wrapper function so that its variables are exposed as globals.
if (
this.bundle.env.sourceType === 'script' &&
asset === this.bundle.getMainEntry()
) {
script = results[i++];
return;
}
let deps = {};
let dependencies = this.bundleGraph.getDependencies(asset);
for (let dep of dependencies) {
let resolved = this.bundleGraph.getResolvedAsset(dep, this.bundle);
1 year ago
if (resolved) {
deps[getSpecifier(dep)] = this.bundleGraph.getAssetPublicId(
resolved,
);
}
}
let {code, mapBuffer} = results[i];
let output = code || '';
wrapped +=
JSON.stringify(this.bundleGraph.getAssetPublicId(asset)) +
':[function(require,module,exports) {\n' +
output +
'\n},';
wrapped += JSON.stringify(deps);
wrapped += ']';
if (this.bundle.env.sourceMap) {
if (mapBuffer) {
map.addBuffer(mapBuffer, lineOffset);
} else {
map.addEmptyMap(
path
.relative(this.options.projectRoot, asset.filePath)
.replace(/\\+/g, '/'),
output,
lineOffset,
);
}
lineOffset += countLines(output) + 1;
}
i++;
}
assets += wrapped;
first = false;
});
let entries = this.bundle.getEntryAssets();
let mainEntry = this.bundle.getMainEntry();
if (
(!this.isEntry() && this.bundle.env.outputFormat === 'global') ||
this.bundle.env.sourceType === 'script'
) {
// In async bundles we don't want the main entry to execute until we require it
// as there might be dependencies in a sibling bundle that hasn't loaded yet.
entries = entries.filter(a => a.id !== mainEntry?.id);
mainEntry = null;
}
let contents =
prefix +
'({' +
assets +
'},' +
JSON.stringify(
entries.map(asset => this.bundleGraph.getAssetPublicId(asset)),
) +
', ' +
JSON.stringify(
mainEntry ? this.bundleGraph.getAssetPublicId(mainEntry) : null,
) +
', ' +
JSON.stringify(this.parcelRequireName) +
')' +
'\n';
// The entry asset of a script bundle gets hoisted outside the bundle wrapper function
// so that its variables become globals. We need to replace any require calls for
// runtimes with a parcelRequire call.
if (this.bundle.env.sourceType === 'script' && script) {
let entryMap;
let mapBuffer = script.mapBuffer;
if (mapBuffer) {
entryMap = new SourceMap(this.options.projectRoot, mapBuffer);
}
contents += replaceScriptDependencies(
this.bundleGraph,
this.bundle,
script.code,
entryMap,
this.parcelRequireName,
);
if (this.bundle.env.sourceMap && entryMap) {
map.addSourceMap(entryMap, lineOffset);
}
}
return {
contents,
map,
};
}
getPrefix(): string {
let interpreter: ?string;
let mainEntry = this.bundle.getMainEntry();
if (mainEntry && this.isEntry() && !this.bundle.target.env.isBrowser()) {
let _interpreter = mainEntry.meta.interpreter;
invariant(_interpreter == null || typeof _interpreter === 'string');
interpreter = _interpreter;
}
let importScripts = '';
if (this.bundle.env.isWorker()) {
let bundles = this.bundleGraph.getReferencedBundles(this.bundle);
for (let b of bundles) {
importScripts += `importScripts("${relativeBundlePath(
this.bundle,
b,
)}");\n`;
}
}
return (
// If the entry asset included a hashbang, repeat it at the top of the bundle
(interpreter != null ? `#!${interpreter}\n` : '') +
importScripts +
PRELUDE
);
}
isEntry(): boolean {
return (
!this.bundleGraph.hasParentBundleOfType(this.bundle, 'js') ||
this.bundle.env.isIsolated() ||
this.bundle.bundleBehavior === 'isolated'
);
}
}