refactor(compiler): extract shared ast transform utils
Also improve referenced identifier check using isReferenced from @babel/types
This commit is contained in:
parent
6be6c268e8
commit
62f752552a
@ -23,14 +23,17 @@ import {
|
||||
makeMap,
|
||||
babelParserDefaultPlugins,
|
||||
hasOwn,
|
||||
isString
|
||||
isString,
|
||||
isReferencedIdentifier,
|
||||
isInDestructureAssignment,
|
||||
isStaticProperty,
|
||||
isStaticPropertyKey,
|
||||
isFunctionType
|
||||
} from '@vue/shared'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import {
|
||||
Node,
|
||||
Function,
|
||||
Identifier,
|
||||
ObjectProperty,
|
||||
AssignmentExpression,
|
||||
UpdateExpression
|
||||
} from '@babel/types'
|
||||
@ -279,7 +282,7 @@ export function processExpression(
|
||||
ids.push(node)
|
||||
}
|
||||
}
|
||||
} else if (isFunction(node)) {
|
||||
} 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 =>
|
||||
@ -372,97 +375,16 @@ export function processExpression(
|
||||
return ret
|
||||
}
|
||||
|
||||
const isFunction = (node: Node): node is Function => {
|
||||
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
|
||||
}
|
||||
|
||||
const isStaticProperty = (node: Node): node is ObjectProperty =>
|
||||
node &&
|
||||
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
|
||||
!node.computed
|
||||
|
||||
const isStaticPropertyKey = (node: Node, parent: Node) =>
|
||||
isStaticProperty(parent) && parent.key === node
|
||||
|
||||
function shouldPrefix(id: Identifier, parent: Node, parentStack: Node[]) {
|
||||
// declaration id
|
||||
if (
|
||||
(parent.type === 'VariableDeclarator' ||
|
||||
parent.type === 'ClassDeclaration') &&
|
||||
parent.id === id
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (isFunction(parent)) {
|
||||
// function decalration/expression id
|
||||
if ((parent as any).id === id) {
|
||||
return false
|
||||
}
|
||||
// params list
|
||||
if (parent.params.includes(id)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// property key
|
||||
// this also covers object destructure pattern
|
||||
if (isStaticPropertyKey(id, parent)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// non-assignment array destructure pattern
|
||||
if (
|
||||
parent.type === 'ArrayPattern' &&
|
||||
!isInDestructureAssignment(parent, parentStack)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// member expression property
|
||||
if (
|
||||
(parent.type === 'MemberExpression' ||
|
||||
parent.type === 'OptionalMemberExpression') &&
|
||||
parent.property === id &&
|
||||
!parent.computed
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// is a special keyword but parsed as identifier
|
||||
if (id.name === 'arguments') {
|
||||
return false
|
||||
}
|
||||
|
||||
// skip whitelisted globals
|
||||
if (isGloballyWhitelisted(id.name)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// special case for webpack compilation
|
||||
if (id.name === 'require') {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function isInDestructureAssignment(parent: Node, parentStack: Node[]): boolean {
|
||||
if (
|
||||
parent &&
|
||||
(parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
|
||||
) {
|
||||
let i = parentStack.length
|
||||
while (i--) {
|
||||
const p = parentStack[i]
|
||||
if (p.type === 'AssignmentExpression') {
|
||||
return true
|
||||
} else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return isReferencedIdentifier(id, parent, parentStack)
|
||||
}
|
||||
|
||||
function stringifyExpression(exp: ExpressionNode | string): string {
|
||||
|
@ -22,6 +22,10 @@ import {
|
||||
camelize,
|
||||
capitalize,
|
||||
generateCodeFrame,
|
||||
isFunctionType,
|
||||
isReferencedIdentifier,
|
||||
isStaticProperty,
|
||||
isStaticPropertyKey,
|
||||
makeMap
|
||||
} from '@vue/shared'
|
||||
import {
|
||||
@ -32,7 +36,6 @@ import {
|
||||
ArrayPattern,
|
||||
Identifier,
|
||||
ExportSpecifier,
|
||||
Function as FunctionNode,
|
||||
TSType,
|
||||
TSTypeLiteral,
|
||||
TSFunctionType,
|
||||
@ -1028,7 +1031,7 @@ export function compileScript(
|
||||
) {
|
||||
;(walk as any)(node, {
|
||||
enter(child: Node, parent: Node) {
|
||||
if (isFunction(child)) {
|
||||
if (isFunctionType(child)) {
|
||||
this.skip()
|
||||
}
|
||||
if (child.type === 'AwaitExpression') {
|
||||
@ -1819,11 +1822,11 @@ export function walkIdentifiers(
|
||||
if (node.type === 'Identifier') {
|
||||
if (
|
||||
!knownIds[node.name] &&
|
||||
isRefIdentifier(node, parent!, parentStack)
|
||||
isReferencedIdentifier(node, parent!, parentStack)
|
||||
) {
|
||||
onIdentifier(node, parent!, parentStack)
|
||||
}
|
||||
} else if (isFunction(node)) {
|
||||
} else if (isFunctionType(node)) {
|
||||
// #3445
|
||||
// should not rewrite local variables sharing a name with a top-level ref
|
||||
if (node.body.type === 'BlockStatement') {
|
||||
@ -1881,79 +1884,6 @@ export function walkIdentifiers(
|
||||
})
|
||||
}
|
||||
|
||||
function isRefIdentifier(
|
||||
id: Identifier,
|
||||
parent: Node | null,
|
||||
parentStack: Node[]
|
||||
) {
|
||||
if (!parent) {
|
||||
return true
|
||||
}
|
||||
|
||||
// declaration id
|
||||
if (
|
||||
(parent.type === 'VariableDeclarator' ||
|
||||
parent.type === 'ClassDeclaration') &&
|
||||
parent.id === id
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (isFunction(parent)) {
|
||||
// function decalration/expression id
|
||||
if ((parent as any).id === id) {
|
||||
return false
|
||||
}
|
||||
// params list
|
||||
if (parent.params.includes(id)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// property key
|
||||
// this also covers object destructure pattern
|
||||
if (isStaticPropertyKey(id, parent)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// non-assignment array destructure pattern
|
||||
if (
|
||||
parent.type === 'ArrayPattern' &&
|
||||
!isInDestructureAssignment(parent, parentStack)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// member expression property
|
||||
if (
|
||||
(parent.type === 'MemberExpression' ||
|
||||
parent.type === 'OptionalMemberExpression') &&
|
||||
parent.property === id &&
|
||||
!parent.computed
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// is a special keyword but parsed as identifier
|
||||
if (id.name === 'arguments') {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const isStaticProperty = (node: Node): node is ObjectProperty =>
|
||||
node &&
|
||||
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
|
||||
!node.computed
|
||||
|
||||
const isStaticPropertyKey = (node: Node, parent: Node) =>
|
||||
isStaticProperty(parent) && parent.key === node
|
||||
|
||||
function isFunction(node: Node): node is FunctionNode {
|
||||
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
|
||||
}
|
||||
|
||||
function isCallOf(
|
||||
node: Node | null | undefined,
|
||||
test: string | ((id: string) => boolean)
|
||||
|
@ -8,27 +8,29 @@ import {
|
||||
WritableComputedRef
|
||||
} from '@vue/reactivity'
|
||||
|
||||
export function $ref<T>(arg: T | Ref<T>): UnwrapRef<T>
|
||||
declare const RefMarker: unique symbol
|
||||
type RefValue<T> = T & { [RefMarker]?: any }
|
||||
|
||||
export function $ref<T>(arg?: T | Ref<T>): RefValue<UnwrapRef<T>>
|
||||
export function $ref() {}
|
||||
|
||||
export function $shallowRef<T>(arg: T): T {
|
||||
return arg
|
||||
}
|
||||
export function $shallowRef<T>(arg?: T): RefValue<T>
|
||||
export function $shallowRef() {}
|
||||
|
||||
declare const ComputedRefMarker: unique symbol
|
||||
type ComputedValue<T> = T & { [ComputedRefMarker]?: any }
|
||||
type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any }
|
||||
|
||||
declare const WritableComputedRefMarker: unique symbol
|
||||
type WritableComputedValue<T> = T & { [WritableComputedRefMarker]?: any }
|
||||
type WritableComputedRefValue<T> = T & { [WritableComputedRefMarker]?: any }
|
||||
|
||||
export function $computed<T>(
|
||||
getter: () => T,
|
||||
debuggerOptions?: DebuggerOptions
|
||||
): ComputedValue<T>
|
||||
): ComputedRefValue<T>
|
||||
export function $computed<T>(
|
||||
options: WritableComputedOptions<T>,
|
||||
debuggerOptions?: DebuggerOptions
|
||||
): WritableComputedValue<T>
|
||||
): WritableComputedRefValue<T>
|
||||
export function $computed() {}
|
||||
|
||||
export function $fromRefs<T>(source: T): ShallowUnwrapRef<T>
|
||||
@ -36,9 +38,13 @@ export function $fromRefs() {
|
||||
return null as any
|
||||
}
|
||||
|
||||
export function $raw<T>(value: ComputedValue<T>): ComputedRef<T>
|
||||
export function $raw<T>(value: WritableComputedValue<T>): WritableComputedRef<T>
|
||||
export function $raw<T>(value: T): Ref<T>
|
||||
export function $raw<T extends ComputedRefValue<any>>(
|
||||
value: T
|
||||
): T extends ComputedRefValue<infer V> ? ComputedRef<V> : never
|
||||
export function $raw<T>(
|
||||
value: WritableComputedRefValue<T>
|
||||
): WritableComputedRef<T>
|
||||
export function $raw<T>(value: RefValue<T>): Ref<T>
|
||||
export function $raw() {
|
||||
return null as any
|
||||
}
|
||||
|
72
packages/shared/src/astUtils.ts
Normal file
72
packages/shared/src/astUtils.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import {
|
||||
Identifier,
|
||||
Node,
|
||||
isReferenced,
|
||||
Function,
|
||||
ObjectProperty
|
||||
} from '@babel/types'
|
||||
|
||||
export function isReferencedIdentifier(
|
||||
id: Identifier,
|
||||
parent: Node | null,
|
||||
parentStack: Node[]
|
||||
) {
|
||||
if (!parent) {
|
||||
return true
|
||||
}
|
||||
|
||||
// is a special keyword but parsed as identifier
|
||||
if (id.name === 'arguments') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (isReferenced(id, parent)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// babel's isReferenced check returns false for ids being assigned to, so we
|
||||
// need to cover those cases here
|
||||
switch (parent.type) {
|
||||
case 'AssignmentExpression':
|
||||
case 'AssignmentPattern':
|
||||
return true
|
||||
case 'ObjectPattern':
|
||||
case 'ArrayPattern':
|
||||
return isInDestructureAssignment(parent, parentStack)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function isInDestructureAssignment(
|
||||
parent: Node,
|
||||
parentStack: Node[]
|
||||
): boolean {
|
||||
if (
|
||||
parent &&
|
||||
(parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
|
||||
) {
|
||||
let i = parentStack.length
|
||||
while (i--) {
|
||||
const p = parentStack[i]
|
||||
if (p.type === 'AssignmentExpression') {
|
||||
return true
|
||||
} else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const isFunctionType = (node: Node): node is Function => {
|
||||
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
|
||||
}
|
||||
|
||||
export const isStaticProperty = (node: Node): node is ObjectProperty =>
|
||||
node &&
|
||||
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
|
||||
!node.computed
|
||||
|
||||
export const isStaticPropertyKey = (node: Node, parent: Node) =>
|
||||
isStaticProperty(parent) && parent.key === node
|
@ -12,6 +12,7 @@ export * from './domAttrConfig'
|
||||
export * from './escapeHtml'
|
||||
export * from './looseEqual'
|
||||
export * from './toDisplayString'
|
||||
export * from './astUtils'
|
||||
|
||||
/**
|
||||
* List of @babel/parser plugins that are used for template expression
|
||||
|
Loading…
Reference in New Issue
Block a user