// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: v-for > basic v-for 1`] = `
-"import { template as _template, fragment as _fragment, children as _children, on as _on, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, append as _append } from 'vue/vapor';
+"import { template as _template, fragment as _fragment, children as _children, on as _on, renderEffect as _renderEffect, setText as _setText, createFor as _createFor, append as _append } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n2 = t0()
const { 0: [n3],} = _children(n2)
_on(n3, "click", $event => (_ctx.remove(_block.s[0])))
- const _updateEffect = () => {
+ _renderEffect(() => {
const [item] = _block.s
_setText(n3, item)
- }
- _renderEffect(_updateEffect)
- return [n2, _updateEffect]
+ })
+ return n2
})
_append(n0, n1)
return n0
}"
`;
-exports[`compiler: v-for > basic v-for 2`] = `
+exports[`compiler: v-for > multi effect 1`] = `
+"import { template as _template, fragment as _fragment, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, createFor as _createFor, append as _append } from 'vue/vapor';
+
+export function render(_ctx) {
+ const t0 = _template("<div></div>")
+ const t1 = _fragment()
+ const n0 = t1()
+ const n1 = _createFor(() => (_ctx.items), (_block) => {
+ const n2 = t0()
+ const { 0: [n3],} = _children(n2)
+ _renderEffect(() => {
+ const [item, index] = _block.s
+ _setDynamicProp(n3, "item", item)
+ })
+ _renderEffect(() => {
+ const [item, index] = _block.s
+ _setDynamicProp(n3, "index", index)
+ })
+ return n2
+ })
+ _append(n0, n1)
+ return n0
+}"
+`;
+
+exports[`compiler: v-for > w/o value 1`] = `
"import { template as _template, fragment as _fragment, createFor as _createFor, append as _append } from 'vue/vapor';
export function render(_ctx) {
const n0 = t1()
const n1 = _createFor(() => (_ctx.items), (_block) => {
const n2 = t0()
- return [n2, () => {}]
+ return n2
})
_append(n0, n1)
return n0
IRNodeTypes,
transformElement,
transformInterpolation,
+ transformVBind,
transformVFor,
transformVOn,
} from '../../src'
const compileWithVFor = makeCompile({
nodeTransforms: [transformInterpolation, transformVFor, transformElement],
- directiveTransforms: { on: transformVOn },
+ directiveTransforms: {
+ bind: transformVBind,
+ on: transformVOn,
+ },
})
describe('compiler: v-for', () => {
expect((ir.operation[0] as ForIRNode).render.effect).lengthOf(1)
})
- test('basic v-for', () => {
+ test('multi effect', () => {
+ const { code } = compileWithVFor(
+ `<div v-for="(item, index) of items" :item="item" :index="index" />`,
+ )
+ expect(code).matchSnapshot()
+ })
+
+ test('w/o value', () => {
const { code } = compileWithVFor(`<div v-for=" of items">item</div>`)
expect(code).matchSnapshot()
})
+
+ test.todo('object de-structured value', () => {
+ const { code } = compileWithVFor(
+ '<span v-for="({ id, value }) in items">{{ id }}{{ value }}</span>',
+ )
+ expect(code).matchSnapshot()
+ })
})
oper: BlockFunctionIRNode,
context: CodegenContext,
args: CodeFragment[] = [],
- returnValue?: () => CodeFragment[],
): CodeFragment[] {
return [
'(',
...args,
') => {',
INDENT_START,
- ...genBlockFunctionContent(oper, context, returnValue),
+ ...genBlockFunctionContent(oper, context),
INDENT_END,
NEWLINE,
'}',
export function genBlockFunctionContent(
ir: BlockFunctionIRNode | RootIRNode,
context: CodegenContext,
- returnValue?: () => CodeFragment[],
): CodeFragment[] {
const { vaporHelper } = context
const [frag, push] = buildCodeFragment(
}
push(...genOperations(ir.operation, context))
- push(...genEffects(ir.effect, context))
-
- push(
- NEWLINE,
- 'return ',
- ...(returnValue ? returnValue() : [`n${ir.dynamic.id}`]),
- )
+ push(...(context.genEffect || genEffects)(ir.effect, context))
+ push(NEWLINE, `return n${ir.dynamic.id}`)
return frag
}
buildCodeFragment,
} from '../generate'
import type { ForIRNode, IREffect } from '../ir'
-import { genOperations } from './operation'
+import { genOperation } from './operation'
import { NewlineType } from '@vue/compiler-dom'
export function genFor(
const rawKey = key && key.content
const sourceExpr = ['() => (', ...genExpression(source, context), ')']
- let updateFn = '_updateEffect'
context.genEffect = genEffectInFor
const idMap: Record<string, string> = {}
if (rawValue) idMap[rawValue] = `_block.s[0]`
if (rawKey) idMap[rawKey] = `_block.s[1]`
- const blockRet = (): CodeFragment[] => [
- `[n${render.dynamic.id!}, ${updateFn}]`,
- ]
-
const blockFn = context.withId(
- () => genBlockFunction(render, context, ['_block'], blockRet),
+ () => genBlockFunction(render, context, ['_block']),
idMap,
)
]
function genEffectInFor(effects: IREffect[]): CodeFragment[] {
- if (!effects.length) {
- updateFn = '() => {}'
- return []
- }
+ const [frag, push] = buildCodeFragment()
- const [frag, push] = buildCodeFragment(INDENT_START)
- // const [value, key] = _block.s
+ const idMap: Record<string, string | null> = {}
+ if (value) idMap[value.content] = null
+ if (key) idMap[key.content] = null
+
+ let statement: CodeFragment[] = []
if (rawValue || rawKey) {
- push(
+ // const [value, key] = _block.s
+ statement = [
NEWLINE,
'const ',
'[',
rawKey && ', ',
rawKey && [rawKey, NewlineType.None, key.loc],
'] = _block.s',
- )
+ ]
}
- const idMap: Record<string, string | null> = {}
- if (value) idMap[value.content] = null
- if (key) idMap[key.content] = null
context.withId(() => {
- effects.forEach(effect =>
- push(...genOperations(effect.operations, context)),
- )
+ for (const { operations } of effects) {
+ push(
+ NEWLINE,
+ `${vaporHelper('renderEffect')}(() => {`,
+ INDENT_START,
+ ...statement,
+ )
+ operations.forEach(op => push(...genOperation(op, context)))
+ push(INDENT_END, NEWLINE, '})')
+ }
}, idMap)
- push(INDENT_END)
-
- return [
- NEWLINE,
- `const ${updateFn} = () => {`,
- ...frag,
- NEWLINE,
- '}',
- NEWLINE,
- `${vaporHelper('renderEffect')}(${updateFn})`,
- ]
+ return frag
}
}
return frag
}
-function genOperation(
+export function genOperation(
oper: OperationNode,
context: CodegenContext,
): CodeFragment[] {
}
export function genEffects(effects: IREffect[], context: CodegenContext) {
- if (context.genEffect) {
- return context.genEffect(effects)
- }
const [frag, push] = buildCodeFragment()
for (const effect of effects) {
push(...genEffect(effect, context))
return frag
}
-function genEffect({ operations }: IREffect, context: CodegenContext) {
+export function genEffect({ operations }: IREffect, context: CodegenContext) {
const { vaporHelper } = context
const [frag, push] = buildCodeFragment(
NEWLINE,
export const createFor = (
src: () => any[] | Record<string, string> | Set<any> | Map<any, any>,
- renderItem: (block: ForBlock) => [Block, () => void],
+ renderItem: (block: ForBlock) => Block,
getKey: ((item: any, index: number) => any) | null,
getMemo?: (item: any) => any[],
hydrationNode?: Node,
memo: getMemo && getMemo(item),
[fragmentKey]: true,
})
- const res = scope.run(() => renderItem(block))!
- block.nodes = res[0]
- block.update = res[1]
+ block.nodes = scope.run(() => renderItem(block))!
+ block.update = () => scope.effects.forEach(effect => effect.run())
if (getMemo) block.update()
if (parent) insert(block.nodes, parent, anchor)
return block