Skip to content

Commit 1248172

Browse files
committed
wip: save
1 parent 3108d91 commit 1248172

File tree

3 files changed

+162
-19
lines changed

3 files changed

+162
-19
lines changed

packages/runtime-vapor/__tests__/hydration.spec.ts

+106-1
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ describe('Vapor Mode hydration', () => {
264264
)
265265
})
266266

267-
test('consecutive component with anchor insertion', async () => {
267+
test('consecutive components with anchor insertion', async () => {
268268
const { container, data } = await testHydration(
269269
`<template>
270270
<div>
@@ -344,6 +344,111 @@ describe('Vapor Mode hydration', () => {
344344
)
345345
})
346346

347+
test('fragment component with anchor insertion', async () => {
348+
const { container, data } = await testHydration(
349+
`<template>
350+
<div>
351+
<span/>
352+
<components.Child/>
353+
<span/>
354+
</div>
355+
</template>
356+
`,
357+
{
358+
Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
359+
},
360+
)
361+
expect(container.innerHTML).toMatchInlineSnapshot(
362+
`"<div><span></span><!--[--><div>foo</div>-foo<!--]--><span></span></div>"`,
363+
)
364+
365+
data.value = 'bar'
366+
await nextTick()
367+
expect(container.innerHTML).toMatchInlineSnapshot(
368+
`"<div><span></span><!--[--><div>bar</div>-bar<!--]--><span></span></div>"`,
369+
)
370+
})
371+
372+
test('consecutive fragment components with anchor insertion', async () => {
373+
const { container, data } = await testHydration(
374+
`<template>
375+
<div>
376+
<span/>
377+
<components.Child/>
378+
<components.Child/>
379+
<span/>
380+
</div>
381+
</template>
382+
`,
383+
{
384+
Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
385+
},
386+
)
387+
expect(container.innerHTML).toMatchInlineSnapshot(
388+
`"<div><span></span><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><span></span></div>"`,
389+
)
390+
391+
data.value = 'bar'
392+
await nextTick()
393+
expect(container.innerHTML).toMatchInlineSnapshot(
394+
`"<div><span></span><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><span></span></div>"`,
395+
)
396+
})
397+
398+
test('mixed fragment component and element with anchor insertion', async () => {
399+
const { container, data } = await testHydration(
400+
`<template>
401+
<div>
402+
<span/>
403+
<components.Child/>
404+
<span/>
405+
<components.Child/>
406+
<span/>
407+
</div>
408+
</template>
409+
`,
410+
{
411+
Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
412+
},
413+
)
414+
expect(container.innerHTML).toMatchInlineSnapshot(
415+
`"<div><span></span><!--[--><div>foo</div>-foo<!--]--><span></span><!--[--><div>foo</div>-foo<!--]--><span></span></div>"`,
416+
)
417+
418+
data.value = 'bar'
419+
await nextTick()
420+
expect(container.innerHTML).toMatchInlineSnapshot(
421+
`"<div><span></span><!--[--><div>bar</div>-bar<!--]--><span></span><!--[--><div>bar</div>-bar<!--]--><span></span></div>"`,
422+
)
423+
})
424+
425+
test('mixed fragment component and text with anchor insertion', async () => {
426+
const { container, data } = await testHydration(
427+
`<template>
428+
<div>
429+
<span/>
430+
<components.Child/>
431+
{{ data }}
432+
<components.Child/>
433+
<span/>
434+
</div>
435+
</template>
436+
`,
437+
{
438+
Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
439+
},
440+
)
441+
expect(container.innerHTML).toMatchInlineSnapshot(
442+
`"<div><span></span><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><!--[[--> <!--]]--><!--[[--> foo <!--]]--><!--[[--> <!--]]--><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><span></span></div>"`,
443+
)
444+
445+
data.value = 'bar'
446+
await nextTick()
447+
expect(container.innerHTML).toMatchInlineSnapshot(
448+
`"<div><span></span><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><!--[[--> <!--]]--><!--[[--> bar <!--]]--><!--[[--> <!--]]--><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><span></span></div>"`,
449+
)
450+
})
451+
347452
test.todo('if')
348453

349454
test.todo('for')

packages/runtime-vapor/src/dom/hydration.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const isComment = (node: Node, data: string): node is Anchor =>
4646
*/
4747
function adoptTemplateImpl(node: Node, template: string): Node | null {
4848
if (!(template[0] === '<' && template[1] === '!')) {
49-
while (node.nodeType === 8) node = next(node)
49+
while (node.nodeType === 8) node = node.nextSibling!
5050
}
5151

5252
if (__DEV__) {
@@ -119,3 +119,33 @@ function locateHydrationNodeImpl() {
119119
resetInsertionState()
120120
currentHydrationNode = node
121121
}
122+
123+
export function isDynamicAnchor(node: Node): node is Comment {
124+
return isComment(node, '[[') || isComment(node, ']]')
125+
}
126+
127+
export function isEmptyText(node: Node): node is Text {
128+
return node.nodeType === 3 && !(node as Text).data.trim()
129+
}
130+
131+
export function locateEndAnchor(
132+
node: Node | null,
133+
open = '[',
134+
close = ']',
135+
): Node | null {
136+
let match = 0
137+
while (node) {
138+
node = node.nextSibling
139+
if (node && node.nodeType === 8) {
140+
if ((node as Comment).data === open) match++
141+
if ((node as Comment).data === close) {
142+
if (match === 0) {
143+
return node
144+
} else {
145+
match--
146+
}
147+
}
148+
}
149+
}
150+
return null
151+
}

packages/runtime-vapor/src/dom/node.ts

+25-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
/*! #__NO_SIDE_EFFECTS__ */
2-
3-
import { isComment, isHydrating } from './hydration'
1+
import {
2+
isComment,
3+
isDynamicAnchor,
4+
isEmptyText,
5+
isHydrating,
6+
locateEndAnchor,
7+
} from './hydration'
48

9+
/*! #__NO_SIDE_EFFECTS__ */
510
export function createTextNode(value = ''): Text {
611
return document.createTextNode(value)
712
}
@@ -23,25 +28,28 @@ export function child(node: ParentNode): Node {
2328

2429
/*! #__NO_SIDE_EFFECTS__ */
2530
export function nthChild(node: Node, i: number): Node {
26-
return node.childNodes[i]
31+
if (!isHydrating) return node.childNodes[i]
32+
33+
let n = node.firstChild!
34+
for (let start = 0; start < i; start++) {
35+
n = next(n) as ChildNode
36+
}
37+
return n
2738
}
2839

2940
/*! #__NO_SIDE_EFFECTS__ */
3041
export function next(node: Node): Node {
42+
if (!isHydrating) return node.nextSibling!
43+
44+
// process fragment as a single node
45+
if (node && isComment(node, '[')) {
46+
node = locateEndAnchor(node)!
47+
}
48+
3149
let n = node.nextSibling!
32-
if (isHydrating) {
33-
// skip dynamic anchors and empty text nodes
34-
while (n && (isDynamicAnchor(n) || isEmptyText(n))) {
35-
n = n.nextSibling!
36-
}
50+
// skip dynamic anchors and empty text nodes
51+
while (n && (isDynamicAnchor(n) || isEmptyText(n))) {
52+
n = n.nextSibling!
3753
}
3854
return n
3955
}
40-
41-
function isDynamicAnchor(node: Node): node is Comment {
42-
return isComment(node, '[[') || isComment(node, ']]')
43-
}
44-
45-
function isEmptyText(node: Node): node is Text {
46-
return node.nodeType === 3 && !(node as Text).data.trim()
47-
}

0 commit comments

Comments
 (0)