fix(compiler): improve auto prefixing cases

This commit is contained in:
Evan You 2019-09-27 23:20:26 -04:00
parent 262be6733c
commit 6377af483b
2 changed files with 86 additions and 22 deletions

View File

@ -308,6 +308,59 @@ describe('compiler: expression transform', () => {
})
})
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: [
`({ foo }) => `,
{ content: `foo` },
` + `,
{ content: `_ctx.bar` }
]
})
})
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: bar } }}`

View File

@ -20,7 +20,7 @@ import {
createCompoundExpression
} from '../ast'
import { Node, Function, Identifier, Property } from 'estree'
import { advancePositionWithClone } from '../utils'
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
export const transformExpression: NodeTransform = (node, context) => {
if (node.type === NodeTypes.INTERPOLATION) {
@ -31,19 +31,19 @@ export const transformExpression: NodeTransform = (node, context) => {
} else if (node.type === NodeTypes.ELEMENT) {
// handle directives on element
for (let i = 0; i < node.props.length; i++) {
const prop = node.props[i]
if (prop.type === NodeTypes.DIRECTIVE) {
const exp = prop.exp as SimpleExpressionNode | undefined
const arg = prop.arg as SimpleExpressionNode | undefined
const dir = node.props[i]
if (dir.type === NodeTypes.DIRECTIVE) {
const exp = dir.exp as SimpleExpressionNode | undefined
const arg = dir.arg as SimpleExpressionNode | undefined
if (exp) {
prop.exp = processExpression(exp, context)
dir.exp = processExpression(exp, context, dir.name === 'slot')
}
if (arg && !arg.isStatic) {
if (prop.name === 'class') {
if (dir.name === 'class') {
// TODO special expression optimization for classes
prop.arg = processExpression(arg, context)
dir.arg = processExpression(arg, context)
} else {
prop.arg = processExpression(arg, context)
dir.arg = processExpression(arg, context)
}
}
}
@ -51,14 +51,6 @@ export const transformExpression: NodeTransform = (node, context) => {
}
}
const simpleIdRE = /^[a-zA-Z$_][\w$]*$/
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
// cache node requires
let _parseScript: typeof parseScript
let _walk: typeof walk
@ -74,14 +66,15 @@ interface PrefixMeta {
// tree-shaken from the browser build.
export function processExpression(
node: SimpleExpressionNode,
context: TransformContext
context: TransformContext,
asParams: boolean = false
): ExpressionNode {
if (!context.prefixIdentifiers) {
return node
}
// fast path if expression is a simple identifier.
if (simpleIdRE.test(node.content)) {
if (isSimpleIdentifier(node.content)) {
if (!context.identifiers[node.content]) {
node.content = `_ctx.${node.content}`
}
@ -95,8 +88,11 @@ export function processExpression(
const walk = _walk || (_walk = require('estree-walker').walk)
let ast
// if the expression is supposed to be used in a function params position
// we need to parse it differently.
const source = `(${node.content})${asParams ? `=>{}` : ``}`
try {
ast = parseScript(`(${node.content})`, { ranges: true }) as any
ast = parseScript(source, { ranges: true }) as any
} catch (e) {
context.onError(e)
return node
@ -132,8 +128,17 @@ export function processExpression(
// so that we don't prefix them
node.params.forEach(p =>
walk(p, {
enter(child) {
if (child.type === 'Identifier') {
enter(child, parent) {
if (
child.type === 'Identifier' &&
!// do not keep as scope variable if this is a default value
// assignment of a param
(
parent &&
parent.type === 'AssignmentPattern' &&
parent.right === child
)
) {
knownIds[child.name] = true
;(
(node as any)._scopeIds ||
@ -187,6 +192,12 @@ export function processExpression(
}
}
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
const globals = new Set(
(
'Infinity,undefined,NaN,isFinite,isNaN,' +