babel-plugin-transform-taroapi 4.1.12-beta.11 → 4.1.12-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 +109 -24
  2. package/package.json +1 -1
  3. package/src/index.ts +142 -24
package/dist/index.js CHANGED
@@ -25,6 +25,54 @@ const plugin = function (babel) {
25
25
  }
26
26
  return node;
27
27
  }
28
+ function getTaroNamespaceCall(t, callee, taroName, file) {
29
+ var _a;
30
+ let target = callee;
31
+ // 如果是可选调用(例如 Taro.JDMTA.isTrafficMapEnable?.()),先取里面的 callee
32
+ if (t.isOptionalCallExpression(target)) {
33
+ target = target.callee;
34
+ }
35
+ // 去掉 TS 断言 / 非空等包裹
36
+ target = stripTSCast(target);
37
+ if (!t.isMemberExpression(target) && !t.isOptionalMemberExpression(target)) {
38
+ return null;
39
+ }
40
+ // target.object 通常是 Taro.xx 这一层,
41
+ // 也可能是形如 (_tmp = Taro.xx) 这样的赋值表达式,需要再解一层。
42
+ let inner = stripTSCast(target.object);
43
+ // 兼容 222 这类形态:(_tmp = Taro.JDMTA).isTrafficMapEnable?.()
44
+ if (t.isAssignmentExpression(inner)) {
45
+ inner = stripTSCast(inner.right);
46
+ }
47
+ if (!t.isMemberExpression(inner) && !t.isOptionalMemberExpression(inner)) {
48
+ return null;
49
+ }
50
+ if (!t.isIdentifier(inner.object, { name: taroName })) {
51
+ return null;
52
+ }
53
+ let namespaceName = null;
54
+ if (t.isIdentifier(inner.property)) {
55
+ namespaceName = inner.property.name;
56
+ }
57
+ else if (t.isStringLiteral(inner.property)) {
58
+ namespaceName = inner.property.value;
59
+ }
60
+ let methodName = null;
61
+ if (t.isIdentifier(target.property)) {
62
+ methodName = target.property.name;
63
+ }
64
+ else if (t.isStringLiteral(target.property)) {
65
+ methodName = target.property.value;
66
+ }
67
+ if (!namespaceName || !methodName)
68
+ return null;
69
+ if (process.env.JDAPI_DEBUG_TAROAPI === 'true') {
70
+ const filename = ((_a = file === null || file === void 0 ? void 0 : file.opts) === null || _a === void 0 ? void 0 : _a.filename) || '';
71
+ // eslint-disable-next-line no-console
72
+ console.log('[jdapi-core-taroapi] getTaroNamespaceCall hit:', `${namespaceName}_${methodName}`, 'file =', filename);
73
+ }
74
+ return { namespaceName, methodName };
75
+ }
28
76
  // 这些变量需要在每个 program 里重置
29
77
  const invokedApis = new Map();
30
78
  let taroName;
@@ -164,31 +212,29 @@ const plugin = function (babel) {
164
212
  const callee = ast.node.callee;
165
213
  // 对存在命名空间的 API 支持 tree-shaking:Taro.xx.yy -> xx_yy
166
214
  // 同时兼容:可选链调用(Taro?.JDMTA.pv() / Taro.JDMTA?.pv())、TS 类型断言(as any / ! / satisfies)
167
- if (t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee)) {
168
- const rawObject = stripTSCast(callee.object);
169
- if (t.isMemberExpression(rawObject) || t.isOptionalMemberExpression(rawObject)) {
170
- const inner = rawObject;
171
- const isTaroNamespace = t.isIdentifier(inner.object, { name: taroName });
172
- if (isTaroNamespace) {
173
- const namespaceName = t.isIdentifier(inner.property) ? inner.property.name : (t.isStringLiteral(inner.property) ? inner.property.value : null);
174
- const methodName = t.isIdentifier(callee.property) ? callee.property.name : (t.isStringLiteral(callee.property) ? callee.property.value : null);
175
- if (namespaceName && methodName) {
176
- const flatName = `${namespaceName}_${methodName}`;
177
- if (this.apis.has(flatName)) {
178
- let identifier;
179
- if (invokedApis.has(flatName)) {
180
- identifier = t.identifier(invokedApis.get(flatName));
181
- }
182
- else {
183
- const newName = ast.scope.generateUid(flatName);
184
- invokedApis.set(flatName, newName);
185
- identifier = t.identifier(newName);
186
- }
187
- ast.node.callee = identifier;
188
- return;
189
- }
190
- }
215
+ const nsInfo = getTaroNamespaceCall(t, callee, taroName, this.file);
216
+ if (nsInfo) {
217
+ const { namespaceName, methodName } = nsInfo;
218
+ const flatName = `${namespaceName}_${methodName}`;
219
+ if (this.apis.has(flatName)) {
220
+ let identifier;
221
+ if (invokedApis.has(flatName)) {
222
+ identifier = t.identifier(invokedApis.get(flatName));
223
+ }
224
+ else {
225
+ const newName = ast.scope.generateUid(flatName);
226
+ invokedApis.set(flatName, newName);
227
+ identifier = t.identifier(newName);
228
+ }
229
+ // 如果当前是可选调用(OptionalCallExpression),直接将整条调用改写为普通函数调用,
230
+ // 避免后续 preset-env 再对可选链做降级,导致重新依赖 Taro.JDMTA。
231
+ if (t.isOptionalCallExpression(ast.node)) {
232
+ ast.replaceWith(t.callExpression(identifier, ast.node.arguments));
233
+ }
234
+ else {
235
+ ast.node.callee = identifier;
191
236
  }
237
+ return;
192
238
  }
193
239
  }
194
240
  if (!ast.scope.hasReference(this.canIUse))
@@ -234,6 +280,45 @@ const plugin = function (babel) {
234
280
  taroName = ast.scope.getBinding(this.bindingName)
235
281
  ? ast.scope.generateUid(this.bindingName)
236
282
  : this.bindingName;
283
+ // 预扫描:在正式 visitor 遍历之前,先找到 import 确定 taroName,
284
+ // 然后把所有 namespace API 的 OptionalCallExpression 降级为普通 CallExpression。
285
+ // 这样可以抢在 @babel/plugin-transform-optional-chaining 展开可选链之前完成替换。
286
+ const pkgName = this.packageName;
287
+ let preScanTaroName = '';
288
+ ast.traverse({
289
+ ImportDeclaration(importPath) {
290
+ if (importPath.node.source.value !== pkgName)
291
+ return;
292
+ for (const spec of importPath.node.specifiers) {
293
+ if (t.isImportDefaultSpecifier(spec)) {
294
+ preScanTaroName = spec.local.name;
295
+ break;
296
+ }
297
+ }
298
+ importPath.stop();
299
+ }
300
+ });
301
+ if (preScanTaroName) {
302
+ const apis = this.apis;
303
+ const file = this.file;
304
+ ast.traverse({
305
+ OptionalCallExpression(optPath) {
306
+ var _a;
307
+ const nsInfo = getTaroNamespaceCall(t, optPath.node.callee, preScanTaroName, file);
308
+ if (nsInfo) {
309
+ const flatName = `${nsInfo.namespaceName}_${nsInfo.methodName}`;
310
+ if (apis.has(flatName)) {
311
+ if (process.env.JDAPI_DEBUG_TAROAPI === 'true') {
312
+ const filename = ((_a = file === null || file === void 0 ? void 0 : file.opts) === null || _a === void 0 ? void 0 : _a.filename) || '';
313
+ // eslint-disable-next-line no-console
314
+ console.log('[jdapi-core-taroapi] pre-transform: strip optional call for', flatName, 'in file:', filename, 'code =', optPath.toString());
315
+ }
316
+ optPath.replaceWith(t.callExpression(optPath.node.callee, optPath.node.arguments));
317
+ }
318
+ }
319
+ }
320
+ });
321
+ }
237
322
  },
238
323
  exit(ast) {
239
324
  const that = this;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babel-plugin-transform-taroapi",
3
- "version": "4.1.12-beta.11",
3
+ "version": "4.1.12-beta.12",
4
4
  "author": "O2Team",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -39,6 +39,72 @@ const plugin = function (babel: typeof BabelCore): BabelCore.PluginObj<IState> {
39
39
  return node
40
40
  }
41
41
 
42
+ function getTaroNamespaceCall (
43
+ t: typeof BabelCore.types,
44
+ callee: BabelCore.types.Expression,
45
+ taroName: string,
46
+ file?: BabelCore.BabelFile | null
47
+ ): { namespaceName: string, methodName: string } | null {
48
+ let target: any = callee
49
+
50
+ // 如果是可选调用(例如 Taro.JDMTA.isTrafficMapEnable?.()),先取里面的 callee
51
+ if (t.isOptionalCallExpression(target)) {
52
+ target = target.callee
53
+ }
54
+
55
+ // 去掉 TS 断言 / 非空等包裹
56
+ target = stripTSCast(target)
57
+
58
+ if (!t.isMemberExpression(target) && !t.isOptionalMemberExpression(target)) {
59
+ return null
60
+ }
61
+
62
+ // target.object 通常是 Taro.xx 这一层,
63
+ // 也可能是形如 (_tmp = Taro.xx) 这样的赋值表达式,需要再解一层。
64
+ let inner: any = stripTSCast(target.object)
65
+ // 兼容 222 这类形态:(_tmp = Taro.JDMTA).isTrafficMapEnable?.()
66
+ if (t.isAssignmentExpression(inner)) {
67
+ inner = stripTSCast(inner.right)
68
+ }
69
+
70
+ if (!t.isMemberExpression(inner) && !t.isOptionalMemberExpression(inner)) {
71
+ return null
72
+ }
73
+
74
+ if (!t.isIdentifier(inner.object, { name: taroName })) {
75
+ return null
76
+ }
77
+
78
+ let namespaceName: string | null = null
79
+ if (t.isIdentifier(inner.property)) {
80
+ namespaceName = inner.property.name
81
+ } else if (t.isStringLiteral(inner.property)) {
82
+ namespaceName = inner.property.value
83
+ }
84
+
85
+ let methodName: string | null = null
86
+ if (t.isIdentifier(target.property)) {
87
+ methodName = target.property.name
88
+ } else if (t.isStringLiteral(target.property)) {
89
+ methodName = target.property.value
90
+ }
91
+
92
+ if (!namespaceName || !methodName) return null
93
+
94
+ if (process.env.JDAPI_DEBUG_TAROAPI === 'true') {
95
+ const filename = file?.opts?.filename || ''
96
+ // eslint-disable-next-line no-console
97
+ console.log(
98
+ '[jdapi-core-taroapi] getTaroNamespaceCall hit:',
99
+ `${namespaceName}_${methodName}`,
100
+ 'file =',
101
+ filename
102
+ )
103
+ }
104
+
105
+ return { namespaceName, methodName }
106
+ }
107
+
42
108
  // 这些变量需要在每个 program 里重置
43
109
  const invokedApis: Map<string, string> = new Map()
44
110
  let taroName: string
@@ -188,33 +254,34 @@ const plugin = function (babel: typeof BabelCore): BabelCore.PluginObj<IState> {
188
254
  },
189
255
  'CallExpression|OptionalCallExpression' (ast: BabelCore.NodePath<any>) {
190
256
  const callee = ast.node.callee
191
-
192
257
  // 对存在命名空间的 API 支持 tree-shaking:Taro.xx.yy -> xx_yy
193
258
  // 同时兼容:可选链调用(Taro?.JDMTA.pv() / Taro.JDMTA?.pv())、TS 类型断言(as any / ! / satisfies)
194
- if (t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee)) {
195
- const rawObject = stripTSCast(callee.object)
196
- if (t.isMemberExpression(rawObject) || t.isOptionalMemberExpression(rawObject)) {
197
- const inner = rawObject
198
- const isTaroNamespace = t.isIdentifier(inner.object, { name: taroName })
199
- if (isTaroNamespace) {
200
- const namespaceName = t.isIdentifier(inner.property) ? inner.property.name : (t.isStringLiteral(inner.property) ? inner.property.value : null)
201
- const methodName = t.isIdentifier(callee.property) ? callee.property.name : (t.isStringLiteral(callee.property) ? callee.property.value : null)
202
- if (namespaceName && methodName) {
203
- const flatName = `${namespaceName}_${methodName}`
204
- if (this.apis.has(flatName)) {
205
- let identifier: BabelCore.types.Identifier
206
- if (invokedApis.has(flatName)) {
207
- identifier = t.identifier(invokedApis.get(flatName)!)
208
- } else {
209
- const newName = ast.scope.generateUid(flatName)
210
- invokedApis.set(flatName, newName)
211
- identifier = t.identifier(newName)
212
- }
213
- ast.node.callee = identifier as any
214
- return
215
- }
216
- }
259
+ const nsInfo = getTaroNamespaceCall(t, callee as any, taroName, this.file as any)
260
+ if (nsInfo) {
261
+ const { namespaceName, methodName } = nsInfo
262
+ const flatName = `${namespaceName}_${methodName}`
263
+ if (this.apis.has(flatName)) {
264
+ let identifier: BabelCore.types.Identifier
265
+ if (invokedApis.has(flatName)) {
266
+ identifier = t.identifier(invokedApis.get(flatName)!)
267
+ } else {
268
+ const newName = ast.scope.generateUid(flatName)
269
+ invokedApis.set(flatName, newName)
270
+ identifier = t.identifier(newName)
271
+ }
272
+ // 如果当前是可选调用(OptionalCallExpression),直接将整条调用改写为普通函数调用,
273
+ // 避免后续 preset-env 再对可选链做降级,导致重新依赖 Taro.JDMTA。
274
+ if (t.isOptionalCallExpression(ast.node)) {
275
+ ast.replaceWith(
276
+ t.callExpression(
277
+ identifier,
278
+ ast.node.arguments as any
279
+ )
280
+ )
281
+ } else {
282
+ ast.node.callee = identifier as any
217
283
  }
284
+ return
218
285
  }
219
286
  }
220
287
 
@@ -260,6 +327,57 @@ const plugin = function (babel: typeof BabelCore): BabelCore.PluginObj<IState> {
260
327
  taroName = ast.scope.getBinding(this.bindingName)
261
328
  ? ast.scope.generateUid(this.bindingName)
262
329
  : this.bindingName
330
+
331
+ // 预扫描:在正式 visitor 遍历之前,先找到 import 确定 taroName,
332
+ // 然后把所有 namespace API 的 OptionalCallExpression 降级为普通 CallExpression。
333
+ // 这样可以抢在 @babel/plugin-transform-optional-chaining 展开可选链之前完成替换。
334
+ const pkgName = this.packageName
335
+ let preScanTaroName = ''
336
+ ast.traverse({
337
+ ImportDeclaration (importPath: BabelCore.NodePath<BabelCore.types.ImportDeclaration>) {
338
+ if (importPath.node.source.value !== pkgName) return
339
+ for (const spec of importPath.node.specifiers) {
340
+ if (t.isImportDefaultSpecifier(spec)) {
341
+ preScanTaroName = spec.local.name
342
+ break
343
+ }
344
+ }
345
+ importPath.stop()
346
+ }
347
+ })
348
+
349
+ if (preScanTaroName) {
350
+ const apis = this.apis
351
+ const file = this.file as any
352
+ ast.traverse({
353
+ OptionalCallExpression (optPath: BabelCore.NodePath<BabelCore.types.OptionalCallExpression>) {
354
+ const nsInfo = getTaroNamespaceCall(t, optPath.node.callee as any, preScanTaroName, file)
355
+ if (nsInfo) {
356
+ const flatName = `${nsInfo.namespaceName}_${nsInfo.methodName}`
357
+ if (apis.has(flatName)) {
358
+ if (process.env.JDAPI_DEBUG_TAROAPI === 'true') {
359
+ const filename = file?.opts?.filename || ''
360
+ // eslint-disable-next-line no-console
361
+ console.log(
362
+ '[jdapi-core-taroapi] pre-transform: strip optional call for',
363
+ flatName,
364
+ 'in file:',
365
+ filename,
366
+ 'code =',
367
+ optPath.toString()
368
+ )
369
+ }
370
+ optPath.replaceWith(
371
+ t.callExpression(
372
+ optPath.node.callee as any,
373
+ optPath.node.arguments as any
374
+ )
375
+ )
376
+ }
377
+ }
378
+ }
379
+ })
380
+ }
263
381
  },
264
382
  exit (ast) {
265
383
  const that = this