exports[`compile > execution order > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = `
"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div>")
-const t1 = _template("<div><div></div><!><div></div><!><div><button>", true)
+const t1 = _template("<div><div></div><!><div></div><!><div><button></button>", true)
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
'<div><div><span>a</span><span>b</span></div></div>',
)
})
+
+test('always close tags', () => {
+ // button always needs closing tag
+ checkAbbr(
+ '<div><button>click</button></div>',
+ '<div><button>click</button>',
+ '<div><button>click</button></div>',
+ )
+
+ // select always needs closing tag
+ checkAbbr(
+ '<div><select></select></div>',
+ '<div><select></select>',
+ '<div><select></select></div>',
+ )
+
+ // table always needs closing tag
+ checkAbbr(
+ '<div><table></table></div>',
+ '<div><table></table>',
+ '<div><table></table></div>',
+ )
+
+ // textarea always needs closing tag
+ checkAbbr(
+ '<div><textarea></textarea></div>',
+ '<div><textarea></textarea>',
+ '<div><textarea></textarea></div>',
+ )
+
+ // template always needs closing tag
+ checkAbbr(
+ '<div><template></template></div>',
+ '<div><template></template>',
+ '<div><template></template></div>',
+ )
+
+ // script always needs closing tag
+ checkAbbr(
+ '<div><script></script></div>',
+ '<div><script></script>',
+ '<div><script></script></div>',
+ )
+
+ // without always-close elements, normal abbreviation should work
+ checkAbbr(
+ '<div><form><input></form></div>',
+ '<div><form><input>',
+ '<div><form><input></form></div>',
+ )
+})
camelize,
capitalize,
extend,
+ isAlwaysCloseTag,
isBuiltInDirective,
isFormattingTag,
isVoidTag,
return true
}
+ // Elements in the alwaysClose list cannot have their end tags omitted
+ // because the browser's HTML parser has special handling for them
+ if (isAlwaysCloseTag(node.tag)) {
+ return false
+ }
+
// Formatting tags and same-name nested tags require explicit closing
// unless on the rightmost path of the tree:
// - Formatting tags: https://html.spec.whatwg.org/multipage/parsing.html#reconstruct-the-active-formatting-elements
// https://html.spec.whatwg.org/multipage/parsing.html#formatting
const FORMATTING_TAGS = 'a,b,big,code,em,font,i,nobr,s,small,strike,strong,tt,u'
+// Elements that always require explicit closing tags due to HTML parsing rules.
+// These include:
+// - Formatting elements (a, b, i, etc.) - handled by FORMATTING_TAGS
+// - Elements with special parsing rules
+// - Scope boundary elements
+const ALWAYS_CLOSE_TAGS =
+ 'title,style,script,noscript,template,' + // raw text / special parsing
+ 'object,table,button,textarea,select,iframe,fieldset' // scope boundary / form elements
+
/**
* Compiler only.
* Do NOT use in runtime code paths unless behind `__DEV__` flag.
*/
export const isFormattingTag: (key: string) => boolean =
/*@__PURE__*/ makeMap(FORMATTING_TAGS)
+/**
+ * Compiler only.
+ * Do NOT use in runtime code paths unless behind `__DEV__` flag.
+ */
+export const isAlwaysCloseTag: (key: string) => boolean =
+ /*@__PURE__*/ makeMap(ALWAYS_CLOSE_TAGS)