127 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			127 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);
 | |
|   },
 | |
| };
 |