babel-plugin-transform-taroapi 4.1.10-beta.14 → 4.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +25 -0
- package/package.json +1 -1
- package/src/index.ts +234 -208
package/dist/index.js
CHANGED
|
@@ -3,6 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const lodash_1 = require("lodash");
|
|
4
4
|
const plugin = function (babel) {
|
|
5
5
|
const t = babel.types;
|
|
6
|
+
// 默认属性映射:将驼峰命名转换为 kebab-case(仅 H5 平台使用)
|
|
7
|
+
const DEFAULT_ATTRIBUTE_MAP = {
|
|
8
|
+
ariaRole: 'role',
|
|
9
|
+
ariaLabel: 'aria-label',
|
|
10
|
+
ariaHidden: 'aria-hidden',
|
|
11
|
+
ariaChecked: 'aria-checked',
|
|
12
|
+
ariaSelected: 'aria-selected',
|
|
13
|
+
ariaRoledescription: 'aria-roledescription',
|
|
14
|
+
ariaValuemax: 'aria-valuemax',
|
|
15
|
+
ariaValuemin: 'aria-valuemin',
|
|
16
|
+
ariaValuenow: 'aria-valuenow',
|
|
17
|
+
ariaValuetext: 'aria-valuetext',
|
|
18
|
+
};
|
|
6
19
|
// 这些变量需要在每个 program 里重置
|
|
7
20
|
const invokedApis = new Map();
|
|
8
21
|
let taroName;
|
|
@@ -133,6 +146,18 @@ const plugin = function (babel) {
|
|
|
133
146
|
replaceCanIUse(ast, this.definition);
|
|
134
147
|
}
|
|
135
148
|
},
|
|
149
|
+
JSXAttribute(ast) {
|
|
150
|
+
// 仅在 H5 平台进行属性转换
|
|
151
|
+
if (process.env.TARO_ENV !== 'h5')
|
|
152
|
+
return;
|
|
153
|
+
const { name } = ast.node;
|
|
154
|
+
if (!t.isJSXIdentifier(name))
|
|
155
|
+
return;
|
|
156
|
+
// 使用默认属性映射进行转换
|
|
157
|
+
if (DEFAULT_ATTRIBUTE_MAP[name.name]) {
|
|
158
|
+
name.name = DEFAULT_ATTRIBUTE_MAP[name.name];
|
|
159
|
+
}
|
|
160
|
+
},
|
|
136
161
|
Program: {
|
|
137
162
|
enter(ast) {
|
|
138
163
|
needDefault = false;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,208 +1,234 @@
|
|
|
1
|
-
import { isMatchWith, setWith } from 'lodash'
|
|
2
|
-
|
|
3
|
-
import type * as BabelCore from '@babel/core'
|
|
4
|
-
|
|
5
|
-
interface IState extends BabelCore.PluginPass {
|
|
6
|
-
apis: Set<string>
|
|
7
|
-
bindingName: string
|
|
8
|
-
packageName: string
|
|
9
|
-
canIUse: string
|
|
10
|
-
definition: Record<string, any>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const plugin = function (babel: typeof BabelCore): BabelCore.PluginObj<IState> {
|
|
14
|
-
const t = babel.types
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
const
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
1
|
+
import { isMatchWith, setWith } from 'lodash'
|
|
2
|
+
|
|
3
|
+
import type * as BabelCore from '@babel/core'
|
|
4
|
+
|
|
5
|
+
interface IState extends BabelCore.PluginPass {
|
|
6
|
+
apis: Set<string>
|
|
7
|
+
bindingName: string
|
|
8
|
+
packageName: string
|
|
9
|
+
canIUse: string
|
|
10
|
+
definition: Record<string, any>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const plugin = function (babel: typeof BabelCore): BabelCore.PluginObj<IState> {
|
|
14
|
+
const t = babel.types
|
|
15
|
+
|
|
16
|
+
// 默认属性映射:将驼峰命名转换为 kebab-case(仅 H5 平台使用)
|
|
17
|
+
const DEFAULT_ATTRIBUTE_MAP: Record<string, string> = {
|
|
18
|
+
ariaRole: 'role',
|
|
19
|
+
ariaLabel: 'aria-label',
|
|
20
|
+
ariaHidden: 'aria-hidden',
|
|
21
|
+
ariaChecked: 'aria-checked',
|
|
22
|
+
ariaSelected: 'aria-selected',
|
|
23
|
+
ariaRoledescription: 'aria-roledescription',
|
|
24
|
+
ariaValuemax: 'aria-valuemax',
|
|
25
|
+
ariaValuemin: 'aria-valuemin',
|
|
26
|
+
ariaValuenow: 'aria-valuenow',
|
|
27
|
+
ariaValuetext: 'aria-valuetext',
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 这些变量需要在每个 program 里重置
|
|
31
|
+
const invokedApis: Map<string, string> = new Map()
|
|
32
|
+
let taroName: string
|
|
33
|
+
let needDefault: boolean
|
|
34
|
+
|
|
35
|
+
let referTaro: any[]
|
|
36
|
+
|
|
37
|
+
function canIUse (definition, scheme = '') {
|
|
38
|
+
if (!scheme) return false
|
|
39
|
+
const o = setWith({}, scheme, true, Object)
|
|
40
|
+
return isMatchWith(definition, o, (a, b) => {
|
|
41
|
+
if (a === '*' || b === true) return true
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function replaceCanIUse (ast: BabelCore.NodePath<BabelCore.types.CallExpression>, definition) {
|
|
46
|
+
const args = ast.node.arguments
|
|
47
|
+
|
|
48
|
+
if (args.length < 1) return
|
|
49
|
+
|
|
50
|
+
// Note: 暂不考虑其他类型的参数映射
|
|
51
|
+
if (t.isStringLiteral(args[0])) {
|
|
52
|
+
const isSupported = canIUse(definition, args[0].value)
|
|
53
|
+
ast.replaceInline(t.booleanLiteral(isSupported))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
name: 'babel-plugin-transform-taro-api',
|
|
59
|
+
pre () {
|
|
60
|
+
const { opts = {} as any } = this
|
|
61
|
+
const { apis = new Set<string>(), bindingName = 'Taro', packageName = '@tarojs/taro-h5', definition = {} } = opts
|
|
62
|
+
this.definition = {
|
|
63
|
+
...definition.apis,
|
|
64
|
+
...definition.components,
|
|
65
|
+
[this.canIUse]: '*'
|
|
66
|
+
}
|
|
67
|
+
this.bindingName = bindingName
|
|
68
|
+
this.packageName = packageName
|
|
69
|
+
this.canIUse = 'canIUse'
|
|
70
|
+
if (apis.size < 1) {
|
|
71
|
+
apis.add(this.canIUse)
|
|
72
|
+
Object.keys(definition.apis || {}).forEach(key => apis.add(key))
|
|
73
|
+
}
|
|
74
|
+
this.apis = apis
|
|
75
|
+
},
|
|
76
|
+
visitor: {
|
|
77
|
+
ImportDeclaration (ast: BabelCore.NodePath<any>) {
|
|
78
|
+
if (ast.node.source.value !== this.packageName) return
|
|
79
|
+
|
|
80
|
+
ast.node.specifiers.forEach(node => {
|
|
81
|
+
if (t.isImportDefaultSpecifier(node)) {
|
|
82
|
+
needDefault = true
|
|
83
|
+
taroName = node.local.name
|
|
84
|
+
} else if (t.isImportSpecifier(node)) {
|
|
85
|
+
const { imported } = node
|
|
86
|
+
const propertyName = t.isIdentifier(imported) ? imported.name : imported.value
|
|
87
|
+
if (this.apis.has(propertyName)) { // 记录 api 名字
|
|
88
|
+
ast.scope.rename(node.local.name)
|
|
89
|
+
invokedApis.set(propertyName, node.local.name)
|
|
90
|
+
} else { // 如果是未实现的 api 改成 Taro.xxx
|
|
91
|
+
needDefault = true
|
|
92
|
+
const localName = node.local.name
|
|
93
|
+
const binding = ast.scope.getBinding(localName)
|
|
94
|
+
const idn = t.identifier(taroName)
|
|
95
|
+
referTaro.push(idn)
|
|
96
|
+
binding && binding.referencePaths.forEach(reference => {
|
|
97
|
+
reference.replaceWith(
|
|
98
|
+
t.memberExpression(
|
|
99
|
+
idn,
|
|
100
|
+
t.identifier(propertyName)
|
|
101
|
+
) as any
|
|
102
|
+
)
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
},
|
|
108
|
+
MemberExpression (ast: BabelCore.NodePath<any>) {
|
|
109
|
+
/* 处理 Taro.xxx */
|
|
110
|
+
const isTaro = t.isIdentifier(ast.node.object, { name: taroName })
|
|
111
|
+
const property = ast.node.property
|
|
112
|
+
let propertyName: string | null = null
|
|
113
|
+
let propName = 'name'
|
|
114
|
+
|
|
115
|
+
if (!isTaro) return
|
|
116
|
+
|
|
117
|
+
// 兼容一下 Taro['xxx']
|
|
118
|
+
if (t.isStringLiteral(property)) {
|
|
119
|
+
propName = 'value'
|
|
120
|
+
}
|
|
121
|
+
propertyName = property[propName]
|
|
122
|
+
|
|
123
|
+
if (!propertyName) return
|
|
124
|
+
|
|
125
|
+
// 同一 api 使用多次,读取变量名
|
|
126
|
+
if (this.apis.has(propertyName)) {
|
|
127
|
+
const parentNode = ast.parent as BabelCore.types.AssignmentExpression
|
|
128
|
+
const isAssignment = t.isAssignmentExpression(parentNode) && parentNode.left === ast.node
|
|
129
|
+
|
|
130
|
+
if (!isAssignment) {
|
|
131
|
+
let identifier: BabelCore.types.Identifier
|
|
132
|
+
if (invokedApis.has(propertyName)) {
|
|
133
|
+
identifier = t.identifier(invokedApis.get(propertyName)!)
|
|
134
|
+
} else {
|
|
135
|
+
const newPropertyName = ast.scope.generateUid(propertyName)
|
|
136
|
+
invokedApis.set(propertyName, newPropertyName)
|
|
137
|
+
/* 未绑定作用域 */
|
|
138
|
+
identifier = t.identifier(newPropertyName)
|
|
139
|
+
}
|
|
140
|
+
ast.replaceWith(identifier as any)
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
needDefault = true
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
CallExpression (ast: BabelCore.NodePath<any>) {
|
|
147
|
+
if (!ast.scope.hasReference(this.canIUse)) return
|
|
148
|
+
const callee = ast.node.callee
|
|
149
|
+
if (t.isMemberExpression(callee) && t.isIdentifier(callee.object, { name: taroName })) {
|
|
150
|
+
let propertyName: string | null = null
|
|
151
|
+
let propName = 'name'
|
|
152
|
+
|
|
153
|
+
// 兼容一下 Taro['xxx']
|
|
154
|
+
if (t.isStringLiteral(callee.property)) {
|
|
155
|
+
propName = 'value'
|
|
156
|
+
}
|
|
157
|
+
propertyName = callee.property[propName]
|
|
158
|
+
if (propertyName === this.canIUse) {
|
|
159
|
+
// Taro.canIUse or Taro['canIUse']
|
|
160
|
+
replaceCanIUse(ast, this.definition)
|
|
161
|
+
}
|
|
162
|
+
} else if (invokedApis.has(this.canIUse)) {
|
|
163
|
+
const { name } = t.identifier(invokedApis.get(this.canIUse)!)
|
|
164
|
+
const isCanIUse = t.isIdentifier(callee, { name })
|
|
165
|
+
// canIUse as _canIUse
|
|
166
|
+
if (isCanIUse) replaceCanIUse(ast, this.definition)
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
JSXAttribute (ast: BabelCore.NodePath<any>) {
|
|
170
|
+
// 仅在 H5 平台进行属性转换
|
|
171
|
+
if (process.env.TARO_ENV !== 'h5') return
|
|
172
|
+
|
|
173
|
+
const { name } = ast.node
|
|
174
|
+
if (!t.isJSXIdentifier(name)) return
|
|
175
|
+
|
|
176
|
+
// 使用默认属性映射进行转换
|
|
177
|
+
if (DEFAULT_ATTRIBUTE_MAP[name.name]) {
|
|
178
|
+
name.name = DEFAULT_ATTRIBUTE_MAP[name.name]
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
Program: {
|
|
182
|
+
enter (ast) {
|
|
183
|
+
needDefault = false
|
|
184
|
+
referTaro = []
|
|
185
|
+
invokedApis.clear()
|
|
186
|
+
|
|
187
|
+
taroName = ast.scope.getBinding(this.bindingName)
|
|
188
|
+
? ast.scope.generateUid(this.bindingName)
|
|
189
|
+
: this.bindingName
|
|
190
|
+
},
|
|
191
|
+
exit (ast) {
|
|
192
|
+
const that = this
|
|
193
|
+
// 防止重复引入
|
|
194
|
+
let isTaroApiImported = false
|
|
195
|
+
referTaro.forEach(node => {
|
|
196
|
+
node.name = taroName
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
ast.traverse({
|
|
200
|
+
ImportDeclaration (ast) {
|
|
201
|
+
const isImportingTaroApi = ast.node.source.value === that.packageName
|
|
202
|
+
if (!isImportingTaroApi) return
|
|
203
|
+
if (isTaroApiImported) return ast.remove()
|
|
204
|
+
isTaroApiImported = true
|
|
205
|
+
const namedImports = Array.from(invokedApis.entries()).map(([imported, local]) => t.importSpecifier(t.identifier(local), t.identifier(imported)))
|
|
206
|
+
if (needDefault) {
|
|
207
|
+
const defaultImport = t.importDefaultSpecifier(t.identifier(taroName))
|
|
208
|
+
ast.node.specifiers = [
|
|
209
|
+
defaultImport,
|
|
210
|
+
...namedImports
|
|
211
|
+
]
|
|
212
|
+
needDefault = false
|
|
213
|
+
} else {
|
|
214
|
+
ast.node.specifiers = namedImports
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
CallExpression (ast: BabelCore.NodePath<any>) {
|
|
218
|
+
if (!invokedApis.has(that.canIUse)) return
|
|
219
|
+
const callee = ast.node.callee
|
|
220
|
+
const { name } = t.identifier(invokedApis.get(that.canIUse)!)
|
|
221
|
+
const isCanIUse = t.isIdentifier(callee, { name })
|
|
222
|
+
if (isCanIUse) {
|
|
223
|
+
// canIUse as _use
|
|
224
|
+
replaceCanIUse(ast, that.definition)
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export default plugin
|