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', () => { test('should not prefix an object property key', () => {
const node = parseWithExpressionTransform( const node = parseWithExpressionTransform(
`{{ { foo: bar } }}` `{{ { foo: bar } }}`

View File

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