Skip to content

Commit 62f7525

Browse files
committed
refactor(compiler): extract shared ast transform utils
Also improve referenced identifier check using isReferenced from @babel/types
1 parent 6be6c26 commit 62f7525

File tree

5 files changed

+105
-174
lines changed

5 files changed

+105
-174
lines changed

packages/compiler-core/src/transforms/transformExpression.ts

+8-86
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@ import {
2323
makeMap,
2424
babelParserDefaultPlugins,
2525
hasOwn,
26-
isString
26+
isString,
27+
isReferencedIdentifier,
28+
isInDestructureAssignment,
29+
isStaticProperty,
30+
isStaticPropertyKey,
31+
isFunctionType
2732
} from '@vue/shared'
2833
import { createCompilerError, ErrorCodes } from '../errors'
2934
import {
3035
Node,
31-
Function,
3236
Identifier,
33-
ObjectProperty,
3437
AssignmentExpression,
3538
UpdateExpression
3639
} from '@babel/types'
@@ -279,7 +282,7 @@ export function processExpression(
279282
ids.push(node)
280283
}
281284
}
282-
} else if (isFunction(node)) {
285+
} else if (isFunctionType(node)) {
283286
// walk function expressions and add its arguments to known identifiers
284287
// so that we don't prefix them
285288
node.params.forEach(p =>
@@ -372,97 +375,16 @@ export function processExpression(
372375
return ret
373376
}
374377

375-
const isFunction = (node: Node): node is Function => {
376-
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
377-
}
378-
379-
const isStaticProperty = (node: Node): node is ObjectProperty =>
380-
node &&
381-
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
382-
!node.computed
383-
384-
const isStaticPropertyKey = (node: Node, parent: Node) =>
385-
isStaticProperty(parent) && parent.key === node
386-
387378
function shouldPrefix(id: Identifier, parent: Node, parentStack: Node[]) {
388-
// declaration id
389-
if (
390-
(parent.type === 'VariableDeclarator' ||
391-
parent.type === 'ClassDeclaration') &&
392-
parent.id === id
393-
) {
394-
return false
395-
}
396-
397-
if (isFunction(parent)) {
398-
// function decalration/expression id
399-
if ((parent as any).id === id) {
400-
return false
401-
}
402-
// params list
403-
if (parent.params.includes(id)) {
404-
return false
405-
}
406-
}
407-
408-
// property key
409-
// this also covers object destructure pattern
410-
if (isStaticPropertyKey(id, parent)) {
411-
return false
412-
}
413-
414-
// non-assignment array destructure pattern
415-
if (
416-
parent.type === 'ArrayPattern' &&
417-
!isInDestructureAssignment(parent, parentStack)
418-
) {
419-
return false
420-
}
421-
422-
// member expression property
423-
if (
424-
(parent.type === 'MemberExpression' ||
425-
parent.type === 'OptionalMemberExpression') &&
426-
parent.property === id &&
427-
!parent.computed
428-
) {
429-
return false
430-
}
431-
432-
// is a special keyword but parsed as identifier
433-
if (id.name === 'arguments') {
434-
return false
435-
}
436-
437379
// skip whitelisted globals
438380
if (isGloballyWhitelisted(id.name)) {
439381
return false
440382
}
441-
442383
// special case for webpack compilation
443384
if (id.name === 'require') {
444385
return false
445386
}
446-
447-
return true
448-
}
449-
450-
function isInDestructureAssignment(parent: Node, parentStack: Node[]): boolean {
451-
if (
452-
parent &&
453-
(parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
454-
) {
455-
let i = parentStack.length
456-
while (i--) {
457-
const p = parentStack[i]
458-
if (p.type === 'AssignmentExpression') {
459-
return true
460-
} else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
461-
break
462-
}
463-
}
464-
}
465-
return false
387+
return isReferencedIdentifier(id, parent, parentStack)
466388
}
467389

468390
function stringifyExpression(exp: ExpressionNode | string): string {

packages/compiler-sfc/src/compileScript.ts

+7-77
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import {
2222
camelize,
2323
capitalize,
2424
generateCodeFrame,
25+
isFunctionType,
26+
isReferencedIdentifier,
27+
isStaticProperty,
28+
isStaticPropertyKey,
2529
makeMap
2630
} from '@vue/shared'
2731
import {
@@ -32,7 +36,6 @@ import {
3236
ArrayPattern,
3337
Identifier,
3438
ExportSpecifier,
35-
Function as FunctionNode,
3639
TSType,
3740
TSTypeLiteral,
3841
TSFunctionType,
@@ -1028,7 +1031,7 @@ export function compileScript(
10281031
) {
10291032
;(walk as any)(node, {
10301033
enter(child: Node, parent: Node) {
1031-
if (isFunction(child)) {
1034+
if (isFunctionType(child)) {
10321035
this.skip()
10331036
}
10341037
if (child.type === 'AwaitExpression') {
@@ -1819,11 +1822,11 @@ export function walkIdentifiers(
18191822
if (node.type === 'Identifier') {
18201823
if (
18211824
!knownIds[node.name] &&
1822-
isRefIdentifier(node, parent!, parentStack)
1825+
isReferencedIdentifier(node, parent!, parentStack)
18231826
) {
18241827
onIdentifier(node, parent!, parentStack)
18251828
}
1826-
} else if (isFunction(node)) {
1829+
} else if (isFunctionType(node)) {
18271830
// #3445
18281831
// should not rewrite local variables sharing a name with a top-level ref
18291832
if (node.body.type === 'BlockStatement') {
@@ -1881,79 +1884,6 @@ export function walkIdentifiers(
18811884
})
18821885
}
18831886

1884-
function isRefIdentifier(
1885-
id: Identifier,
1886-
parent: Node | null,
1887-
parentStack: Node[]
1888-
) {
1889-
if (!parent) {
1890-
return true
1891-
}
1892-
1893-
// declaration id
1894-
if (
1895-
(parent.type === 'VariableDeclarator' ||
1896-
parent.type === 'ClassDeclaration') &&
1897-
parent.id === id
1898-
) {
1899-
return false
1900-
}
1901-
1902-
if (isFunction(parent)) {
1903-
// function decalration/expression id
1904-
if ((parent as any).id === id) {
1905-
return false
1906-
}
1907-
// params list
1908-
if (parent.params.includes(id)) {
1909-
return false
1910-
}
1911-
}
1912-
1913-
// property key
1914-
// this also covers object destructure pattern
1915-
if (isStaticPropertyKey(id, parent)) {
1916-
return false
1917-
}
1918-
1919-
// non-assignment array destructure pattern
1920-
if (
1921-
parent.type === 'ArrayPattern' &&
1922-
!isInDestructureAssignment(parent, parentStack)
1923-
) {
1924-
return false
1925-
}
1926-
1927-
// member expression property
1928-
if (
1929-
(parent.type === 'MemberExpression' ||
1930-
parent.type === 'OptionalMemberExpression') &&
1931-
parent.property === id &&
1932-
!parent.computed
1933-
) {
1934-
return false
1935-
}
1936-
1937-
// is a special keyword but parsed as identifier
1938-
if (id.name === 'arguments') {
1939-
return false
1940-
}
1941-
1942-
return true
1943-
}
1944-
1945-
const isStaticProperty = (node: Node): node is ObjectProperty =>
1946-
node &&
1947-
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
1948-
!node.computed
1949-
1950-
const isStaticPropertyKey = (node: Node, parent: Node) =>
1951-
isStaticProperty(parent) && parent.key === node
1952-
1953-
function isFunction(node: Node): node is FunctionNode {
1954-
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
1955-
}
1956-
19571887
function isCallOf(
19581888
node: Node | null | undefined,
19591889
test: string | ((id: string) => boolean)

packages/runtime-core/src/helpers/refSugar.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,43 @@ import {
88
WritableComputedRef
99
} from '@vue/reactivity'
1010

11-
export function $ref<T>(arg: T | Ref<T>): UnwrapRef<T>
11+
declare const RefMarker: unique symbol
12+
type RefValue<T> = T & { [RefMarker]?: any }
13+
14+
export function $ref<T>(arg?: T | Ref<T>): RefValue<UnwrapRef<T>>
1215
export function $ref() {}
1316

14-
export function $shallowRef<T>(arg: T): T {
15-
return arg
16-
}
17+
export function $shallowRef<T>(arg?: T): RefValue<T>
18+
export function $shallowRef() {}
1719

1820
declare const ComputedRefMarker: unique symbol
19-
type ComputedValue<T> = T & { [ComputedRefMarker]?: any }
21+
type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any }
2022

2123
declare const WritableComputedRefMarker: unique symbol
22-
type WritableComputedValue<T> = T & { [WritableComputedRefMarker]?: any }
24+
type WritableComputedRefValue<T> = T & { [WritableComputedRefMarker]?: any }
2325

2426
export function $computed<T>(
2527
getter: () => T,
2628
debuggerOptions?: DebuggerOptions
27-
): ComputedValue<T>
29+
): ComputedRefValue<T>
2830
export function $computed<T>(
2931
options: WritableComputedOptions<T>,
3032
debuggerOptions?: DebuggerOptions
31-
): WritableComputedValue<T>
33+
): WritableComputedRefValue<T>
3234
export function $computed() {}
3335

3436
export function $fromRefs<T>(source: T): ShallowUnwrapRef<T>
3537
export function $fromRefs() {
3638
return null as any
3739
}
3840

39-
export function $raw<T>(value: ComputedValue<T>): ComputedRef<T>
40-
export function $raw<T>(value: WritableComputedValue<T>): WritableComputedRef<T>
41-
export function $raw<T>(value: T): Ref<T>
41+
export function $raw<T extends ComputedRefValue<any>>(
42+
value: T
43+
): T extends ComputedRefValue<infer V> ? ComputedRef<V> : never
44+
export function $raw<T>(
45+
value: WritableComputedRefValue<T>
46+
): WritableComputedRef<T>
47+
export function $raw<T>(value: RefValue<T>): Ref<T>
4248
export function $raw() {
4349
return null as any
4450
}

packages/shared/src/astUtils.ts

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {
2+
Identifier,
3+
Node,
4+
isReferenced,
5+
Function,
6+
ObjectProperty
7+
} from '@babel/types'
8+
9+
export function isReferencedIdentifier(
10+
id: Identifier,
11+
parent: Node | null,
12+
parentStack: Node[]
13+
) {
14+
if (!parent) {
15+
return true
16+
}
17+
18+
// is a special keyword but parsed as identifier
19+
if (id.name === 'arguments') {
20+
return false
21+
}
22+
23+
if (isReferenced(id, parent)) {
24+
return true
25+
}
26+
27+
// babel's isReferenced check returns false for ids being assigned to, so we
28+
// need to cover those cases here
29+
switch (parent.type) {
30+
case 'AssignmentExpression':
31+
case 'AssignmentPattern':
32+
return true
33+
case 'ObjectPattern':
34+
case 'ArrayPattern':
35+
return isInDestructureAssignment(parent, parentStack)
36+
}
37+
38+
return false
39+
}
40+
41+
export function isInDestructureAssignment(
42+
parent: Node,
43+
parentStack: Node[]
44+
): boolean {
45+
if (
46+
parent &&
47+
(parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
48+
) {
49+
let i = parentStack.length
50+
while (i--) {
51+
const p = parentStack[i]
52+
if (p.type === 'AssignmentExpression') {
53+
return true
54+
} else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
55+
break
56+
}
57+
}
58+
}
59+
return false
60+
}
61+
62+
export const isFunctionType = (node: Node): node is Function => {
63+
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
64+
}
65+
66+
export const isStaticProperty = (node: Node): node is ObjectProperty =>
67+
node &&
68+
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
69+
!node.computed
70+
71+
export const isStaticPropertyKey = (node: Node, parent: Node) =>
72+
isStaticProperty(parent) && parent.key === node

packages/shared/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export * from './domAttrConfig'
1212
export * from './escapeHtml'
1313
export * from './looseEqual'
1414
export * from './toDisplayString'
15+
export * from './astUtils'
1516

1617
/**
1718
* List of @babel/parser plugins that are used for template expression

0 commit comments

Comments
 (0)