126 lines
3.6 KiB
JavaScript
126 lines
3.6 KiB
JavaScript
const { parse, compile: compilerDomCompile } = require('@vue/compiler-dom');
|
|
|
|
const COMMENT_NODE_TYPE = 3;
|
|
|
|
const hasProp = (node, prop) => node.props?.some((p) => p.name === prop);
|
|
|
|
function modifyKeysInsideTemplateTag(templateNode) {
|
|
if (!templateNode.tag === 'template' || !hasProp(templateNode, 'for')) {
|
|
return;
|
|
}
|
|
|
|
let keyCandidate = null;
|
|
for (const node of templateNode.children) {
|
|
const keyBindingIndex = node.props
|
|
? node.props.findIndex((prop) => prop.arg && prop.arg.content === 'key')
|
|
: -1;
|
|
|
|
if (keyBindingIndex !== -1 && !hasProp(node, 'for')) {
|
|
if (!keyCandidate) {
|
|
keyCandidate = node.props[keyBindingIndex];
|
|
}
|
|
node.props.splice(keyBindingIndex, 1);
|
|
}
|
|
}
|
|
|
|
if (keyCandidate) {
|
|
templateNode.props.push(keyCandidate);
|
|
}
|
|
}
|
|
|
|
function getSlotName(node) {
|
|
return node?.props?.find((prop) => prop.name === 'slot')?.arg?.content;
|
|
}
|
|
|
|
function filterCommentNodeAndTrailingSpace(node, idx, list) {
|
|
if (node.type === COMMENT_NODE_TYPE) {
|
|
return false;
|
|
}
|
|
|
|
if (node.content !== ' ') {
|
|
return true;
|
|
}
|
|
|
|
if (list[idx - 1]?.type === COMMENT_NODE_TYPE) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function filterCommentNodes(node) {
|
|
const { length: originalLength } = node.children;
|
|
// eslint-disable-next-line no-param-reassign
|
|
node.children = node.children.filter(filterCommentNodeAndTrailingSpace);
|
|
if (node.children.length !== originalLength) {
|
|
// trim remaining spaces
|
|
while (node.children.at(-1)?.content === ' ') {
|
|
node.children.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
function dropVOnceForChildrenInsideVIfBecauseOfIssue7725(node) {
|
|
// See https://github.com/vuejs/core/issues/7725 for details
|
|
if (!hasProp(node, 'if')) {
|
|
return;
|
|
}
|
|
|
|
node.children?.forEach((child) => {
|
|
if (Array.isArray(child.props)) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
child.props = child.props.filter((prop) => prop.name !== 'once');
|
|
}
|
|
});
|
|
}
|
|
|
|
function fixSameSlotsInsideTemplateFailingWhenUsingWhitespacePreserveDueToIssue6063(node) {
|
|
// See https://github.com/vuejs/core/issues/6063 for details
|
|
// eslint-disable-next-line no-param-reassign
|
|
node.children = node.children.filter((child, idx) => {
|
|
if (child.content !== ' ') {
|
|
// We need to drop only comment nodes
|
|
return true;
|
|
}
|
|
|
|
const previousNodeSlotName = getSlotName(node.children[idx - 1]);
|
|
const nextNodeSlotName = getSlotName(node.children[idx + 1]);
|
|
|
|
if (previousNodeSlotName && previousNodeSlotName === nextNodeSlotName) {
|
|
// We have a space beween two slot entries with same slot name, we need to drop it
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
parse,
|
|
compile(template, options) {
|
|
const rootNode = parse(template, options);
|
|
|
|
const pendingNodes = [rootNode];
|
|
while (pendingNodes.length) {
|
|
const currentNode = pendingNodes.pop();
|
|
if (Array.isArray(currentNode.children)) {
|
|
// This one will be dropped all together with compiler when we drop Vue.js 2 support
|
|
modifyKeysInsideTemplateTag(currentNode);
|
|
|
|
dropVOnceForChildrenInsideVIfBecauseOfIssue7725(currentNode);
|
|
|
|
// See https://github.com/vuejs/core/issues/7909 for details
|
|
// However, this issue applies not only to root-level nodes
|
|
// But on any level comments could change slot emptiness detection
|
|
// so we simply drop them
|
|
filterCommentNodes(currentNode);
|
|
|
|
fixSameSlotsInsideTemplateFailingWhenUsingWhitespacePreserveDueToIssue6063(currentNode);
|
|
|
|
currentNode.children.forEach((child) => pendingNodes.push(child));
|
|
}
|
|
}
|
|
|
|
return compilerDomCompile(rootNode, options);
|
|
},
|
|
};
|