提交 7d7f03b2 编写于 作者: fxy060608's avatar fxy060608

wip(uvue): transformExpression 支持 inline (增加 transformExpression.spec.ts)

上级 b96e1206
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`compiler: expression transform bindingMetadata inline mode 1`] = `"createElementVNode("view", null, toDisplayString(__props.props) + " " + toDisplayString(unref(setup)) + " " + toDisplayString(setupConst) + " " + toDisplayString(_ctx.data) + " " + toDisplayString(_ctx.options) + " " + toDisplayString(isNaN), 1 /* TEXT */)"`;
exports[`compiler: expression transform bindingMetadata non-inline mode 1`] = `"createElementVNode("view", null, toDisplayString($props.props) + " " + toDisplayString($setup.setup) + " " + toDisplayString($data.data) + " " + toDisplayString($options.options) + " " + toDisplayString(isNaN), 1 /* TEXT */)"`;
exports[`compiler: expression transform bindingMetadata should not prefix temp variable of for loop 1`] = `
"createVNode(_component_div, utsMapOf({
onClick: () => {
for (let i = 0; i < _ctx.list.length; i++) {
_ctx.log(i)
}
}
}), null, 8 /* PROPS */, ["onClick"])"
`;
exports[`compiler: expression transform bindingMetadata should not prefix temp variable of for...in 1`] = `
"createVNode(_component_div, utsMapOf({
onClick: () => {
for (const x in _ctx.list) {
_ctx.log(x)
}
}
}), null, 8 /* PROPS */, ["onClick"])"
`;
exports[`compiler: expression transform bindingMetadata should not prefix temp variable of for...of 1`] = `
"createVNode(_component_div, utsMapOf({
onClick: () => {
for (const x of _ctx.list) {
_ctx.log(x)
}
}
}), null, 8 /* PROPS */, ["onClick"])"
`;
// import { PatchFlagNames, PatchFlags } from '@vue/shared'
import {
BindingTypes,
ConstantTypes,
DirectiveNode,
ElementNode,
InterpolationNode,
NodeTypes,
baseParse as parse,
} from '@vue/compiler-core'
import { transform } from '../../../src/plugins/android/uvue/compiler/transform'
import { transformIf } from '../../../src/plugins/android/uvue/compiler/transforms/vIf'
import { transformExpression } from '../../../src/plugins/android/uvue/compiler/transforms/transformExpression'
import { compile as baseCompile } from '../../../src/plugins/android/uvue/compiler'
import { TemplateCompilerOptions } from '../../../src/plugins/android/uvue/compiler/options'
function parseWithExpressionTransform(
template: string,
options: TemplateCompilerOptions = {}
) {
const ast = parse(template)
transform(ast, {
prefixIdentifiers: true,
nodeTransforms: [transformIf, transformExpression],
...options,
})
return ast.children[0]
}
describe('compiler: expression transform', () => {
test('interpolation (root)', () => {
const node = parseWithExpressionTransform(`{{ foo }}`) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.foo`,
})
})
test('empty interpolation', () => {
const node = parseWithExpressionTransform(`{{}}`) as InterpolationNode
const node2 = parseWithExpressionTransform(`{{ }}`) as InterpolationNode
const node3 = parseWithExpressionTransform(
`<view>{{ }}</view>`
) as ElementNode
const objectToBeMatched = {
type: NodeTypes.SIMPLE_EXPRESSION,
content: ``,
}
expect(node.content).toMatchObject(objectToBeMatched)
expect(node2.content).toMatchObject(objectToBeMatched)
expect((node3.children[0] as InterpolationNode).content).toMatchObject(
objectToBeMatched
)
})
test('interpolation (children)', () => {
const el = parseWithExpressionTransform(
`<view>{{ foo }}</view>`
) as ElementNode
const node = el.children[0] as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.foo`,
})
})
test('interpolation (complex)', () => {
const el = parseWithExpressionTransform(
`<view>{{ foo + bar(baz.qux) }}</view>`
) as ElementNode
const node = el.children[0] as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `_ctx.foo` },
` + `,
{ content: `_ctx.bar` },
`(`,
{ content: `_ctx.baz` },
`.`,
{ content: `qux` },
`)`,
],
})
})
test('directive value', () => {
const node = parseWithExpressionTransform(
`<div v-foo:arg="baz"/>`
) as ElementNode
const arg = (node.props[0] as DirectiveNode).arg!
expect(arg).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `arg`,
})
const exp = (node.props[0] as DirectiveNode).exp!
expect(exp).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.baz`,
})
})
test('dynamic directive arg', () => {
const node = parseWithExpressionTransform(
`<div v-foo:[arg]="baz"/>`
) as ElementNode
const arg = (node.props[0] as DirectiveNode).arg!
expect(arg).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.arg`,
})
const exp = (node.props[0] as DirectiveNode).exp!
expect(exp).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.baz`,
})
})
test('should prefix complex expressions', () => {
const node = parseWithExpressionTransform(
`{{ foo(baz + 1, { key: kuz }) }}`
) as InterpolationNode
// should parse into compound expression
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{
content: `_ctx.foo`,
loc: {
source: `foo`,
start: {
offset: 3,
line: 1,
column: 4,
},
end: {
offset: 6,
line: 1,
column: 7,
},
},
},
`(`,
{
content: `_ctx.baz`,
loc: {
source: `baz`,
start: {
offset: 7,
line: 1,
column: 8,
},
end: {
offset: 10,
line: 1,
column: 11,
},
},
},
` + 1, { key: `,
{
content: `_ctx.kuz`,
loc: {
source: `kuz`,
start: {
offset: 23,
line: 1,
column: 24,
},
end: {
offset: 26,
line: 1,
column: 27,
},
},
},
` })`,
],
})
})
test('should not prefix whitelisted globals', () => {
const node = parseWithExpressionTransform(
`{{ Math.max(1, 2) }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [{ content: `Math` }, `.`, { content: `max` }, `(1, 2)`],
})
})
test('should not prefix reserved literals', () => {
function assert(exp: string) {
const node = parseWithExpressionTransform(
`{{ ${exp} }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: exp,
})
}
assert(`true`)
assert(`false`)
assert(`null`)
assert(`this`)
})
test('should not prefix id of a function declaration', () => {
const node = parseWithExpressionTransform(
`{{ function foo() { return bar } }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`function `,
{ content: `foo` },
`() { return `,
{ content: `_ctx.bar` },
` }`,
],
})
})
test('should not prefix params of a function expression', () => {
const node = parseWithExpressionTransform(
`{{ foo => foo + bar }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `foo` },
` => `,
{ content: `foo` },
` + `,
{ content: `_ctx.bar` },
],
})
})
test('should prefix default value of a function expression param', () => {
const node = parseWithExpressionTransform(
`{{ (foo = baz) => foo + bar }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`(`,
{ content: `foo` },
` = `,
{ content: `_ctx.baz` },
`) => `,
{ content: `foo` },
` + `,
{ content: `_ctx.bar` },
],
})
})
test('should not prefix function param destructuring', () => {
const node = parseWithExpressionTransform(
`{{ ({ foo }) => foo + bar }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`({ `,
{ content: `foo` },
` }) => `,
{ content: `foo` },
` + `,
{ content: `_ctx.bar` },
],
})
})
test('function params should not affect out of scope identifiers', () => {
const node = parseWithExpressionTransform(
`{{ { a: foo => foo, b: foo } }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`{ a: `,
{ content: `foo` },
` => `,
{ content: `foo` },
`, b: `,
{ content: `_ctx.foo` },
` }`,
],
})
})
test('should prefix default value of function param destructuring', () => {
const node = parseWithExpressionTransform(
`{{ ({ foo = bar }) => foo + bar }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`({ `,
{ content: `foo` },
` = `,
{ content: `_ctx.bar` },
` }) => `,
{ content: `foo` },
` + `,
{ content: `_ctx.bar` },
],
})
})
test('should not prefix an object property key', () => {
const node = parseWithExpressionTransform(
`{{ { foo() { baz() }, value: bar } }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`{ foo() { `,
{ content: `_ctx.baz` },
`() }, value: `,
{ content: `_ctx.bar` },
` }`,
],
})
})
test('should not duplicate object key with same name as value', () => {
const node = parseWithExpressionTransform(
`{{ { foo: foo } }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`{ foo: `, { content: `_ctx.foo` }, ` }`],
})
})
test('should prefix a computed object property key', () => {
const node = parseWithExpressionTransform(
`{{ { [foo]: bar } }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`{ [`,
{ content: `_ctx.foo` },
`]: `,
{ content: `_ctx.bar` },
` }`,
],
})
})
test('should prefix object property shorthand value', () => {
const node = parseWithExpressionTransform(
`{{ { foo } }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`{ foo: `, { content: `_ctx.foo` }, ` }`],
})
})
test('should not prefix id in a member expression', () => {
const node = parseWithExpressionTransform(
`{{ foo.bar.baz }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `_ctx.foo` },
`.`,
{ content: `bar` },
`.`,
{ content: `baz` },
],
})
})
test('should prefix computed id in a member expression', () => {
const node = parseWithExpressionTransform(
`{{ foo[bar][baz] }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `_ctx.foo` },
`[`,
{ content: `_ctx.bar` },
`][`,
{ content: '_ctx.baz' },
`]`,
],
})
})
test('should handle parse error', () => {
// const onError = vi.fn()
// parseWithExpressionTransform(`{{ a( }}`, { onError })
// expect(onError.mock.calls[0][0].message).toMatch(
// `Error parsing JavaScript expression: Unexpected token`
// )
})
test('should prefix in assignment', () => {
const node = parseWithExpressionTransform(
`{{ x = 1 }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [{ content: `_ctx.x` }, ` = 1`],
})
})
test('should prefix in assignment pattern', () => {
const node = parseWithExpressionTransform(
`{{ { x, y: [z] } = obj }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`{ x: `,
{ content: `_ctx.x` },
`, y: [`,
{ content: `_ctx.z` },
`] } = `,
{ content: `_ctx.obj` },
],
})
})
// #8295
test('should treat floating point number literals as constant', () => {
const node = parseWithExpressionTransform(
`{{ [1, 2.1] }}`
) as InterpolationNode
expect(node.content).toMatchObject({
constType: ConstantTypes.CAN_STRINGIFY,
})
})
describe('ES Proposals support', () => {
test('bigInt', () => {
const node = parseWithExpressionTransform(
`{{ 13000n }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `13000n`,
isStatic: false,
constType: ConstantTypes.CAN_STRINGIFY,
})
})
test('nullish coalescing', () => {
const node = parseWithExpressionTransform(
`{{ a ?? b }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [{ content: `_ctx.a` }, ` ?? `, { content: `_ctx.b` }],
})
})
test('optional chaining', () => {
const node = parseWithExpressionTransform(
`{{ a?.b?.c }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `_ctx.a` },
`?.`,
{ content: `b` },
`?.`,
{ content: `c` },
],
})
})
test('Enabling additional plugins', () => {
// enabling pipeline operator to replace filters:
// const node = parseWithExpressionTransform(`{{ a |> uppercase }}`, {
// expressionPlugins: [
// [
// 'pipelineOperator',
// {
// proposal: 'minimal',
// },
// ],
// ],
// }) as InterpolationNode
// expect(node.content).toMatchObject({
// type: NodeTypes.COMPOUND_EXPRESSION,
// children: [
// { content: `_ctx.a` },
// ` |> `,
// { content: `_ctx.uppercase` },
// ],
// })
})
})
describe('bindingMetadata', () => {
const bindingMetadata = {
props: BindingTypes.PROPS,
setup: BindingTypes.SETUP_MAYBE_REF,
setupConst: BindingTypes.SETUP_CONST,
data: BindingTypes.DATA,
options: BindingTypes.OPTIONS,
reactive: BindingTypes.SETUP_REACTIVE_CONST,
literal: BindingTypes.LITERAL_CONST,
isNaN: BindingTypes.SETUP_REF,
}
function compileWithBindingMetadata(
template: string,
options?: TemplateCompilerOptions
) {
return baseCompile(template, {
prefixIdentifiers: true,
bindingMetadata,
...options,
})
}
test('non-inline mode', () => {
const { code } = compileWithBindingMetadata(
`<view>{{ props }} {{ setup }} {{ data }} {{ options }} {{ isNaN }}</view>`
)
expect(code).toMatch(`$props.props`)
expect(code).toMatch(`$setup.setup`)
// expect(code).toMatch(`$setup.isNaN`)
expect(code).toMatch(`$data.data`)
expect(code).toMatch(`$options.options`)
// expect(code).toMatch(`_ctx, _cache, $props, $setup, $data, $options`)
expect(code).toMatchSnapshot()
})
test('should not prefix temp variable of for...in', () => {
const { code } = compileWithBindingMetadata(
`<div @click="() => {
for (const x in list) {
log(x)
}
}"/>`
)
expect(code).not.toMatch(`_ctx.x`)
expect(code).toMatchSnapshot()
})
test('should not prefix temp variable of for...of', () => {
const { code } = compileWithBindingMetadata(
`<div @click="() => {
for (const x of list) {
log(x)
}
}"/>`
)
expect(code).not.toMatch(`_ctx.x`)
expect(code).toMatchSnapshot()
})
test('should not prefix temp variable of for loop', () => {
const { code } = compileWithBindingMetadata(
`<div @click="() => {
for (let i = 0; i < list.length; i++) {
log(i)
}
}"/>`
)
expect(code).not.toMatch(`_ctx.i`)
expect(code).toMatchSnapshot()
})
test('inline mode', () => {
const { code } = compileWithBindingMetadata(
`<view>{{ props }} {{ setup }} {{ setupConst }} {{ data }} {{ options }} {{ isNaN }}</view>`,
{ inline: true }
)
expect(code).toMatch(`__props.props`)
expect(code).toMatch(`unref(setup)`)
expect(code).toMatch(`toDisplayString(setupConst)`)
expect(code).toMatch(`_ctx.data`)
expect(code).toMatch(`_ctx.options`)
// isNaN 设置了全局方法
// expect(code).toMatch(`isNaN.value`)
expect(code).toMatchSnapshot()
})
test('literal const handling', () => {
const { code } = compileWithBindingMetadata(
`<view>{{ literal }}</view>`,
{
inline: true,
}
)
expect(code).toMatch(`toDisplayString(literal)`)
// #7973 should skip patch for literal const
// TODO
// expect(code).not.toMatch(
// `${PatchFlags.TEXT} /* ${PatchFlagNames[PatchFlags.TEXT]} */`
// )
})
test('literal const handling, non-inline mode', () => {
const { code } = compileWithBindingMetadata(`<view>{{ literal }}</view>`)
expect(code).toMatch(`toDisplayString($setup.literal)`)
// #7973 should skip patch for literal const
// TODO
// expect(code).not.toMatch(
// `${PatchFlags.TEXT} /* ${PatchFlagNames[PatchFlags.TEXT]} */`
// )
})
test('reactive const handling', () => {
// const { code } = compileWithBindingMetadata(`<view>{{ reactive }}</view>`, {
// inline: true,
// })
// #7973 should not skip patch for reactive const
// TODO
// expect(code).toMatch(
// `${PatchFlags.TEXT} /* ${PatchFlagNames[PatchFlags.TEXT]} */`
// )
})
})
})
...@@ -9,8 +9,13 @@ ...@@ -9,8 +9,13 @@
// support and the code is wrapped in `with (this) { ... }`. // support and the code is wrapped in `with (this) { ... }`.
import { NodeTransform, TransformContext } from '../transform' import { NodeTransform, TransformContext } from '../transform'
import { makeMap, hasOwn, isString } from '@vue/shared' import { makeMap, hasOwn, isString, genPropsAccessExp } from '@vue/shared'
import { Node, Identifier } from '@babel/types' import {
Node,
Identifier,
UpdateExpression,
AssignmentExpression,
} from '@babel/types'
import { parse } from '@babel/parser' import { parse } from '@babel/parser'
import { import {
...@@ -21,11 +26,14 @@ import { ...@@ -21,11 +26,14 @@ import {
createCompoundExpression, createCompoundExpression,
createSimpleExpression, createSimpleExpression,
ExpressionNode, ExpressionNode,
IS_REF,
isInDestructureAssignment,
isSimpleIdentifier, isSimpleIdentifier,
isStaticProperty, isStaticProperty,
isStaticPropertyKey, isStaticPropertyKey,
NodeTypes, NodeTypes,
SimpleExpressionNode, SimpleExpressionNode,
UNREF,
walkIdentifiers, walkIdentifiers,
} from '@vue/compiler-core' } from '@vue/compiler-core'
...@@ -40,6 +48,10 @@ const isGloballyWhitelisted = /*#__PURE__*/ makeMap(GLOBALS_WHITE_LISTED) ...@@ -40,6 +48,10 @@ const isGloballyWhitelisted = /*#__PURE__*/ makeMap(GLOBALS_WHITE_LISTED)
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this') const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
// a heuristic safeguard to bail constant expressions on presence of
// likely function invocation and member access
const constantBailRE = /\w\s*\(|\.[^\d]/
export const transformExpression: NodeTransform = (node, context) => { export const transformExpression: NodeTransform = (node, context) => {
if (node.type === NodeTypes.INTERPOLATION) { if (node.type === NodeTypes.INTERPOLATION) {
node.content = processExpression( node.content = processExpression(
...@@ -101,16 +113,100 @@ export function processExpression( ...@@ -101,16 +113,100 @@ export function processExpression(
return node return node
} }
const { bindingMetadata } = context const { inline, bindingMetadata } = context
const rewriteIdentifier = (raw: string, parent?: Node, id?: Identifier) => { const rewriteIdentifier = (raw: string, parent?: Node, id?: Identifier) => {
const type = hasOwn(bindingMetadata, raw) && bindingMetadata[raw] const type = hasOwn(bindingMetadata, raw) && bindingMetadata[raw]
if (type && type.startsWith('setup')) { if (inline) {
// setup bindings in non-inline mode // x = y
return `$setup.${raw}` const isAssignmentLVal =
} else if (type === BindingTypes.PROPS_ALIASED) { parent && parent.type === 'AssignmentExpression' && parent.left === id
return `$props['${bindingMetadata.__propsAliases![raw]}']` // x++
} else if (type) { const isUpdateArg =
return `$${type}.${raw}` parent && parent.type === 'UpdateExpression' && parent.argument === id
// ({ x } = y)
const isDestructureAssignment =
parent && isInDestructureAssignment(parent, parentStack)
if (
isConst(type) ||
type === BindingTypes.SETUP_REACTIVE_CONST ||
localVars[raw]
) {
return raw
} else if (type === BindingTypes.SETUP_REF) {
return `${raw}.value`
} else if (type === BindingTypes.SETUP_MAYBE_REF) {
// const binding that may or may not be ref
// if it's not a ref, then assignments don't make sense -
// so we ignore the non-ref assignment case and generate code
// that assumes the value to be a ref for more efficiency
return isAssignmentLVal || isUpdateArg || isDestructureAssignment
? `${raw}.value`
: `${context.helperString(UNREF)}(${raw})`
} else if (type === BindingTypes.SETUP_LET) {
if (isAssignmentLVal) {
// let binding.
// this is a bit more tricky as we need to cover the case where
// let is a local non-ref value, and we need to replicate the
// right hand side value.
// x = y --> isRef(x) ? x.value = y : x = y
const { right: rVal, operator } = parent as AssignmentExpression
const rExp = rawExp.slice(rVal.start! - 1, rVal.end! - 1)
const rExpString = stringifyExpression(
processExpression(
createSimpleExpression(rExp, false),
context,
false,
false,
knownIds
)
)
return `${context.helperString(
IS_REF
)}(${raw}) ? ${raw}.value ${operator} ${rExpString} : ${raw}`
} else if (isUpdateArg) {
// make id replace parent in the code range so the raw update operator
// is removed
id!.start = parent!.start
id!.end = parent!.end
const { prefix: isPrefix, operator } = parent as UpdateExpression
const prefix = isPrefix ? operator : ``
const postfix = isPrefix ? `` : operator
// let binding.
// x++ --> isRef(a) ? a.value++ : a++
return `${context.helperString(
IS_REF
)}(${raw}) ? ${prefix}${raw}.value${postfix} : ${prefix}${raw}${postfix}`
} else if (isDestructureAssignment) {
// TODO
// let binding in a destructure assignment - it's very tricky to
// handle both possible cases here without altering the original
// structure of the code, so we just assume it's not a ref here
// for now
return raw
} else {
return `${context.helperString(UNREF)}(${raw})`
}
} else if (type === BindingTypes.PROPS) {
// use __props which is generated by compileScript so in ts mode
// it gets correct type
return genPropsAccessExp(raw)
} else if (type === BindingTypes.PROPS_ALIASED) {
// prop with a different local alias (from defineProps() destructure)
return genPropsAccessExp(bindingMetadata.__propsAliases![raw])
}
} else {
if (
(type && type.startsWith('setup')) ||
type === BindingTypes.LITERAL_CONST
) {
// setup bindings in non-inline mode
return `$setup.${raw}`
} else if (type === BindingTypes.PROPS_ALIASED) {
return `$props['${bindingMetadata.__propsAliases![raw]}']`
} else if (type) {
return `$${type}.${raw}`
}
} }
// fallback to ctx // fallback to ctx
return `_ctx.${raw}` return `_ctx.${raw}`
...@@ -119,7 +215,7 @@ export function processExpression( ...@@ -119,7 +215,7 @@ export function processExpression(
// fast path if expression is a simple identifier. // fast path if expression is a simple identifier.
const rawExp = node.content const rawExp = node.content
// bail constant on parens (function invocation) and dot (member access) // bail constant on parens (function invocation) and dot (member access)
const bailConstant = rawExp.indexOf(`(`) > -1 || rawExp.indexOf('.') > 0 const bailConstant = constantBailRE.test(rawExp)
if (isSimpleIdentifier(rawExp)) { if (isSimpleIdentifier(rawExp)) {
const isScopeVarReference = context.identifiers[rawExp] const isScopeVarReference = context.identifiers[rawExp]
...@@ -275,3 +371,9 @@ export function stringifyExpression(exp: ExpressionNode | string): string { ...@@ -275,3 +371,9 @@ export function stringifyExpression(exp: ExpressionNode | string): string {
.join('') .join('')
} }
} }
function isConst(type: unknown) {
return (
type === BindingTypes.SETUP_CONST || type === BindingTypes.LITERAL_CONST
)
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册