feat(compiler): handle complex destructure expressions in v-for
This commit is contained in:
@@ -67,6 +67,8 @@ interface PrefixMeta {
|
||||
export function processExpression(
|
||||
node: SimpleExpressionNode,
|
||||
context: TransformContext,
|
||||
// some expressions like v-slot props & v-for aliases should be parsed as
|
||||
// function params
|
||||
asParams: boolean = false
|
||||
): ExpressionNode {
|
||||
if (!context.prefixIdentifiers) {
|
||||
@@ -75,7 +77,7 @@ export function processExpression(
|
||||
|
||||
// fast path if expression is a simple identifier.
|
||||
if (isSimpleIdentifier(node.content)) {
|
||||
if (!context.identifiers[node.content]) {
|
||||
if (!asParams && !context.identifiers[node.content]) {
|
||||
node.content = `_ctx.${node.content}`
|
||||
}
|
||||
return node
|
||||
@@ -107,17 +109,14 @@ export function processExpression(
|
||||
if (node.type === 'Identifier') {
|
||||
if (!ids.includes(node)) {
|
||||
if (!knownIds[node.name] && shouldPrefix(node, parent)) {
|
||||
if (
|
||||
isPropertyKey(node, parent) &&
|
||||
(parent as Property).value === node
|
||||
) {
|
||||
if (isPropertyShorthand(node, parent)) {
|
||||
// property shorthand like { foo }, we need to add the key since we
|
||||
// rewrite the value
|
||||
node.prefix = `${node.name}: `
|
||||
}
|
||||
node.name = `_ctx.${node.name}`
|
||||
ids.push(node)
|
||||
} else if (!isPropertyKey(node, parent)) {
|
||||
} else if (!isStaticPropertyKey(node, parent)) {
|
||||
// also generate sub-expressioms for other identifiers for better
|
||||
// source map support. (except for property keys which are static)
|
||||
ids.push(node)
|
||||
@@ -131,9 +130,11 @@ export function processExpression(
|
||||
enter(child, parent) {
|
||||
if (
|
||||
child.type === 'Identifier' &&
|
||||
!// do not keep as scope variable if this is a default value
|
||||
// assignment of a param
|
||||
(
|
||||
// do not record as scope variable if is a destrcuture key
|
||||
!isStaticPropertyKey(child, parent) &&
|
||||
// do not record if this is a default value
|
||||
// assignment of a destructured variable
|
||||
!(
|
||||
parent &&
|
||||
parent.type === 'AssignmentPattern' &&
|
||||
parent.right === child
|
||||
@@ -213,7 +214,16 @@ const isFunction = (node: Node): node is Function =>
|
||||
/Function(Expression|Declaration)$/.test(node.type)
|
||||
|
||||
const isPropertyKey = (node: Node, parent: Node) =>
|
||||
parent.type === 'Property' && parent.key === node && !parent.computed
|
||||
parent &&
|
||||
parent.type === 'Property' &&
|
||||
parent.key === node &&
|
||||
!parent.computed
|
||||
|
||||
const isPropertyShorthand = (node: Node, parent: Node) =>
|
||||
isPropertyKey(node, parent) && (parent as Property).value === node
|
||||
|
||||
const isStaticPropertyKey = (node: Node, parent: Node) =>
|
||||
isPropertyKey(node, parent) && (parent as Property).value !== node
|
||||
|
||||
const globals = new Set(
|
||||
(
|
||||
@@ -236,11 +246,7 @@ function shouldPrefix(identifier: Identifier, parent: Node) {
|
||||
parent.params.includes(identifier))
|
||||
) &&
|
||||
// not a key of Property
|
||||
!(
|
||||
isPropertyKey(identifier, parent) &&
|
||||
// shorthand keys should be prefixed
|
||||
!((parent as Property).value === identifier)
|
||||
) &&
|
||||
!isStaticPropertyKey(identifier, parent) &&
|
||||
// not a property of a MemberExpression
|
||||
!(
|
||||
parent.type === 'MemberExpression' &&
|
||||
|
||||
@@ -39,19 +39,20 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
children: [node]
|
||||
})
|
||||
|
||||
// scope management
|
||||
const { addIdentifier, removeIdentifier } = context
|
||||
if (!__BROWSER__) {
|
||||
// scope management
|
||||
const { addIdentifiers, removeIdentifiers } = context
|
||||
|
||||
// inject identifiers to context
|
||||
value && addIdentifier(value.content)
|
||||
key && addIdentifier(key.content)
|
||||
index && addIdentifier(index.content)
|
||||
// inject identifiers to context
|
||||
value && addIdentifiers(value)
|
||||
key && addIdentifiers(key)
|
||||
index && addIdentifiers(index)
|
||||
|
||||
return () => {
|
||||
// remove injected identifiers on exit
|
||||
value && removeIdentifier(value.content)
|
||||
key && removeIdentifier(key.content)
|
||||
index && removeIdentifier(index.content)
|
||||
return () => {
|
||||
value && removeIdentifiers(value)
|
||||
key && removeIdentifiers(key)
|
||||
index && removeIdentifiers(index)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context.onError(
|
||||
@@ -67,14 +68,16 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
)
|
||||
|
||||
const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
|
||||
// This regex doesn't cover the case if key or index aliases have destructuring,
|
||||
// but those do not make sense in the first place, so this works in practice.
|
||||
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
||||
const stripParensRE = /^\(|\)$/g
|
||||
|
||||
interface ForParseResult {
|
||||
source: ExpressionNode
|
||||
value: SimpleExpressionNode | undefined
|
||||
key: SimpleExpressionNode | undefined
|
||||
index: SimpleExpressionNode | undefined
|
||||
value: ExpressionNode | undefined
|
||||
key: ExpressionNode | undefined
|
||||
index: ExpressionNode | undefined
|
||||
}
|
||||
|
||||
function parseForExpression(
|
||||
@@ -88,21 +91,22 @@ function parseForExpression(
|
||||
|
||||
const [, LHS, RHS] = inMatch
|
||||
|
||||
let source: ExpressionNode = createAliasExpression(
|
||||
loc,
|
||||
RHS.trim(),
|
||||
exp.indexOf(RHS, LHS.length)
|
||||
)
|
||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||
source = processExpression(source, context)
|
||||
}
|
||||
|
||||
const result: ForParseResult = {
|
||||
source,
|
||||
source: createAliasExpression(
|
||||
loc,
|
||||
RHS.trim(),
|
||||
exp.indexOf(RHS, LHS.length)
|
||||
),
|
||||
value: undefined,
|
||||
key: undefined,
|
||||
index: undefined
|
||||
}
|
||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||
result.source = processExpression(
|
||||
result.source as SimpleExpressionNode,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
let valueContent = LHS.trim()
|
||||
.replace(stripParensRE, '')
|
||||
@@ -118,6 +122,9 @@ function parseForExpression(
|
||||
if (keyContent) {
|
||||
keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length)
|
||||
result.key = createAliasExpression(loc, keyContent, keyOffset)
|
||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||
result.key = processExpression(result.key, context, true)
|
||||
}
|
||||
}
|
||||
|
||||
if (iteratorMatch[2]) {
|
||||
@@ -134,12 +141,18 @@ function parseForExpression(
|
||||
: trimmedOffset + valueContent.length
|
||||
)
|
||||
)
|
||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||
result.index = processExpression(result.index, context, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valueContent) {
|
||||
result.value = createAliasExpression(loc, valueContent, trimmedOffset)
|
||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||
result.value = processExpression(result.value, context, true)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -31,12 +31,9 @@ export const trackSlotScopes: NodeTransform = (node, context) => {
|
||||
) {
|
||||
const vSlot = node.props.find(isVSlot)
|
||||
if (vSlot && vSlot.exp) {
|
||||
const { identifiers } = vSlot.exp
|
||||
if (identifiers) {
|
||||
identifiers.forEach(context.addIdentifier)
|
||||
return () => {
|
||||
identifiers.forEach(context.removeIdentifier)
|
||||
}
|
||||
context.addIdentifiers(vSlot.exp)
|
||||
return () => {
|
||||
context.removeIdentifiers(vSlot.exp!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user