feat: add next helper for sibilings

This commit is contained in:
三咲智子 Kevin Deng 2024-02-26 22:37:08 +08:00
parent 2075042956
commit 2c15171dcf
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
7 changed files with 100 additions and 35 deletions

View File

@ -0,0 +1,19 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: children transform > children & sibling references 1`] = `
"import { next as _next, setText as _setText, createTextNode as _createTextNode, insert as _insert, template as _template } from 'vue/vapor';
const t0 = _template("<div><p></p> <!><p></p></div>")
export function render(_ctx) {
const n4 = t0()
const n0 = n4.firstChild
const n3 = _next(n0, 2)
const n2 = n3.nextSibling
_setText(n0, 'first')
const n1 = _createTextNode()
_setText(n1, 'second', " ", 'third', " ")
_setText(n2, 'forth')
_insert(n1, n4, n3)
return n4
}"
`;

View File

@ -12,12 +12,12 @@ export function render(_ctx) {
`;
exports[`compiler: v-once > basic 1`] = `
"import { children as _children, createTextNode as _createTextNode, setText as _setText, setClass as _setClass, prepend as _prepend, template as _template } from 'vue/vapor';
"import { createTextNode as _createTextNode, setText as _setText, setClass as _setClass, prepend as _prepend, template as _template } from 'vue/vapor';
const t0 = _template("<div><span></span></div>")
export function render(_ctx) {
const n2 = t0()
const n1 = _children(n2, 0)
const n1 = n2.firstChild
const n0 = _createTextNode()
_setText(n0, _ctx.msg, " ")
_setClass(n1, _ctx.clz)
@ -37,12 +37,12 @@ export function render(_ctx) {
`;
exports[`compiler: v-once > on nested plain element 1`] = `
"import { children as _children, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
"import { setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
const t0 = _template("<div><div></div></div>")
export function render(_ctx) {
const n1 = t0()
const n0 = _children(n1, 0)
const n0 = n1.firstChild
_setDynamicProp(n0, "id", _ctx.foo)
return n1
}"

View File

@ -1,3 +1,39 @@
import { makeCompile } from './_utils'
import {
transformChildren,
transformElement,
transformText,
transformVIf,
} from '../../src'
const compileWithElementTransform = makeCompile({
nodeTransforms: [
transformText,
transformVIf,
transformElement,
transformChildren,
],
})
describe('compiler: children transform', () => {
test.todo('basic')
test('children & sibling references', () => {
const { code, vaporHelpers } = compileWithElementTransform(
`<div>
<p>{{'first'}}</p>
{{'second'}}
{{'third'}}
<p>{{'forth'}}</p>
</div>`,
)
expect(code).toMatchSnapshot()
expect(Array.from(vaporHelpers)).containSubset([
'next',
'setText',
'createTextNode',
'insert',
'template',
])
})
})

View File

@ -1,6 +1,6 @@
import type { CodegenContext } from '../generate'
import { DynamicFlag, type IRDynamicInfo } from '../ir'
import { NEWLINE, buildCodeFragment, genCall } from './utils'
import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils'
export function genTemplates(
templates: string[],
@ -19,7 +19,7 @@ export function genChildren(
context: CodegenContext,
from: number,
paths: number[] = [],
) {
): CodeFragment[] {
const { vaporHelper } = context
const [frag, push] = buildCodeFragment()
let offset = 0
@ -29,6 +29,7 @@ export function genChildren(
push(NEWLINE, `const n${id} = t${template}()`)
}
let prev: [id: number, elementIndex: number] | undefined
for (const [index, child] of children.entries()) {
if (child.flags & DynamicFlag.NON_TEMPLATE) {
offset--
@ -44,20 +45,34 @@ export function genChildren(
const elementIndex = Number(index) + offset
const newPaths = [...paths, elementIndex]
if (id !== undefined) {
push(
NEWLINE,
`const n${id} = `,
...genCall(
vaporHelper('children'),
`n${from}`,
...newPaths.map(String),
),
)
push(...genChildren(child, context, id, []))
} else {
if (id === undefined) {
push(...genChildren(child, context, from, newPaths))
continue
}
push(NEWLINE, `const n${id} = `)
if (prev) {
const offset = elementIndex - prev[1]
if (offset === 1) {
push(`n${prev[0]}.nextSibling`)
} else {
push(...genCall(vaporHelper('next'), `n${prev[0]}`, String(offset)))
}
} else {
if (newPaths.length === 1 && newPaths[0] === 0) {
push(`n${from}.firstChild`)
} else {
push(
...genCall(
vaporHelper('children'),
`n${from}`,
...newPaths.map(String),
),
)
}
}
prev = [id, elementIndex]
push(...genChildren(child, context, id, []))
}
return frag

View File

@ -21,7 +21,6 @@ export const transformChildren: NodeTransform = (node, context) => {
if (!isFragment && node.type !== NodeTypes.ELEMENT) return
let referencedCount = 0
for (const [i, child] of node.children.entries()) {
const childContext = createContext(
child,
@ -42,18 +41,12 @@ export const transformChildren: NodeTransform = (node, context) => {
}
} else {
context.childrenTemplate.push(childContext.template)
if (childContext.dynamic.flags & DynamicFlag.REFERENCED) {
referencedCount++
}
}
context.dynamic.children[i] = childContext.dynamic
}
if (!isFragment) {
if (referencedCount > 1) {
context.reference()
}
processDynamicChildren(context as TransformContext<ElementNode>)
}
}

View File

@ -1,5 +1,3 @@
import { isArray } from '@vue/shared'
/*! #__NO_SIDE_EFFECTS__ */
export function template(html: string) {
let node: ChildNode
@ -13,15 +11,19 @@ export function template(html: string) {
}
/*! #__NO_SIDE_EFFECTS__ */
export function children(node: Node | Node[], ...paths: number[]): Node {
export function children(node: Node, ...paths: number[]): Node {
for (const idx of paths) {
if (isArray(node)) {
node = node[idx]
} else {
for (let i = 0; i <= idx; i++) {
node = (node as Node)[i === 0 ? 'firstChild' : 'nextSibling']!
}
for (let i = 0; i <= idx; i++) {
node = (node as Node)[i === 0 ? 'firstChild' : 'nextSibling']!
}
}
return node as Node
}
/*! #__NO_SIDE_EFFECTS__ */
export function next(node: Node, offset: number): Node {
for (let i = 0; i < offset; i++) {
node = (node as Node).nextSibling!
}
return node as Node
}

View File

@ -73,7 +73,7 @@ export {
type DirectiveModifiers,
} from './directives'
export { template, children } from './dom/template'
export { template, children, next } from './dom/template'
export { insert, prepend, remove, createTextNode } from './dom/element'
export { setStyle } from './dom/style'
export {