refactor(compiler): further extract babel ast utilities

This commit is contained in:
Evan You
2021-08-22 14:51:16 -04:00
parent 62f752552a
commit 73f8cae465
7 changed files with 284 additions and 324 deletions

View File

@@ -17,18 +17,19 @@ import {
createCompoundExpression,
ConstantTypes
} from '../ast'
import {
isInDestructureAssignment,
isStaticProperty,
isStaticPropertyKey,
walkIdentifiers
} from '../babelUtils'
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
import {
isGloballyWhitelisted,
makeMap,
babelParserDefaultPlugins,
hasOwn,
isString,
isReferencedIdentifier,
isInDestructureAssignment,
isStaticProperty,
isStaticPropertyKey,
isFunctionType
isString
} from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors'
import {
@@ -39,7 +40,6 @@ import {
} from '@babel/types'
import { validateBrowserExpression } from '../validateExpression'
import { parse } from '@babel/parser'
import { walk } from 'estree-walker'
import { IS_REF, UNREF } from '../runtimeHelpers'
import { BindingTypes } from '../options'
@@ -245,89 +245,49 @@ export function processExpression(
return node
}
const ids: (Identifier & PrefixMeta)[] = []
const knownIds = Object.create(context.identifiers)
const isDuplicate = (node: Node & PrefixMeta): boolean =>
ids.some(id => id.start === node.start)
type QualifiedId = Identifier & PrefixMeta
const ids: QualifiedId[] = []
const parentStack: Node[] = []
const knownIds: Record<string, number> = Object.create(context.identifiers)
// walk the AST and look for identifiers that need to be prefixed.
;(walk as any)(ast, {
enter(node: Node & PrefixMeta, parent: Node | undefined) {
parent && parentStack.push(parent)
if (node.type === 'Identifier') {
if (!isDuplicate(node)) {
// v2 wrapped filter call
if (__COMPAT__ && node.name.startsWith('_filter_')) {
return
}
walkIdentifiers(
ast,
(node, parent, _, isReferenced, isLocal) => {
if (isStaticPropertyKey(node, parent!)) {
return
}
// v2 wrapped filter call
if (__COMPAT__ && node.name.startsWith('_filter_')) {
return
}
const needPrefix = shouldPrefix(node, parent!, parentStack)
if (!knownIds[node.name] && needPrefix) {
if (isStaticProperty(parent!) && parent.shorthand) {
// property shorthand like { foo }, we need to add the key since
// we rewrite the value
node.prefix = `${node.name}: `
}
node.name = rewriteIdentifier(node.name, parent, node)
ids.push(node)
} else if (!isStaticPropertyKey(node, parent!)) {
// The identifier is considered constant unless it's pointing to a
// scope variable (a v-for alias, or a v-slot prop)
if (!(needPrefix && knownIds[node.name]) && !bailConstant) {
node.isConstant = true
}
// also generate sub-expressions for other identifiers for better
// source map support. (except for property keys which are static)
ids.push(node)
}
const needPrefix = isReferenced && canPrefix(node)
if (needPrefix && !isLocal) {
if (isStaticProperty(parent!) && parent.shorthand) {
// property shorthand like { foo }, we need to add the key since
// we rewrite the value
;(node as QualifiedId).prefix = `${node.name}: `
}
} else if (isFunctionType(node)) {
// walk function expressions and add its arguments to known identifiers
// so that we don't prefix them
node.params.forEach(p =>
(walk as any)(p, {
enter(child: Node, parent: Node) {
if (
child.type === 'Identifier' &&
// do not record as scope variable if is a destructured 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
)
) {
const { name } = child
if (node.scopeIds && node.scopeIds.has(name)) {
return
}
if (name in knownIds) {
knownIds[name]++
} else {
knownIds[name] = 1
}
;(node.scopeIds || (node.scopeIds = new Set())).add(name)
}
}
})
)
node.name = rewriteIdentifier(node.name, parent, node)
ids.push(node as QualifiedId)
} else {
// The identifier is considered constant unless it's pointing to a
// local scope variable (a v-for alias, or a v-slot prop)
if (!(needPrefix && isLocal) && !bailConstant) {
;(node as QualifiedId).isConstant = true
}
// also generate sub-expressions for other identifiers for better
// source map support. (except for property keys which are static)
ids.push(node as QualifiedId)
}
},
leave(node: Node & PrefixMeta, parent: Node | undefined) {
parent && parentStack.pop()
if (node !== ast.body[0].expression && node.scopeIds) {
node.scopeIds.forEach((id: string) => {
knownIds[id]--
if (knownIds[id] === 0) {
delete knownIds[id]
}
})
}
}
})
undefined,
parentStack,
knownIds,
// invoke on ALL identifiers
true
)
// We break up the compound expression into an array of strings and sub
// expressions (for identifiers that have been prefixed). In codegen, if
@@ -375,7 +335,7 @@ export function processExpression(
return ret
}
function shouldPrefix(id: Identifier, parent: Node, parentStack: Node[]) {
function canPrefix(id: Identifier) {
// skip whitelisted globals
if (isGloballyWhitelisted(id.name)) {
return false
@@ -384,7 +344,7 @@ function shouldPrefix(id: Identifier, parent: Node, parentStack: Node[]) {
if (id.name === 'require') {
return false
}
return isReferencedIdentifier(id, parent, parentStack)
return true
}
function stringifyExpression(exp: ExpressionNode | string): string {