-
Notifications
You must be signed in to change notification settings - Fork 1
/
replacement.ts
125 lines (119 loc) · 4.49 KB
/
replacement.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import type { NodePath } from '@babel/traverse'
import type { MemberExpression, ObjectProperty, RestElement, VariableDeclarator } from '@babel/types'
import babel from '@babel/core'
import t from '@babel/types'
import logger from './log'
function isValidMemberExpression(node: NodePath<MemberExpression>, constObjectName: string) {
if (constObjectName === 'this')
return node.get('object').isThisExpression()
return node.get('object').isIdentifier({ name: constObjectName })
}
function isValidDestructure(node: NodePath<VariableDeclarator>, constObjectName: string) {
if (constObjectName === 'this')
return node.get('init').isThisExpression()
return node.get('init').isIdentifier({ name: constObjectName })
}
/**
* Replace `props.prop` with given const
* @param node Root node of function body needs to be optimized
* @param constObjectName Name of the object that contains the constants
* @param consts Map of constants
*/
export function replaceMemberExpression(node: NodePath, constObjectName: string, consts: Record<string, unknown>) {
const memberExpressions: NodePath<MemberExpression>[] = []
const potentialOptimizeProps: Set<string> = new Set()
node.traverse({
MemberExpression(nodePath) {
if (isValidMemberExpression(nodePath, constObjectName)) {
memberExpressions.push(nodePath)
potentialOptimizeProps.add(nodePath.toString().split('.')[1])
}
},
})
memberExpressions.forEach((nodePath) => {
const propNode = nodePath.get('property').node
if (propNode.type !== 'Identifier')
return
const propName = propNode.name
if (propName in consts) {
nodePath.replaceWithSourceString(`${consts[propName]}`)
potentialOptimizeProps.delete(propName)
}
})
logger.log(`Potential optimize member expression: ${Array.from(potentialOptimizeProps).join(', ')}`)
}
function replaceDestructureWithOneConst(
root: NodePath,
property: NodePath<ObjectProperty | RestElement>,
consts: Record<string, unknown>,
potentialOptimizeProps: Set<string>,
) {
if (!property.isObjectProperty())
return
const key = property.get('key')
if (!key.isIdentifier())
return
const propName = key.node.name
if (propName in consts) {
const value = babel.template.expression.ast(`${consts[propName]}`)
const newVariableDeclarator = t.variableDeclarator(t.identifier(propName), value)
root.replaceWith(newVariableDeclarator)
}
else {
potentialOptimizeProps.add(propName)
}
}
function replaceDestructureWithMultiDeclarations(
root: NodePath,
properties: NodePath<ObjectProperty | RestElement>[],
consts: Record<string, unknown>,
potentialOptimizeProps: Set<string>,
) {
const deleteProperties: NodePath<ObjectProperty>[] = []
properties.forEach((property) => {
if (property.isObjectProperty()) {
const key = property.get('key')
if (key.isIdentifier()) {
const propName = key.node.name
if (propName in consts)
deleteProperties.push(property)
else
potentialOptimizeProps.add(propName)
}
}
})
const newVariableDeclarators: VariableDeclarator[] = deleteProperties.map((property) => {
const key = property.get('key')
if (!key.isIdentifier())
throw new Error('Unexpected property key type')
const propName = key.node.name
const value = babel.template.expression.ast(`${consts[propName]}`)
return t.variableDeclarator(t.identifier(propName), value)
})
deleteProperties.forEach(property => property.remove())
root.insertAfter(newVariableDeclarators)
}
/**
* replace `const { prop } = props` with given const
* @param node Root node of function body needs to be optimized
* @param constObjectName Name of the object that contains the constants
* @param consts Map of constants
*/
export function replaceDestructure(node: NodePath, constObjectName: string, consts: Record<string, unknown>) {
const potentialOptimizeProps: Set<string> = new Set()
node.traverse({
VariableDeclarator(nodePath) {
if (!isValidDestructure(nodePath, constObjectName))
return
const id = nodePath.get('id')
if (!id.isObjectPattern())
return
const properties = id.get('properties')
if (properties.length === 1)
replaceDestructureWithOneConst(nodePath, properties[0], consts, potentialOptimizeProps)
else
replaceDestructureWithMultiDeclarations(nodePath, properties, consts, potentialOptimizeProps)
},
})
logger.log(`Potential optimize destructure: ${Array.from(potentialOptimizeProps).join(', ')}`)
}