238 lines
7.5 KiB
JavaScript
238 lines
7.5 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
exports.default = void 0;
|
||
|
function _assert() {
|
||
|
const data = _interopRequireDefault(require("assert"));
|
||
|
_assert = function () {
|
||
|
return data;
|
||
|
};
|
||
|
return data;
|
||
|
}
|
||
|
function _stream() {
|
||
|
const data = require("stream");
|
||
|
_stream = 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 _posthtml() {
|
||
|
const data = _interopRequireDefault(require("posthtml"));
|
||
|
_posthtml = function () {
|
||
|
return data;
|
||
|
};
|
||
|
return data;
|
||
|
}
|
||
|
function _nullthrows() {
|
||
|
const data = _interopRequireDefault(require("nullthrows"));
|
||
|
_nullthrows = function () {
|
||
|
return data;
|
||
|
};
|
||
|
return data;
|
||
|
}
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
// https://www.w3.org/TR/html5/dom.html#metadata-content-2
|
||
|
const metadataContent = new Set(['base', 'link', 'meta', 'noscript',
|
||
|
// 'script', // retain script order (somewhat)
|
||
|
'style', 'template', 'title']);
|
||
|
var _default = exports.default = new (_plugin().Packager)({
|
||
|
async loadConfig({
|
||
|
config
|
||
|
}) {
|
||
|
var _posthtmlConfig$conte;
|
||
|
let posthtmlConfig = await config.getConfig(['.posthtmlrc', '.posthtmlrc.js', '.posthtmlrc.cjs', '.posthtmlrc.mjs', 'posthtml.config.js', 'posthtml.config.cjs', 'posthtml.config.mjs'], {
|
||
|
packageKey: 'posthtml'
|
||
|
});
|
||
|
return {
|
||
|
render: posthtmlConfig === null || posthtmlConfig === void 0 || (_posthtmlConfig$conte = posthtmlConfig.contents) === null || _posthtmlConfig$conte === void 0 ? void 0 : _posthtmlConfig$conte.render
|
||
|
};
|
||
|
},
|
||
|
async package({
|
||
|
bundle,
|
||
|
bundleGraph,
|
||
|
getInlineBundleContents,
|
||
|
config
|
||
|
}) {
|
||
|
let assets = [];
|
||
|
bundle.traverseAssets(asset => {
|
||
|
assets.push(asset);
|
||
|
});
|
||
|
_assert().default.equal(assets.length, 1, 'HTML bundles must only contain one asset');
|
||
|
let asset = assets[0];
|
||
|
let code = await asset.getCode();
|
||
|
|
||
|
// Add bundles in the same bundle group that are not inline. For example, if two inline
|
||
|
// bundles refer to the same library that is extracted into a shared bundle.
|
||
|
let referencedBundles = [...(0, _utils().setDifference)(new Set(bundleGraph.getReferencedBundles(bundle)), new Set(bundleGraph.getReferencedBundles(bundle, {
|
||
|
recursive: false
|
||
|
})))];
|
||
|
let renderConfig = config === null || config === void 0 ? void 0 : config.render;
|
||
|
let {
|
||
|
html
|
||
|
} = await (0, _posthtml().default)([tree => insertBundleReferences(referencedBundles, tree), tree => replaceInlineAssetContent(bundleGraph, getInlineBundleContents, tree)]).process(code, {
|
||
|
...renderConfig,
|
||
|
xmlMode: bundle.type === 'xhtml',
|
||
|
closingSingleTag: bundle.type === 'xhtml' ? 'slash' : undefined
|
||
|
});
|
||
|
let {
|
||
|
contents,
|
||
|
map
|
||
|
} = (0, _utils().replaceURLReferences)({
|
||
|
bundle,
|
||
|
bundleGraph,
|
||
|
contents: html,
|
||
|
relative: false,
|
||
|
getReplacement: contents => contents.replace(/"/g, '"')
|
||
|
});
|
||
|
return (0, _utils().replaceInlineReferences)({
|
||
|
bundle,
|
||
|
bundleGraph,
|
||
|
contents,
|
||
|
getInlineBundleContents,
|
||
|
getInlineReplacement: (dep, inlineType, contents) => ({
|
||
|
from: dep.id,
|
||
|
to: contents.replace(/"/g, '"').trim()
|
||
|
}),
|
||
|
map
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
async function getAssetContent(bundleGraph, getInlineBundleContents, assetId) {
|
||
|
let inlineBundle;
|
||
|
bundleGraph.traverseBundles((bundle, context, {
|
||
|
stop
|
||
|
}) => {
|
||
|
let entryAssets = bundle.getEntryAssets();
|
||
|
if (entryAssets.some(a => a.uniqueKey === assetId)) {
|
||
|
inlineBundle = bundle;
|
||
|
stop();
|
||
|
}
|
||
|
});
|
||
|
if (inlineBundle) {
|
||
|
const bundleResult = await getInlineBundleContents(inlineBundle, bundleGraph);
|
||
|
return {
|
||
|
bundle: inlineBundle,
|
||
|
contents: bundleResult.contents
|
||
|
};
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
async function replaceInlineAssetContent(bundleGraph, getInlineBundleContents, tree) {
|
||
|
const inlineNodes = [];
|
||
|
tree.walk(node => {
|
||
|
if (node.attrs && node.attrs['data-parcel-key']) {
|
||
|
inlineNodes.push(node);
|
||
|
}
|
||
|
return node;
|
||
|
});
|
||
|
for (let node of inlineNodes) {
|
||
|
let newContent = await getAssetContent(bundleGraph, getInlineBundleContents, node.attrs['data-parcel-key']);
|
||
|
if (newContent != null) {
|
||
|
let {
|
||
|
contents,
|
||
|
bundle
|
||
|
} = newContent;
|
||
|
node.content = (contents instanceof _stream().Readable ? await (0, _utils().bufferStream)(contents) : contents).toString();
|
||
|
if (node.tag === 'script' && (0, _nullthrows().default)(bundle).env.outputFormat === 'esmodule') {
|
||
|
node.attrs.type = 'module';
|
||
|
}
|
||
|
|
||
|
// Escape closing script tags and HTML comments in JS content.
|
||
|
// https://www.w3.org/TR/html52/semantics-scripting.html#restrictions-for-contents-of-script-elements
|
||
|
// Avoid replacing </script with <\/script as it would break the following valid JS: 0</script/ (i.e. regexp literal).
|
||
|
// Instead, escape the s character.
|
||
|
if (node.tag === 'script') {
|
||
|
node.content = node.content.replace(/<!--/g, '<\\!--').replace(/<\/(script)/gi, '</\\$1');
|
||
|
}
|
||
|
|
||
|
// Escape closing style tags in CSS content.
|
||
|
if (node.tag === 'style') {
|
||
|
node.content = node.content.replace(/<\/(style)/gi, '<\\/$1');
|
||
|
}
|
||
|
|
||
|
// remove attr from output
|
||
|
delete node.attrs['data-parcel-key'];
|
||
|
}
|
||
|
}
|
||
|
return tree;
|
||
|
}
|
||
|
function insertBundleReferences(siblingBundles, tree) {
|
||
|
const bundles = [];
|
||
|
for (let bundle of siblingBundles) {
|
||
|
if (bundle.type === 'css') {
|
||
|
bundles.push({
|
||
|
tag: 'link',
|
||
|
attrs: {
|
||
|
rel: 'stylesheet',
|
||
|
href: (0, _utils().urlJoin)(bundle.target.publicUrl, bundle.name)
|
||
|
}
|
||
|
});
|
||
|
} else if (bundle.type === 'js') {
|
||
|
let nomodule = bundle.env.outputFormat !== 'esmodule' && bundle.env.sourceType === 'module' && bundle.env.shouldScopeHoist;
|
||
|
bundles.push({
|
||
|
tag: 'script',
|
||
|
attrs: {
|
||
|
type: bundle.env.outputFormat === 'esmodule' ? 'module' : undefined,
|
||
|
nomodule: nomodule ? '' : undefined,
|
||
|
defer: nomodule ? '' : undefined,
|
||
|
src: (0, _utils().urlJoin)(bundle.target.publicUrl, bundle.name)
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
addBundlesToTree(bundles, tree);
|
||
|
}
|
||
|
function addBundlesToTree(bundles, tree) {
|
||
|
const main = find(tree, 'head') || find(tree, 'html');
|
||
|
const content = main ? main.content || (main.content = []) : tree;
|
||
|
const index = findBundleInsertIndex(content);
|
||
|
content.splice(index, 0, ...bundles);
|
||
|
}
|
||
|
function find(tree, tag) {
|
||
|
let res;
|
||
|
tree.match({
|
||
|
tag
|
||
|
}, node => {
|
||
|
res = node;
|
||
|
return node;
|
||
|
});
|
||
|
return res;
|
||
|
}
|
||
|
function findBundleInsertIndex(content) {
|
||
|
// HTML document order (https://html.spec.whatwg.org/multipage/syntax.html#writing)
|
||
|
// - Any number of comments and ASCII whitespace.
|
||
|
// - A DOCTYPE.
|
||
|
// - Any number of comments and ASCII whitespace.
|
||
|
// - The document element, in the form of an html element.
|
||
|
// - Any number of comments and ASCII whitespace.
|
||
|
//
|
||
|
// -> Insert before first non-metadata (or script) element; if none was found, after the doctype
|
||
|
|
||
|
let doctypeIndex;
|
||
|
for (let index = 0; index < content.length; index++) {
|
||
|
const node = content[index];
|
||
|
if (node && node.tag && !metadataContent.has(node.tag)) {
|
||
|
return index;
|
||
|
}
|
||
|
if (typeof node === 'string' && node.toLowerCase().startsWith('<!doctype')) {
|
||
|
doctypeIndex = index;
|
||
|
}
|
||
|
}
|
||
|
return doctypeIndex ? doctypeIndex + 1 : 0;
|
||
|
}
|