vite-plugin-head
Version:
Modify, add, delete Metadata in the head element.
139 lines (138 loc) • 3.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const parse5_1 = require("parse5");
const traverse = (root, options) => {
const visit = (node, parent) => {
let res;
if (!node) {
return;
}
if (options && options.pre) {
res = options.pre(node, parent);
}
if (options) {
for (const key in options) {
if (key !== "pre" && key !== "post" && node.nodeName === key) {
const call = options[key];
call && call(node, parent);
}
}
}
let { childNodes } = node;
if (node.content &&
Array.isArray(node.content.childNodes)) {
({ childNodes } = node.content);
}
if (res !== false && Array.isArray(childNodes) && childNodes.length >= 0) {
childNodes.forEach((child) => {
visit(child, node);
});
}
if (options && options.post) {
options.post(node, parent);
}
};
visit(root, null);
};
/**
* get attrs
* @param node element
* @param keys default all
*/
function getAttrs(node, keys) {
const obj = {};
for (let index = 0; index < node.attrs?.length; index++) {
const attr = node.attrs[index];
if (!keys) {
if (attr?.name) {
obj[attr.name] = attr.value;
}
continue;
}
if (attr?.name && keys.includes(attr.name)) {
obj[attr.name] = attr.value;
}
}
return obj;
}
function handleCssPreload(node) {
let attrRel = null;
let attrHref = null;
if (!node?.attrs) {
return;
}
for (let index = 0; index < node.attrs?.length; index++) {
const attr = node.attrs[index];
if (attr?.name === "rel") {
attrRel = attr;
}
if (attr?.name === "href") {
attrHref = attr;
}
}
if (attrRel && attrHref && /(.css)$/.test(attrHref.value)) {
attrRel.value = "preload";
}
}
function handleTitle(node, title) {
traverse(node, {
"#text": (tNode) => {
tNode.value = title;
},
});
}
function handleTransformLink(node, transformLink) {
const attrs = getAttrs(node);
const newAttrs = transformLink(attrs, attrs["href"]);
const attrList = [];
for (const key in newAttrs) {
if (newAttrs[key]) {
attrList.push({
name: key,
value: newAttrs[key],
});
}
}
node.attrs = attrList;
}
function headTransform(html, option) {
const htmlAst = (0, parse5_1.parse)(html, {
sourceCodeLocationInfo: true,
});
let hasTitle = false;
let headNode = null;
traverse(htmlAst, {
head(node) {
headNode = node;
},
link(node) {
if (option?.cssPreload) {
handleCssPreload(node);
}
if (option?.transformLink) {
handleTransformLink(node, option.transformLink);
}
},
title(node) {
hasTitle = true;
if (option?.title) {
handleTitle(node, option.title);
}
},
});
if (!hasTitle && headNode) {
const titleNode = (0, parse5_1.parseFragment)(`<title>${option.title}</title>`)
.childNodes[0];
headNode.childNodes.unshift(titleNode);
titleNode.parentNode = headNode;
}
const resHtml = (0, parse5_1.serialize)(htmlAst);
return resHtml;
}
exports.default = (option) => ({
name: "vite-plugin-head",
enforce: "post",
transformIndexHtml(html) {
return headTransform(html, option);
},
});