Skip to content

Commit c8625fa

Browse files
committed
feat(compiler): support templateRef
1 parent 4363374 commit c8625fa

File tree

7 files changed

+187
-8
lines changed

7 files changed

+187
-8
lines changed

src/core/compiler/compile.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818

1919
import { transformElement } from './transforms/transformElement'
2020
import { transformChildren } from './transforms/transformChildren'
21+
import { transformTemplateRef } from './transforms/transformTemplateRef'
2122
import {
2223
type HackOptions,
2324
IRNodeTypes,
@@ -132,7 +133,13 @@ export function getBaseTransformPreset(
132133
prefixIdentifiers?: boolean,
133134
): TransformPreset {
134135
return [
135-
[transformText, transformElement, transformVSlot, transformChildren],
136+
[
137+
transformTemplateRef,
138+
transformText,
139+
transformElement,
140+
transformVSlot,
141+
transformChildren,
142+
],
136143
{
137144
bind: transformVBind,
138145
on: transformVOn,

src/core/compiler/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export * from './ir'
1010
export { transformText } from './transforms/transformText'
1111
export { transformElement } from './transforms/transformElement'
1212
export { transformChildren } from './transforms/transformChildren'
13+
export { transformTemplateRef } from './transforms/transformTemplateRef'
1314
export { transformVBind } from './transforms/vBind'
1415
export { transformVOn } from './transforms/vOn'
1516
export { transformVSlot } from './transforms/vSlot'
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { IRNodeTypes } from '../ir'
2+
import { findProp, isConstantExpression, resolveExpression } from '../utils'
3+
import type { NodeTransform } from '../transform'
4+
5+
export const transformTemplateRef: NodeTransform = (node, context) => {
6+
if (node.type !== 'JSXElement') return
7+
8+
const dir = findProp(node, 'ref')
9+
if (!dir?.value) return
10+
11+
const value = resolveExpression(dir.value, context)
12+
13+
return () => {
14+
const id = context.reference()
15+
const effect = !isConstantExpression(value)
16+
effect &&
17+
context.registerOperation({
18+
type: IRNodeTypes.DECLARE_OLD_REF,
19+
id,
20+
})
21+
context.registerEffect([value], {
22+
type: IRNodeTypes.SET_TEMPLATE_REF,
23+
element: id,
24+
value,
25+
refFor: !!context.inVFor,
26+
effect,
27+
})
28+
}
29+
}

src/core/compiler/transforms/vFor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export function processMapCallExpression(
2929
argument.params[2] && resolveExpression(argument.params[2], context)
3030

3131
const returnExpression = getReturnExpression(argument)
32-
const keyProperty = findProp(returnExpression, context)
32+
const keyProp = findProp(returnExpression, 'key')
33+
const keyProperty = keyProp && resolveExpression(keyProp.value, context)
3334
return () => {
3435
exitBlock()
3536
context.registerOperation({

src/core/compiler/utils.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,11 @@ export function isMapCallExpression(
266266
)
267267
}
268268

269-
export function findProp(
270-
expression: Expression | undefined,
271-
context: TransformContext,
272-
) {
269+
export function findProp(expression: Expression | undefined, key: string) {
273270
if (expression?.type === 'JSXElement') {
274271
for (const attr of expression.openingElement.attributes) {
275-
if (attr.type === 'JSXAttribute' && attr.name.name === 'key') {
276-
return resolveExpression(attr.value, context)
272+
if (attr.type === 'JSXAttribute' && attr.name.name === key) {
273+
return attr
277274
}
278275
}
279276
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`compiler: template ref transform > dynamic ref 1`] = `
4+
"import { renderEffect as _renderEffect, setRef as _setRef, template as _template } from 'vue/vapor';
5+
const t0 = _template("<div></div>")
6+
7+
export function render(_ctx) {
8+
const n0 = t0()
9+
let r0
10+
_renderEffect(() => r0 = _setRef(n0, foo, r0))
11+
return n0
12+
}"
13+
`;
14+
15+
exports[`compiler: template ref transform > static ref 1`] = `
16+
"import { setRef as _setRef, template as _template } from 'vue/vapor';
17+
const t0 = _template("<div></div>")
18+
19+
export function render(_ctx) {
20+
const n0 = t0()
21+
_setRef(n0, "foo")
22+
return n0
23+
}"
24+
`;
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { describe, expect, test } from 'vitest'
2+
import {
3+
DynamicFlag,
4+
IRNodeTypes,
5+
transformChildren,
6+
transformElement,
7+
transformTemplateRef,
8+
} from '../../src/core/compiler/index'
9+
10+
import { makeCompile } from './_utils'
11+
12+
const compileWithTransformRef = makeCompile({
13+
nodeTransforms: [transformTemplateRef, transformElement, transformChildren],
14+
prefixIdentifiers: false,
15+
})
16+
17+
describe('compiler: template ref transform', () => {
18+
test('static ref', () => {
19+
const { ir, code } = compileWithTransformRef(`<div ref="foo" />`)
20+
21+
expect(ir.block.dynamic.children[0]).toMatchObject({
22+
id: 0,
23+
flags: DynamicFlag.REFERENCED,
24+
})
25+
expect(ir.template).toEqual(['<div></div>'])
26+
expect(ir.block.operation).lengthOf(1)
27+
expect(ir.block.operation[0]).toMatchObject({
28+
type: IRNodeTypes.SET_TEMPLATE_REF,
29+
element: 0,
30+
value: {
31+
content: 'foo',
32+
isStatic: true,
33+
loc: {
34+
start: { line: 1, column: 10, offset: 9 },
35+
end: { line: 1, column: 15, offset: 14 },
36+
},
37+
},
38+
})
39+
expect(code).matchSnapshot()
40+
expect(code).contains('_setRef(n0, "foo")')
41+
})
42+
43+
test('dynamic ref', () => {
44+
const { ir, code } = compileWithTransformRef(`<div ref={foo} />`)
45+
46+
expect(ir.block.dynamic.children[0]).toMatchObject({
47+
id: 0,
48+
flags: DynamicFlag.REFERENCED,
49+
})
50+
expect(ir.template).toEqual(['<div></div>'])
51+
expect(ir.block.operation).toMatchObject([
52+
{
53+
type: IRNodeTypes.DECLARE_OLD_REF,
54+
id: 0,
55+
},
56+
])
57+
expect(ir.block.effect).toMatchObject([
58+
{
59+
operations: [
60+
{
61+
type: IRNodeTypes.SET_TEMPLATE_REF,
62+
element: 0,
63+
value: {
64+
content: 'foo',
65+
isStatic: false,
66+
},
67+
},
68+
],
69+
},
70+
])
71+
expect(code).matchSnapshot()
72+
expect(code).contains('_setRef(n0, foo, r0)')
73+
})
74+
75+
// test('ref + v-if', () => {
76+
// const { ir, code } = compileWithTransformRef(
77+
// `<div ref="foo" v-if="true" />`,
78+
// )
79+
80+
// expect(ir.block.operation).lengthOf(1)
81+
// expect(ir.block.operation[0].type).toBe(IRNodeTypes.IF)
82+
83+
// const { positive } = ir.block.operation[0] as IfIRNode
84+
// expect(positive.operation).toMatchObject([
85+
// {
86+
// type: IRNodeTypes.SET_TEMPLATE_REF,
87+
// element: 2,
88+
// value: {
89+
// content: 'foo',
90+
// isStatic: true,
91+
// },
92+
// effect: false,
93+
// },
94+
// ])
95+
// expect(code).matchSnapshot()
96+
// expect(code).contains('_setRef(n2, "foo")')
97+
// })
98+
99+
// test('ref + v-for', () => {
100+
// const { ir, code } = compileWithTransformRef(
101+
// `<div ref="foo" v-for="item in [1,2,3]" />`,
102+
// )
103+
104+
// const { render } = ir.block.operation[0] as ForIRNode
105+
// expect(render.operation).toMatchObject([
106+
// {
107+
// type: IRNodeTypes.SET_TEMPLATE_REF,
108+
// element: 2,
109+
// value: {
110+
// content: 'foo',
111+
// isStatic: true,
112+
// },
113+
// refFor: true,
114+
// effect: false,
115+
// },
116+
// ])
117+
// expect(code).matchSnapshot()
118+
// expect(code).contains('_setRef(n2, "foo", void 0, true)')
119+
// })
120+
})

0 commit comments

Comments
 (0)