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,
|
makeMap,
|
||||||
babelParserDefaultPlugins,
|
babelParserDefaultPlugins,
|
||||||
hasOwn,
|
hasOwn,
|
||||||
isString
|
isString,
|
||||||
|
isReferencedIdentifier,
|
||||||
|
isInDestructureAssignment,
|
||||||
|
isStaticProperty,
|
||||||
|
isStaticPropertyKey,
|
||||||
|
isFunctionType
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import {
|
import {
|
||||||
Node,
|
Node,
|
||||||
Function,
|
|
||||||
Identifier,
|
Identifier,
|
||||||
ObjectProperty,
|
|
||||||
AssignmentExpression,
|
AssignmentExpression,
|
||||||
UpdateExpression
|
UpdateExpression
|
||||||
} from '@babel/types'
|
} from '@babel/types'
|
||||||
@ -279,7 +282,7 @@ export function processExpression(
|
|||||||
ids.push(node)
|
ids.push(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isFunction(node)) {
|
} else if (isFunctionType(node)) {
|
||||||
// walk function expressions and add its arguments to known identifiers
|
// walk function expressions and add its arguments to known identifiers
|
||||||
// so that we don't prefix them
|
// so that we don't prefix them
|
||||||
node.params.forEach(p =>
|
node.params.forEach(p =>
|
||||||
@ -372,97 +375,16 @@ export function processExpression(
|
|||||||
return ret
|
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[]) {
|
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
|
// skip whitelisted globals
|
||||||
if (isGloballyWhitelisted(id.name)) {
|
if (isGloballyWhitelisted(id.name)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// special case for webpack compilation
|
// special case for webpack compilation
|
||||||
if (id.name === 'require') {
|
if (id.name === 'require') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return isReferencedIdentifier(id, parent, parentStack)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringifyExpression(exp: ExpressionNode | string): string {
|
function stringifyExpression(exp: ExpressionNode | string): string {
|
||||||
|
@ -22,6 +22,10 @@ import {
|
|||||||
camelize,
|
camelize,
|
||||||
capitalize,
|
capitalize,
|
||||||
generateCodeFrame,
|
generateCodeFrame,
|
||||||
|
isFunctionType,
|
||||||
|
isReferencedIdentifier,
|
||||||
|
isStaticProperty,
|
||||||
|
isStaticPropertyKey,
|
||||||
makeMap
|
makeMap
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
@ -32,7 +36,6 @@ import {
|
|||||||
ArrayPattern,
|
ArrayPattern,
|
||||||
Identifier,
|
Identifier,
|
||||||
ExportSpecifier,
|
ExportSpecifier,
|
||||||
Function as FunctionNode,
|
|
||||||
TSType,
|
TSType,
|
||||||
TSTypeLiteral,
|
TSTypeLiteral,
|
||||||
TSFunctionType,
|
TSFunctionType,
|
||||||
@ -1028,7 +1031,7 @@ export function compileScript(
|
|||||||
) {
|
) {
|
||||||
;(walk as any)(node, {
|
;(walk as any)(node, {
|
||||||
enter(child: Node, parent: Node) {
|
enter(child: Node, parent: Node) {
|
||||||
if (isFunction(child)) {
|
if (isFunctionType(child)) {
|
||||||
this.skip()
|
this.skip()
|
||||||
}
|
}
|
||||||
if (child.type === 'AwaitExpression') {
|
if (child.type === 'AwaitExpression') {
|
||||||
@ -1819,11 +1822,11 @@ export function walkIdentifiers(
|
|||||||
if (node.type === 'Identifier') {
|
if (node.type === 'Identifier') {
|
||||||
if (
|
if (
|
||||||
!knownIds[node.name] &&
|
!knownIds[node.name] &&
|
||||||
isRefIdentifier(node, parent!, parentStack)
|
isReferencedIdentifier(node, parent!, parentStack)
|
||||||
) {
|
) {
|
||||||
onIdentifier(node, parent!, parentStack)
|
onIdentifier(node, parent!, parentStack)
|
||||||
}
|
}
|
||||||
} else if (isFunction(node)) {
|
} else if (isFunctionType(node)) {
|
||||||
// #3445
|
// #3445
|
||||||
// should not rewrite local variables sharing a name with a top-level ref
|
// should not rewrite local variables sharing a name with a top-level ref
|
||||||
if (node.body.type === 'BlockStatement') {
|
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(
|
function isCallOf(
|
||||||
node: Node | null | undefined,
|
node: Node | null | undefined,
|
||||||
test: string | ((id: string) => boolean)
|
test: string | ((id: string) => boolean)
|
||||||
|
@ -8,27 +8,29 @@ import {
|
|||||||
WritableComputedRef
|
WritableComputedRef
|
||||||
} from '@vue/reactivity'
|
} 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 $ref() {}
|
||||||
|
|
||||||
export function $shallowRef<T>(arg: T): T {
|
export function $shallowRef<T>(arg?: T): RefValue<T>
|
||||||
return arg
|
export function $shallowRef() {}
|
||||||
}
|
|
||||||
|
|
||||||
declare const ComputedRefMarker: unique symbol
|
declare const ComputedRefMarker: unique symbol
|
||||||
type ComputedValue<T> = T & { [ComputedRefMarker]?: any }
|
type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any }
|
||||||
|
|
||||||
declare const WritableComputedRefMarker: unique symbol
|
declare const WritableComputedRefMarker: unique symbol
|
||||||
type WritableComputedValue<T> = T & { [WritableComputedRefMarker]?: any }
|
type WritableComputedRefValue<T> = T & { [WritableComputedRefMarker]?: any }
|
||||||
|
|
||||||
export function $computed<T>(
|
export function $computed<T>(
|
||||||
getter: () => T,
|
getter: () => T,
|
||||||
debuggerOptions?: DebuggerOptions
|
debuggerOptions?: DebuggerOptions
|
||||||
): ComputedValue<T>
|
): ComputedRefValue<T>
|
||||||
export function $computed<T>(
|
export function $computed<T>(
|
||||||
options: WritableComputedOptions<T>,
|
options: WritableComputedOptions<T>,
|
||||||
debuggerOptions?: DebuggerOptions
|
debuggerOptions?: DebuggerOptions
|
||||||
): WritableComputedValue<T>
|
): WritableComputedRefValue<T>
|
||||||
export function $computed() {}
|
export function $computed() {}
|
||||||
|
|
||||||
export function $fromRefs<T>(source: T): ShallowUnwrapRef<T>
|
export function $fromRefs<T>(source: T): ShallowUnwrapRef<T>
|
||||||
@ -36,9 +38,13 @@ export function $fromRefs() {
|
|||||||
return null as any
|
return null as any
|
||||||
}
|
}
|
||||||
|
|
||||||
export function $raw<T>(value: ComputedValue<T>): ComputedRef<T>
|
export function $raw<T extends ComputedRefValue<any>>(
|
||||||
export function $raw<T>(value: WritableComputedValue<T>): WritableComputedRef<T>
|
value: T
|
||||||
export function $raw<T>(value: T): Ref<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() {
|
export function $raw() {
|
||||||
return null as any
|
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 './escapeHtml'
|
||||||
export * from './looseEqual'
|
export * from './looseEqual'
|
||||||
export * from './toDisplayString'
|
export * from './toDisplayString'
|
||||||
|
export * from './astUtils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of @babel/parser plugins that are used for template expression
|
* List of @babel/parser plugins that are used for template expression
|
||||||
|
Loading…
Reference in New Issue
Block a user