Skip to content

Commit

Permalink
refactor(compiler): extract shared ast transform utils
Browse files Browse the repository at this point in the history
Also improve referenced identifier check using isReferenced from  @babel/types
  • Loading branch information
yyx990803 committed Aug 22, 2021
1 parent 6be6c26 commit 62f7525
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 174 deletions.
94 changes: 8 additions & 86 deletions packages/compiler-core/src/transforms/transformExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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 =>
Expand Down Expand Up @@ -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 {
Expand Down
84 changes: 7 additions & 77 deletions packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import {
camelize,
capitalize,
generateCodeFrame,
isFunctionType,
isReferencedIdentifier,
isStaticProperty,
isStaticPropertyKey,
makeMap
} from '@vue/shared'
import {
Expand All @@ -32,7 +36,6 @@ import {
ArrayPattern,
Identifier,
ExportSpecifier,
Function as FunctionNode,
TSType,
TSTypeLiteral,
TSFunctionType,
Expand Down Expand Up @@ -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') {
Expand Down Expand Up @@ -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') {
Expand Down Expand Up @@ -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)
Expand Down
28 changes: 17 additions & 11 deletions packages/runtime-core/src/helpers/refSugar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,43 @@ 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>
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 changes: 72 additions & 0 deletions packages/shared/src/astUtils.ts
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 62f7525

Please sign in to comment.