|
@@ -3,6 +3,8 @@ import {
|
|
|
NewlineType,
|
|
NewlineType,
|
|
|
type SourceLocation,
|
|
type SourceLocation,
|
|
|
advancePositionWithClone,
|
|
advancePositionWithClone,
|
|
|
|
|
+ isInDestructureAssignment,
|
|
|
|
|
+ isStaticProperty,
|
|
|
walkIdentifiers,
|
|
walkIdentifiers,
|
|
|
} from '@vue/compiler-dom'
|
|
} from '@vue/compiler-dom'
|
|
|
import { isGloballyAllowed, isString, makeMap } from '@vue/shared'
|
|
import { isGloballyAllowed, isString, makeMap } from '@vue/shared'
|
|
@@ -13,6 +15,7 @@ import {
|
|
|
type CodegenContext,
|
|
type CodegenContext,
|
|
|
buildCodeFragment,
|
|
buildCodeFragment,
|
|
|
} from '../generate'
|
|
} from '../generate'
|
|
|
|
|
+import type { Node } from '@babel/types'
|
|
|
|
|
|
|
|
export function genExpression(
|
|
export function genExpression(
|
|
|
node: IRExpression,
|
|
node: IRExpression,
|
|
@@ -42,11 +45,21 @@ export function genExpression(
|
|
|
|
|
|
|
|
// the expression is a simple identifier
|
|
// the expression is a simple identifier
|
|
|
if (ast === null) {
|
|
if (ast === null) {
|
|
|
- return [genIdentifier(rawExpr, context, loc)]
|
|
|
|
|
|
|
+ return genIdentifier(rawExpr, context, loc)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const ids: Identifier[] = []
|
|
const ids: Identifier[] = []
|
|
|
- walkIdentifiers(ast!, id => ids.push(id))
|
|
|
|
|
|
|
+ const parentStackMap = new WeakMap<Identifier, Node[]>()
|
|
|
|
|
+ const parentStack: Node[] = []
|
|
|
|
|
+ walkIdentifiers(
|
|
|
|
|
+ ast!,
|
|
|
|
|
+ id => {
|
|
|
|
|
+ ids.push(id)
|
|
|
|
|
+ parentStackMap.set(id, parentStack.slice())
|
|
|
|
|
+ },
|
|
|
|
|
+ false,
|
|
|
|
|
+ parentStack,
|
|
|
|
|
+ )
|
|
|
if (ids.length) {
|
|
if (ids.length) {
|
|
|
ids.sort((a, b) => a.start! - b.start!)
|
|
ids.sort((a, b) => a.start! - b.start!)
|
|
|
const [frag, push] = buildCodeFragment()
|
|
const [frag, push] = buildCodeFragment()
|
|
@@ -60,12 +73,20 @@ export function genExpression(
|
|
|
if (leadingText.length) push([leadingText, NewlineType.Unknown])
|
|
if (leadingText.length) push([leadingText, NewlineType.Unknown])
|
|
|
|
|
|
|
|
const source = rawExpr.slice(start, end)
|
|
const source = rawExpr.slice(start, end)
|
|
|
|
|
+ const parentStack = parentStackMap.get(id)!
|
|
|
push(
|
|
push(
|
|
|
- genIdentifier(source, context, {
|
|
|
|
|
- start: advancePositionWithClone(node.loc.start, source, start),
|
|
|
|
|
- end: advancePositionWithClone(node.loc.start, source, end),
|
|
|
|
|
|
|
+ ...genIdentifier(
|
|
|
source,
|
|
source,
|
|
|
- }),
|
|
|
|
|
|
|
+ context,
|
|
|
|
|
+ {
|
|
|
|
|
+ start: advancePositionWithClone(node.loc.start, source, start),
|
|
|
|
|
+ end: advancePositionWithClone(node.loc.start, source, end),
|
|
|
|
|
+ source,
|
|
|
|
|
+ },
|
|
|
|
|
+ id,
|
|
|
|
|
+ parentStack[parentStack.length - 1],
|
|
|
|
|
+ parentStack,
|
|
|
|
|
+ ),
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
if (i === ids.length - 1 && end < rawExpr.length) {
|
|
if (i === ids.length - 1 && end < rawExpr.length) {
|
|
@@ -81,30 +102,55 @@ export function genExpression(
|
|
|
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
|
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
|
|
|
|
|
|
|
function genIdentifier(
|
|
function genIdentifier(
|
|
|
- id: string,
|
|
|
|
|
|
|
+ raw: string,
|
|
|
{ options, vaporHelper, identifiers }: CodegenContext,
|
|
{ options, vaporHelper, identifiers }: CodegenContext,
|
|
|
loc?: SourceLocation,
|
|
loc?: SourceLocation,
|
|
|
-): CodeFragment {
|
|
|
|
|
|
|
+ id?: Identifier,
|
|
|
|
|
+ parent?: Node,
|
|
|
|
|
+ parentStack?: Node[],
|
|
|
|
|
+): CodeFragment[] {
|
|
|
const { inline, bindingMetadata } = options
|
|
const { inline, bindingMetadata } = options
|
|
|
- let name: string | undefined = id
|
|
|
|
|
|
|
+ let name: string | undefined = raw
|
|
|
|
|
|
|
|
- const idMap = identifiers[id]
|
|
|
|
|
|
|
+ const idMap = identifiers[raw]
|
|
|
if (idMap && idMap.length) {
|
|
if (idMap && idMap.length) {
|
|
|
- return [idMap[0], NewlineType.None, loc]
|
|
|
|
|
|
|
+ return [[idMap[0], NewlineType.None, loc]]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let prefix: string | undefined
|
|
|
|
|
+ if (isStaticProperty(parent!) && parent.shorthand) {
|
|
|
|
|
+ // property shorthand like { foo }, we need to add the key since
|
|
|
|
|
+ // we rewrite the value
|
|
|
|
|
+ prefix = `${raw}: `
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (inline) {
|
|
if (inline) {
|
|
|
- switch (bindingMetadata[id]) {
|
|
|
|
|
|
|
+ switch (bindingMetadata[raw]) {
|
|
|
case BindingTypes.SETUP_REF:
|
|
case BindingTypes.SETUP_REF:
|
|
|
- name = id += '.value'
|
|
|
|
|
|
|
+ name = raw = `${raw}.value`
|
|
|
break
|
|
break
|
|
|
case BindingTypes.SETUP_MAYBE_REF:
|
|
case BindingTypes.SETUP_MAYBE_REF:
|
|
|
- id = `${vaporHelper('unref')}(${id})`
|
|
|
|
|
- name = undefined
|
|
|
|
|
|
|
+ // ({ x } = y)
|
|
|
|
|
+ const isDestructureAssignment =
|
|
|
|
|
+ parent && isInDestructureAssignment(parent, parentStack || [])
|
|
|
|
|
+ // x = y
|
|
|
|
|
+ const isAssignmentLVal =
|
|
|
|
|
+ parent && parent.type === 'AssignmentExpression' && parent.left === id
|
|
|
|
|
+ // x++
|
|
|
|
|
+ const isUpdateArg =
|
|
|
|
|
+ parent && parent.type === 'UpdateExpression' && parent.argument === id
|
|
|
|
|
+ // 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
|
|
|
|
|
+ raw =
|
|
|
|
|
+ isAssignmentLVal || isUpdateArg || isDestructureAssignment
|
|
|
|
|
+ ? (name = `${raw}.value`)
|
|
|
|
|
+ : `${vaporHelper('unref')}(${raw})`
|
|
|
break
|
|
break
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- id = `_ctx.${id}`
|
|
|
|
|
|
|
+ raw = `_ctx.${raw}`
|
|
|
}
|
|
}
|
|
|
- return [id, NewlineType.None, loc, name]
|
|
|
|
|
|
|
+ return [prefix, [raw, NewlineType.None, loc, name]]
|
|
|
}
|
|
}
|