Skip to content

Commit 8059826

Browse files
committed
fix(compiler-vapor): deal with formatting elements and ambiguous cases
1 parent f6ced78 commit 8059826

File tree

4 files changed

+114
-9
lines changed

4 files changed

+114
-9
lines changed

packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`compiler: element transform > b in nested divs 1`] = `
4+
"import { template as _template } from 'vue';
5+
const t0 = _template("<div><div><b></b></div><div>", true)
6+
7+
export function render(_ctx) {
8+
const n0 = t0()
9+
return n0
10+
}"
11+
`;
12+
313
exports[`compiler: element transform > MathML 1`] = `
414
"import { template as _template } from 'vue';
515
const t0 = _template("<math><mrow><mi>x</mi></mrow></math>", true, 2)
@@ -477,7 +487,7 @@ export function render(_ctx) {
477487
478488
exports[`compiler: element transform > props + children 1`] = `
479489
"import { template as _template } from 'vue';
480-
const t0 = _template("<div id=\\"foo\\"><span></span><main><b></b><div><div><span></span><span>", true)
490+
const t0 = _template("<div id=\\"foo\\"><span><b></b></span><main><b></b><div><div><span></span><span>", true)
481491
482492
export function render(_ctx) {
483493
const n0 = t0()
@@ -520,6 +530,26 @@ export function render(_ctx) {
520530
}"
521531
`;
522532
533+
exports[`compiler: element transform > pure nested divs 1`] = `
534+
"import { template as _template } from 'vue';
535+
const t0 = _template("<div><div><div></div></div><div>", true)
536+
537+
export function render(_ctx) {
538+
const n0 = t0()
539+
return n0
540+
}"
541+
`;
542+
543+
exports[`compiler: element transform > span in nested divs 1`] = `
544+
"import { template as _template } from 'vue';
545+
const t0 = _template("<div><div><span></div><div>", true)
546+
547+
export function render(_ctx) {
548+
const n0 = t0()
549+
return n0
550+
}"
551+
`;
552+
523553
exports[`compiler: element transform > static props 1`] = `
524554
"import { template as _template } from 'vue';
525555
const t0 = _template("<div id=\\"foo\\" class=\\"bar\\">", true)

packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,11 +602,47 @@ describe('compiler: element transform', () => {
602602

603603
test('props + children', () => {
604604
const { code, ir } = compileWithElementTransform(
605-
`<div id="foo"><span/><main><b/><div><div><span/><span/></div></div></main></div>`,
605+
`<div id="foo"><span><b/></span><main><b/><div><div><span/><span/></div></div></main></div>`,
606606
)
607607

608608
const template =
609-
'<div id="foo"><span></span><main><b></b><div><div><span></span><span>'
609+
'<div id="foo"><span><b></b></span><main><b></b><div><div><span></span><span>'
610+
expect(code).toMatchSnapshot()
611+
expect(code).contains(JSON.stringify(template))
612+
expect(ir.template).toMatchObject([template])
613+
expect(ir.block.effect).lengthOf(0)
614+
})
615+
616+
test('span in nested divs', () => {
617+
const { code, ir } = compileWithElementTransform(
618+
`<div><div><span/></div><div/></div>`,
619+
)
620+
621+
const template = '<div><div><span></div><div>'
622+
expect(code).toMatchSnapshot()
623+
expect(code).contains(JSON.stringify(template))
624+
expect(ir.template).toMatchObject([template])
625+
expect(ir.block.effect).lengthOf(0)
626+
})
627+
628+
test('b in nested divs', () => {
629+
const { code, ir } = compileWithElementTransform(
630+
`<div><div><b/></div><div/></div>`,
631+
)
632+
633+
const template = '<div><div><b></b></div><div>'
634+
expect(code).toMatchSnapshot()
635+
expect(code).contains(JSON.stringify(template))
636+
expect(ir.template).toMatchObject([template])
637+
expect(ir.block.effect).lengthOf(0)
638+
})
639+
640+
test('pure nested divs', () => {
641+
const { code, ir } = compileWithElementTransform(
642+
`<div><div><div/></div><div/></div>`,
643+
)
644+
645+
const template = '<div><div><div></div></div><div>'
610646
expect(code).toMatchSnapshot()
611647
expect(code).contains(JSON.stringify(template))
612648
expect([...ir.template.keys()]).toMatchObject([template])

packages/compiler-vapor/src/transforms/transformElement.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
capitalize,
2222
extend,
2323
isBuiltInDirective,
24+
isFormattingTag,
2425
isVoidTag,
2526
makeMap,
2627
} from '@vue/shared'
@@ -77,14 +78,43 @@ export const transformElement: NodeTransform = (node, context) => {
7778
getEffectIndex,
7879
)
7980

80-
let { index, parent } = context
81-
const isLastChild =
82-
parent &&
83-
parent.node.children.filter(child => child.type !== NodeTypes.COMMENT)
84-
.length ===
85-
index + 1
81+
let { index, parent, block } = context
82+
let isLastChild = true
83+
const checkIsLastChild = () => {
84+
isLastChild &&=
85+
!!parent &&
86+
(block !== parent.block ||
87+
parent.node.children.filter(child => child.type !== NodeTypes.COMMENT)
88+
.length ===
89+
index + 1)
90+
}
8691

92+
checkIsLastChild()
8793
const singleRoot = isSingleRoot(context)
94+
95+
// If it is a formatting tag, close tag is absolutely required if it's not on the right-most path of the tree
96+
// => https://html.spec.whatwg.org/multipage/parsing.html#reconstruct-the-active-formatting-elements
97+
// If the parent has the same tag, then close tag is required if it's not on the right-most path of the tree
98+
// => The parent's close tag will close the child otherwise; disambiguation is needed
99+
if (
100+
isFormattingTag(node.tag) ||
101+
(parent &&
102+
parent.node.type === NodeTypes.ELEMENT &&
103+
node.tag === parent.node.tag)
104+
) {
105+
while (
106+
isLastChild &&
107+
parent &&
108+
parent.parent &&
109+
block === parent.parent.block &&
110+
parent.node.type === NodeTypes.ELEMENT
111+
) {
112+
index = parent.index
113+
parent = parent.parent
114+
checkIsLastChild()
115+
}
116+
}
117+
88118
if (isComponent) {
89119
transformComponentElement(
90120
node as ComponentNode,

packages/shared/src/domTagConfig.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ const MATH_TAGS =
3838
const VOID_TAGS =
3939
'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr'
4040

41+
// https://html.spec.whatwg.org/multipage/parsing.html#formatting
42+
const FORMATTING_TAGS = 'a,b,big,code,em,font,i,nobr,s,small,strike,strong,tt,u'
43+
4144
/**
4245
* Compiler only.
4346
* Do NOT use in runtime code paths unless behind `__DEV__` flag.
@@ -62,3 +65,9 @@ export const isMathMLTag: (key: string) => boolean =
6265
*/
6366
export const isVoidTag: (key: string) => boolean =
6467
/*@__PURE__*/ makeMap(VOID_TAGS)
68+
/**
69+
* Compiler only.
70+
* Do NOT use in runtime code paths unless behind `__DEV__` flag.
71+
*/
72+
export const isFormattingTag: (key: string) => boolean =
73+
/*@__PURE__*/ makeMap(FORMATTING_TAGS)

0 commit comments

Comments
 (0)