babel-plugin-transform-taroapi 4.1.10-alpha.0 → 4.1.10-beta.12

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.
Files changed (3) hide show
  1. package/dist/index.js +25 -0
  2. package/package.json +1 -1
  3. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babel-plugin-transform-taroapi",
3
- "version": "4.1.10-alpha.0",
3
+ "version": "4.1.10-beta.12",
4
4
  "author": "O2Team",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
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
- // 这些变量需要在每个 program 里重置
17
- const invokedApis: Map<string, string> = new Map()
18
- let taroName: string
19
- let needDefault: boolean
20
-
21
- let referTaro: any[]
22
-
23
- function canIUse (definition, scheme = '') {
24
- if (!scheme) return false
25
- const o = setWith({}, scheme, true, Object)
26
- return isMatchWith(definition, o, (a, b) => {
27
- if (a === '*' || b === true) return true
28
- })
29
- }
30
-
31
- function replaceCanIUse (ast: BabelCore.NodePath<BabelCore.types.CallExpression>, definition) {
32
- const args = ast.node.arguments
33
-
34
- if (args.length < 1) return
35
-
36
- // Note: 暂不考虑其他类型的参数映射
37
- if (t.isStringLiteral(args[0])) {
38
- const isSupported = canIUse(definition, args[0].value)
39
- ast.replaceInline(t.booleanLiteral(isSupported))
40
- }
41
- }
42
-
43
- return {
44
- name: 'babel-plugin-transform-taro-api',
45
- pre () {
46
- const { opts = {} as any } = this
47
- const { apis = new Set<string>(), bindingName = 'Taro', packageName = '@tarojs/taro-h5', definition = {} } = opts
48
- this.definition = {
49
- ...definition.apis,
50
- ...definition.components,
51
- [this.canIUse]: '*'
52
- }
53
- this.bindingName = bindingName
54
- this.packageName = packageName
55
- this.canIUse = 'canIUse'
56
- if (apis.size < 1) {
57
- apis.add(this.canIUse)
58
- Object.keys(definition.apis || {}).forEach(key => apis.add(key))
59
- }
60
- this.apis = apis
61
- },
62
- visitor: {
63
- ImportDeclaration (ast: BabelCore.NodePath<any>) {
64
- if (ast.node.source.value !== this.packageName) return
65
-
66
- ast.node.specifiers.forEach(node => {
67
- if (t.isImportDefaultSpecifier(node)) {
68
- needDefault = true
69
- taroName = node.local.name
70
- } else if (t.isImportSpecifier(node)) {
71
- const { imported } = node
72
- const propertyName = t.isIdentifier(imported) ? imported.name : imported.value
73
- if (this.apis.has(propertyName)) { // 记录 api 名字
74
- ast.scope.rename(node.local.name)
75
- invokedApis.set(propertyName, node.local.name)
76
- } else { // 如果是未实现的 api 改成 Taro.xxx
77
- needDefault = true
78
- const localName = node.local.name
79
- const binding = ast.scope.getBinding(localName)
80
- const idn = t.identifier(taroName)
81
- referTaro.push(idn)
82
- binding && binding.referencePaths.forEach(reference => {
83
- reference.replaceWith(
84
- t.memberExpression(
85
- idn,
86
- t.identifier(propertyName)
87
- ) as any
88
- )
89
- })
90
- }
91
- }
92
- })
93
- },
94
- MemberExpression (ast: BabelCore.NodePath<any>) {
95
- /* 处理 Taro.xxx */
96
- const isTaro = t.isIdentifier(ast.node.object, { name: taroName })
97
- const property = ast.node.property
98
- let propertyName: string | null = null
99
- let propName = 'name'
100
-
101
- if (!isTaro) return
102
-
103
- // 兼容一下 Taro['xxx']
104
- if (t.isStringLiteral(property)) {
105
- propName = 'value'
106
- }
107
- propertyName = property[propName]
108
-
109
- if (!propertyName) return
110
-
111
- // 同一 api 使用多次,读取变量名
112
- if (this.apis.has(propertyName)) {
113
- const parentNode = ast.parent as BabelCore.types.AssignmentExpression
114
- const isAssignment = t.isAssignmentExpression(parentNode) && parentNode.left === ast.node
115
-
116
- if (!isAssignment) {
117
- let identifier: BabelCore.types.Identifier
118
- if (invokedApis.has(propertyName)) {
119
- identifier = t.identifier(invokedApis.get(propertyName)!)
120
- } else {
121
- const newPropertyName = ast.scope.generateUid(propertyName)
122
- invokedApis.set(propertyName, newPropertyName)
123
- /* 未绑定作用域 */
124
- identifier = t.identifier(newPropertyName)
125
- }
126
- ast.replaceWith(identifier as any)
127
- }
128
- } else {
129
- needDefault = true
130
- }
131
- },
132
- CallExpression (ast: BabelCore.NodePath<any>) {
133
- if (!ast.scope.hasReference(this.canIUse)) return
134
- const callee = ast.node.callee
135
- if (t.isMemberExpression(callee) && t.isIdentifier(callee.object, { name: taroName })) {
136
- let propertyName: string | null = null
137
- let propName = 'name'
138
-
139
- // 兼容一下 Taro['xxx']
140
- if (t.isStringLiteral(callee.property)) {
141
- propName = 'value'
142
- }
143
- propertyName = callee.property[propName]
144
- if (propertyName === this.canIUse) {
145
- // Taro.canIUse or Taro['canIUse']
146
- replaceCanIUse(ast, this.definition)
147
- }
148
- } else if (invokedApis.has(this.canIUse)) {
149
- const { name } = t.identifier(invokedApis.get(this.canIUse)!)
150
- const isCanIUse = t.isIdentifier(callee, { name })
151
- // canIUse as _canIUse
152
- if (isCanIUse) replaceCanIUse(ast, this.definition)
153
- }
154
- },
155
- Program: {
156
- enter (ast) {
157
- needDefault = false
158
- referTaro = []
159
- invokedApis.clear()
160
-
161
- taroName = ast.scope.getBinding(this.bindingName)
162
- ? ast.scope.generateUid(this.bindingName)
163
- : this.bindingName
164
- },
165
- exit (ast) {
166
- const that = this
167
- // 防止重复引入
168
- let isTaroApiImported = false
169
- referTaro.forEach(node => {
170
- node.name = taroName
171
- })
172
-
173
- ast.traverse({
174
- ImportDeclaration (ast) {
175
- const isImportingTaroApi = ast.node.source.value === that.packageName
176
- if (!isImportingTaroApi) return
177
- if (isTaroApiImported) return ast.remove()
178
- isTaroApiImported = true
179
- const namedImports = Array.from(invokedApis.entries()).map(([imported, local]) => t.importSpecifier(t.identifier(local), t.identifier(imported)))
180
- if (needDefault) {
181
- const defaultImport = t.importDefaultSpecifier(t.identifier(taroName))
182
- ast.node.specifiers = [
183
- defaultImport,
184
- ...namedImports
185
- ]
186
- needDefault = false
187
- } else {
188
- ast.node.specifiers = namedImports
189
- }
190
- },
191
- CallExpression (ast: BabelCore.NodePath<any>) {
192
- if (!invokedApis.has(that.canIUse)) return
193
- const callee = ast.node.callee
194
- const { name } = t.identifier(invokedApis.get(that.canIUse)!)
195
- const isCanIUse = t.isIdentifier(callee, { name })
196
- if (isCanIUse) {
197
- // canIUse as _use
198
- replaceCanIUse(ast, that.definition)
199
- }
200
- },
201
- })
202
- }
203
- }
204
- }
205
- }
206
- }
207
-
208
- export default plugin
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