Skip to content

Commit 3242944

Browse files
committed
WIP: feat v-let
1 parent e3bc21f commit 3242944

File tree

11 files changed

+191
-9
lines changed

11 files changed

+191
-9
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`compiler: v-let transform complex expression 1`] = `
4+
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
5+
6+
export function render(_ctx, _cache) {
7+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
8+
((a=_ctx.foo+_ctx.bar) => _createElementVNode(\\"div\\", null, [
9+
((b=a+_ctx.baz) => _createElementVNode(\\"span\\", null, [
10+
_createTextVNode(_toDisplayString(b), 1 /* TEXT */)
11+
]))()
12+
]))()
13+
]))
14+
}"
15+
`;
16+
17+
exports[`compiler: v-let transform multiple declare 1`] = `
18+
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
19+
20+
export function render(_ctx, _cache) {
21+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
22+
((a=1, b=2) => _createElementVNode(\\"div\\", null, [
23+
_createTextVNode(_toDisplayString(a) + \\" \\" + _toDisplayString(b), 1 /* TEXT */)
24+
]))()
25+
]))
26+
}"
27+
`;
28+
29+
exports[`compiler: v-let transform nested v-let 1`] = `
30+
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
31+
32+
export function render(_ctx, _cache) {
33+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
34+
((a=1) => _createElementVNode(\\"div\\", null, [
35+
((b=1) => _createElementVNode(\\"span\\", null, [
36+
_createTextVNode(_toDisplayString(a) + _toDisplayString(b), 1 /* TEXT */)
37+
]))()
38+
]))()
39+
]))
40+
}"
41+
`;
42+
43+
exports[`compiler: v-let transform should work 1`] = `
44+
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
45+
46+
export function render(_ctx, _cache) {
47+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
48+
((a=1) => _createElementVNode(\\"div\\", null, [
49+
_createTextVNode(_toDisplayString(a), 1 /* TEXT */)
50+
]))()
51+
]))
52+
}"
53+
`;
54+
55+
exports[`compiler: v-let transform work with variable 1`] = `
56+
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
57+
58+
export function render(_ctx, _cache) {
59+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
60+
((a=_ctx.msg) => _createElementVNode(\\"div\\", null, [
61+
((b=a) => _createElementVNode(\\"span\\", null, [
62+
_createTextVNode(_toDisplayString(b), 1 /* TEXT */)
63+
]))()
64+
]))()
65+
]))
66+
}"
67+
`;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { baseCompile } from '../../src'
2+
3+
describe('compiler: v-let transform', () => {
4+
function compile(content: string) {
5+
return baseCompile(`<div>${content}</div>`, {
6+
mode: 'module',
7+
prefixIdentifiers: true
8+
}).code
9+
}
10+
11+
test('should work', () => {
12+
expect(compile(`<div v-let="a=1">{{a}}</div>`)).toMatchSnapshot()
13+
})
14+
15+
test('multiple declare', () => {
16+
expect(compile(
17+
`<div v-let="a=1, b=2">
18+
{{a}} {{b}}
19+
</div>`
20+
)).toMatchSnapshot()
21+
})
22+
23+
test('nested v-let', () => {
24+
expect(compile(
25+
`<div v-let="a=1">
26+
<span v-let="b=1">{{a}}{{b}}</span>
27+
</div>`
28+
)).toMatchSnapshot()
29+
})
30+
31+
test('work with variable', () => {
32+
expect(compile(
33+
`<div v-let="a=msg">
34+
<span v-let="b=a">{{b}}</span>
35+
</div>`
36+
)).toMatchSnapshot()
37+
})
38+
39+
test('complex expression', () => {
40+
expect(compile(
41+
`<div v-let="a=foo+bar">
42+
<span v-let="b=a+baz">{{b}}</span>
43+
</div>`
44+
)).toMatchSnapshot()
45+
})
46+
47+
})

packages/compiler-core/src/ast.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export interface PlainElementNode extends BaseElementNode {
137137
| SimpleExpressionNode // when hoisted
138138
| CacheExpression // when cached by v-once
139139
| MemoExpression // when cached by v-memo
140+
| CallExpression
140141
| undefined
141142
ssrCodegenNode?: TemplateLiteral
142143
}
@@ -323,7 +324,7 @@ export type JSChildNode =
323324

324325
export interface CallExpression extends Node {
325326
type: NodeTypes.JS_CALL_EXPRESSION
326-
callee: string | symbol
327+
callee: string | symbol | FunctionExpression
327328
arguments: (
328329
| string
329330
| symbol

packages/compiler-core/src/codegen.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -779,11 +779,23 @@ function genNullableArgs(args: any[]): CallExpression['arguments'] {
779779
// JavaScript
780780
function genCallExpression(node: CallExpression, context: CodegenContext) {
781781
const { push, helper, pure } = context
782-
const callee = isString(node.callee) ? node.callee : helper(node.callee)
782+
let callee
783+
if (isString(node.callee)) {
784+
callee = node.callee
785+
} else if (isSymbol(node.callee)) {
786+
callee = helper(node.callee)
787+
} else {
788+
// anonymous function.
789+
push(`(`)
790+
genNode(node.callee, context)
791+
push(`)`)
792+
}
793+
783794
if (pure) {
784795
push(PURE_ANNOTATION)
785796
}
786-
push(callee + `(`, node)
797+
callee && push(callee)
798+
push(`(`, node)
787799
genNodeList(node.arguments, context)
788800
push(`)`)
789801
}

packages/compiler-core/src/compile.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { transformModel } from './transforms/vModel'
1818
import { transformFilter } from './compat/transformFilter'
1919
import { defaultOnError, createCompilerError, ErrorCodes } from './errors'
2020
import { transformMemo } from './transforms/vMemo'
21+
import { trackVLetScopes, transformLet } from './transforms/vLet'
2122

2223
export type TransformPreset = [
2324
NodeTransform[],
@@ -32,6 +33,7 @@ export function getBaseTransformPreset(
3233
transformOnce,
3334
transformIf,
3435
transformMemo,
36+
transformLet,
3537
transformFor,
3638
...(__COMPAT__ ? [transformFilter] : []),
3739
...(!__BROWSER__ && prefixIdentifiers
@@ -46,6 +48,7 @@ export function getBaseTransformPreset(
4648
transformSlotOutlet,
4749
transformElement,
4850
trackSlotScopes,
51+
trackVLetScopes,
4952
transformText
5053
],
5154
{

packages/compiler-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export {
5252
trackVForSlotScopes,
5353
trackSlotScopes
5454
} from './transforms/vSlot'
55+
export { transformLet, trackVLetScopes } from './transforms/vLet'
5556
export {
5657
transformElement,
5758
resolveComponentType,

packages/compiler-core/src/transforms/hoistStatic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ function getConstantTypeOfHelperCall(
296296
): ConstantTypes {
297297
if (
298298
value.type === NodeTypes.JS_CALL_EXPRESSION &&
299-
!isString(value.callee) &&
299+
isSymbol(value.callee) &&
300300
allowHoistedHelperSet.has(value.callee)
301301
) {
302302
const arg = value.arguments[0] as JSChildNode

packages/compiler-core/src/transforms/transformExpression.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ export const transformExpression: NodeTransform = (node, context) => {
6969
dir.exp = processExpression(
7070
exp,
7171
context,
72-
// slot args must be processed as function params
73-
dir.name === 'slot'
72+
// slot and let args must be processed as function params
73+
dir.name === 'slot' || dir.name === 'let'
7474
)
7575
}
7676
if (arg && arg.type === NodeTypes.SIMPLE_EXPRESSION && !arg.isStatic) {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { NodeTransform } from '../transform'
2+
import { findDir } from '../utils'
3+
import {
4+
createCallExpression,
5+
createFunctionExpression,
6+
NodeTypes,
7+
PlainElementNode
8+
} from '../ast'
9+
10+
const seen = new WeakSet()
11+
12+
export const transformLet: NodeTransform = (node, context) => {
13+
if (node.type === NodeTypes.ELEMENT) {
14+
const dir = findDir(node, 'let')
15+
if (!dir || seen.has(node)) {
16+
return
17+
}
18+
seen.add(node)
19+
return () => {
20+
const codegenNode =
21+
node.codegenNode ||
22+
(context.currentNode as PlainElementNode).codegenNode
23+
if (codegenNode && codegenNode.type === NodeTypes.VNODE_CALL) {
24+
node.codegenNode = createCallExpression(createFunctionExpression(
25+
[dir.exp!],
26+
codegenNode,
27+
))
28+
}
29+
}
30+
}
31+
}
32+
33+
34+
export const trackVLetScopes: NodeTransform = (node, context) => {
35+
if (
36+
node.type === NodeTypes.ELEMENT
37+
) {
38+
const vLet = findDir(node, 'let')
39+
if (vLet) {
40+
const vLetExp = vLet.exp
41+
if (!__BROWSER__ && context.prefixIdentifiers) {
42+
vLetExp && context.addIdentifiers(vLetExp)
43+
}
44+
return () => {
45+
if (!__BROWSER__ && context.prefixIdentifiers) {
46+
vLetExp && context.removeIdentifiers(vLetExp)
47+
}
48+
}
49+
}
50+
}
51+
}

packages/compiler-core/src/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
WITH_MEMO,
4343
OPEN_BLOCK
4444
} from './runtimeHelpers'
45-
import { isString, isObject, hyphenate, extend, NOOP } from '@vue/shared'
45+
import { isString, isObject, hyphenate, extend, NOOP, isSymbol } from '@vue/shared'
4646
import { PropsExpression } from './transforms/transformElement'
4747
import { parseExpression } from '@babel/parser'
4848
import { Expression } from '@babel/types'
@@ -351,7 +351,7 @@ function getUnnormalizedProps(
351351
props.type === NodeTypes.JS_CALL_EXPRESSION
352352
) {
353353
const callee = props.callee
354-
if (!isString(callee) && propsHelperSet.has(callee)) {
354+
if (isSymbol(callee) && propsHelperSet.has(callee)) {
355355
return getUnnormalizedProps(
356356
props.arguments[0] as PropsExpression,
357357
callPath.concat(props)

packages/shared/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export const isReservedProp = /*#__PURE__*/ makeMap(
9292
)
9393

9494
export const isBuiltInDirective = /*#__PURE__*/ makeMap(
95-
'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo'
95+
'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo,let'
9696
)
9797

9898
const cacheStringFunction = <T extends (str: string) => string>(fn: T): T => {

0 commit comments

Comments
 (0)