react-native-boost 0.5.3 → 0.5.5

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.
@@ -1,8 +1,8 @@
1
1
  import { declare } from '@babel/helper-plugin-utils';
2
2
  import { types } from '@babel/core';
3
- import { minimatch } from 'minimatch';
4
- import path from 'node:path';
5
3
  import { addDefault, addNamed } from '@babel/helper-module-imports';
4
+ import { minimatch } from 'minimatch';
5
+ import nodePath from 'node:path';
6
6
 
7
7
  class PluginError extends Error {
8
8
  constructor(message) {
@@ -11,15 +11,23 @@ class PluginError extends Error {
11
11
  }
12
12
  }
13
13
 
14
- const ensureArray = (value) => {
15
- if (Array.isArray(value)) return value;
16
- return [value];
17
- };
14
+ const RUNTIME_MODULE_NAME = "react-native-boost";
15
+ const ACCESSIBILITY_PROPERTIES = /* @__PURE__ */ new Set([
16
+ "accessibilityLabel",
17
+ "aria-label",
18
+ "accessibilityState",
19
+ "aria-busy",
20
+ "aria-checked",
21
+ "aria-disabled",
22
+ "aria-expanded",
23
+ "aria-selected",
24
+ "accessible"
25
+ ]);
18
26
 
19
27
  function addFileImportHint({
20
28
  file,
21
29
  nameHint,
22
- path: path2,
30
+ path,
23
31
  importName,
24
32
  moduleName,
25
33
  importType = "named"
@@ -27,12 +35,35 @@ function addFileImportHint({
27
35
  var _a;
28
36
  if (!((_a = file.__hasImports) == null ? void 0 : _a[nameHint])) {
29
37
  file.__hasImports = file.__hasImports || {};
30
- file.__hasImports[nameHint] = importType === "default" ? addDefault(path2, moduleName, { nameHint }) : addNamed(path2, importName, moduleName, { nameHint });
38
+ file.__hasImports[nameHint] = importType === "default" ? addDefault(path, moduleName, { nameHint }) : addNamed(path, importName, moduleName, { nameHint });
31
39
  }
32
40
  return file.__hasImports[nameHint];
33
41
  }
34
- const isIgnoredFile = (p, ignores) => {
35
- const hub = p.hub;
42
+ const replaceWithNativeComponent = (path, parent, file, nativeComponentName) => {
43
+ const nativeIdentifier = addFileImportHint({
44
+ file,
45
+ nameHint: nativeComponentName,
46
+ path,
47
+ importName: nativeComponentName,
48
+ moduleName: RUNTIME_MODULE_NAME,
49
+ importType: "named"
50
+ });
51
+ const currentName = path.node.name.name;
52
+ const jsxName = path.node.name;
53
+ jsxName.name = nativeIdentifier.name;
54
+ if (!path.node.selfClosing && parent.closingElement && types.isJSXIdentifier(parent.closingElement.name) && parent.closingElement.name.name === currentName) {
55
+ parent.closingElement.name.name = nativeIdentifier.name;
56
+ }
57
+ return nativeIdentifier;
58
+ };
59
+
60
+ const ensureArray = (value) => {
61
+ if (Array.isArray(value)) return value;
62
+ return [value];
63
+ };
64
+
65
+ const isIgnoredFile = (path, ignores) => {
66
+ const hub = path.hub;
36
67
  const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
37
68
  if (!file) {
38
69
  throw new PluginError("No file found in Babel hub");
@@ -40,19 +71,19 @@ const isIgnoredFile = (p, ignores) => {
40
71
  const fileName = file.opts.filename;
41
72
  const baseDirectory = "cwd" in file.opts ? file.opts.cwd : process.cwd();
42
73
  for (const pattern of ignores) {
43
- const absolutePattern = path.isAbsolute(pattern) ? pattern : path.join(baseDirectory, pattern);
74
+ const absolutePattern = nodePath.isAbsolute(pattern) ? pattern : nodePath.join(baseDirectory, pattern);
44
75
  if (minimatch(fileName, absolutePattern, { dot: true })) {
45
76
  return true;
46
77
  }
47
78
  }
48
79
  return false;
49
80
  };
50
- const shouldIgnoreOptimization = (path2) => {
81
+ const isIgnoredLine = (path) => {
51
82
  var _a, _b, _c;
52
- if ((_a = path2.node.leadingComments) == null ? void 0 : _a.some((comment) => comment.value.includes("@boost-ignore"))) {
83
+ if ((_a = path.node.leadingComments) == null ? void 0 : _a.some((comment) => comment.value.includes("@boost-ignore"))) {
53
84
  return true;
54
85
  }
55
- const jsxElementPath = path2.parentPath;
86
+ const jsxElementPath = path.parentPath;
56
87
  if ((_b = jsxElementPath.node.leadingComments) == null ? void 0 : _b.some((comment) => comment.value.includes("@boost-ignore"))) {
57
88
  return true;
58
89
  }
@@ -90,11 +121,51 @@ const shouldIgnoreOptimization = (path2) => {
90
121
  }
91
122
  return false;
92
123
  };
93
- const hasBlacklistedProperty = (path2, blacklist) => {
94
- return path2.node.attributes.some((attribute) => {
124
+ const isValidJSXComponent = (path, componentName) => {
125
+ if (!types.isJSXIdentifier(path.node.name)) return false;
126
+ const parent = path.parent;
127
+ if (!types.isJSXElement(parent)) return false;
128
+ const componentIdentifier = path.node.name.name;
129
+ const binding = path.scope.getBinding(componentIdentifier);
130
+ if (!binding) return false;
131
+ if (binding.kind === "module" && types.isImportDeclaration(binding.path.parent) && types.isImportSpecifier(binding.path.node)) {
132
+ const imported = binding.path.node.imported;
133
+ if (types.isIdentifier(imported)) {
134
+ return imported.name === componentName;
135
+ }
136
+ }
137
+ return path.node.name.name === componentName;
138
+ };
139
+ const isReactNativeImport = (path, expectedImportedName) => {
140
+ if (!types.isJSXIdentifier(path.node.name)) return false;
141
+ const localName = path.node.name.name;
142
+ const binding = path.scope.getBinding(localName);
143
+ if (!binding) return false;
144
+ if (binding.kind === "module") {
145
+ const importDeclaration = binding.path.parent;
146
+ if (!types.isImportDeclaration(importDeclaration)) return false;
147
+ if (importDeclaration.source.value !== "react-native") return false;
148
+ if (types.isImportSpecifier(binding.path.node)) {
149
+ const imported = binding.path.node.imported;
150
+ if (types.isIdentifier(imported)) {
151
+ return imported.name === expectedImportedName;
152
+ }
153
+ }
154
+ if (types.isImportDefaultSpecifier(binding.path.node)) {
155
+ return true;
156
+ }
157
+ }
158
+ return false;
159
+ };
160
+
161
+ const hasBlacklistedProperty = (path, blacklist) => {
162
+ return path.node.attributes.some((attribute) => {
163
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && blacklist.has(attribute.name.name)) {
164
+ return true;
165
+ }
95
166
  if (types.isJSXSpreadAttribute(attribute)) {
96
167
  if (types.isIdentifier(attribute.argument)) {
97
- const binding = path2.scope.getBinding(attribute.argument.name);
168
+ const binding = path.scope.getBinding(attribute.argument.name);
98
169
  let objectExpression;
99
170
  if (binding) {
100
171
  if (types.isVariableDeclarator(binding.path.node)) {
@@ -147,38 +218,27 @@ const buildPropertiesFromAttributes = (attributes) => {
147
218
  ...arguments_
148
219
  ]);
149
220
  };
150
- const accessibilityProperties = /* @__PURE__ */ new Set([
151
- "accessibilityLabel",
152
- "aria-label",
153
- "accessibilityState",
154
- "aria-busy",
155
- "aria-checked",
156
- "aria-disabled",
157
- "aria-expanded",
158
- "aria-selected",
159
- "accessible"
160
- ]);
161
- const hasAccessibilityProperty = (path2, attributes) => {
221
+ const hasAccessibilityProperty = (path, attributes) => {
162
222
  for (const attribute of attributes) {
163
223
  if (types.isJSXAttribute(attribute)) {
164
224
  const key = attribute.name.name;
165
- if (typeof key === "string" && accessibilityProperties.has(key)) {
225
+ if (typeof key === "string" && ACCESSIBILITY_PROPERTIES.has(key)) {
166
226
  return true;
167
227
  }
168
228
  } else if (types.isJSXSpreadAttribute(attribute)) {
169
229
  if (types.isObjectExpression(attribute.argument)) {
170
230
  for (const property of attribute.argument.properties) {
171
- if (types.isObjectProperty(property) && types.isIdentifier(property.key) && accessibilityProperties.has(property.key.name)) {
231
+ if (types.isObjectProperty(property) && types.isIdentifier(property.key) && ACCESSIBILITY_PROPERTIES.has(property.key.name)) {
172
232
  return true;
173
233
  }
174
234
  }
175
235
  } else if (types.isIdentifier(attribute.argument)) {
176
- const binding = path2.scope.getBinding(attribute.argument.name);
236
+ const binding = path.scope.getBinding(attribute.argument.name);
177
237
  if (binding && types.isVariableDeclarator(binding.path.node)) {
178
238
  const declarator = binding.path.node;
179
239
  if (declarator.init && types.isObjectExpression(declarator.init)) {
180
240
  for (const property of declarator.init.properties) {
181
- if (types.isObjectProperty(property) && types.isIdentifier(property.key) && accessibilityProperties.has(property.key.name)) {
241
+ if (types.isObjectProperty(property) && types.isIdentifier(property.key) && ACCESSIBILITY_PROPERTIES.has(property.key.name)) {
182
242
  return true;
183
243
  }
184
244
  }
@@ -194,6 +254,22 @@ const hasAccessibilityProperty = (path2, attributes) => {
194
254
  return false;
195
255
  };
196
256
 
257
+ const isStringNode = (path, child) => {
258
+ if (types.isJSXText(child) || types.isStringLiteral(child)) return true;
259
+ if (types.isJSXExpressionContainer(child)) {
260
+ const expression = child.expression;
261
+ if (types.isIdentifier(expression)) {
262
+ const binding = path.scope.getBinding(expression.name);
263
+ if (binding && binding.path.node && types.isVariableDeclarator(binding.path.node)) {
264
+ return !!binding.path.node.init && types.isStringLiteral(binding.path.node.init);
265
+ }
266
+ return false;
267
+ }
268
+ if (types.isStringLiteral(expression)) return true;
269
+ }
270
+ return false;
271
+ };
272
+
197
273
  const textBlacklistedProperties = /* @__PURE__ */ new Set([
198
274
  "allowFontScaling",
199
275
  "ellipsizeMode",
@@ -217,25 +293,12 @@ const textBlacklistedProperties = /* @__PURE__ */ new Set([
217
293
  const textOptimizer = (path, log = () => {
218
294
  }) => {
219
295
  var _a, _b, _c;
220
- if (!types.isJSXIdentifier(path.node.name)) return;
221
- const parent = path.parent;
222
- if (!types.isJSXElement(parent)) return;
223
- const elementName = path.node.name.name;
224
- if (elementName !== "Text") return;
225
- if (shouldIgnoreOptimization(path)) {
226
- return;
227
- }
228
- const binding = path.scope.getBinding(elementName);
229
- if (!binding) return;
230
- if (binding.kind === "module") {
231
- const parentNode = binding.path.parent;
232
- if (!types.isImportDeclaration(parentNode) || parentNode.source.value !== "react-native") {
233
- return;
234
- }
235
- }
296
+ if (isIgnoredLine(path)) return;
297
+ if (!isValidJSXComponent(path, "Text")) return;
298
+ if (!isReactNativeImport(path, "Text")) return;
236
299
  if (hasBlacklistedProperty(path, textBlacklistedProperties)) return;
237
- if (hasInvalidChildren(path)) return;
238
- if (!hasOnlyStringChildren(path, parent)) return;
300
+ const parent = path.parent;
301
+ if (hasInvalidChildren(path, parent)) return;
239
302
  const hub = path.hub;
240
303
  const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
241
304
  if (!file) {
@@ -244,32 +307,69 @@ const textOptimizer = (path, log = () => {
244
307
  const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
245
308
  const lineNumber = (_c = (_b = path.node.loc) == null ? void 0 : _b.start.line) != null ? _c : "unknown line";
246
309
  log(`Optimizing Text component in ${filename}:${lineNumber}`);
247
- fixNegativeNumberOfLines({ path, log });
248
310
  const originalAttributes = [...path.node.attributes];
249
- let styleAttribute, styleExpr;
250
- for (const attribute of originalAttributes) {
251
- if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "style" })) {
252
- styleAttribute = attribute;
253
- break;
311
+ fixNegativeNumberOfLines({ path, log });
312
+ processProps(path, file, originalAttributes);
313
+ replaceWithNativeComponent(path, parent, file, "NativeText");
314
+ };
315
+ function hasInvalidChildren(path, parent) {
316
+ for (const attribute of path.node.attributes) {
317
+ if (types.isJSXSpreadAttribute(attribute)) continue;
318
+ if (types.isJSXIdentifier(attribute.name) && attribute.value && // For a "children" attribute, optimization is allowed only if it is a string
319
+ attribute.name.name === "children" && !isStringNode(path, attribute.value)) {
320
+ return true;
254
321
  }
255
322
  }
256
- if (styleAttribute && styleAttribute.value && types.isJSXExpressionContainer(styleAttribute.value) && !types.isJSXEmptyExpression(styleAttribute.value.expression)) {
257
- styleExpr = styleAttribute.value.expression;
323
+ return !parent.children.every((child) => isStringNode(path, child));
324
+ }
325
+ function fixNegativeNumberOfLines({
326
+ path,
327
+ log
328
+ }) {
329
+ for (const attribute of path.node.attributes) {
330
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "numberOfLines" }) && attribute.value && types.isJSXExpressionContainer(attribute.value)) {
331
+ let originalValue;
332
+ if (types.isNumericLiteral(attribute.value.expression)) {
333
+ originalValue = attribute.value.expression.value;
334
+ } else if (types.isUnaryExpression(attribute.value.expression) && attribute.value.expression.operator === "-" && types.isNumericLiteral(attribute.value.expression.argument)) {
335
+ originalValue = -attribute.value.expression.argument.value;
336
+ }
337
+ if (originalValue !== void 0 && originalValue < 0) {
338
+ log(
339
+ `Warning: 'numberOfLines' in <Text> must be a non-negative number, received: ${originalValue}. The value will be set to 0.`
340
+ );
341
+ attribute.value.expression = types.numericLiteral(0);
342
+ }
343
+ }
344
+ }
345
+ }
346
+ function extractStyleAttribute(attributes) {
347
+ for (const attribute of attributes) {
348
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "style" })) {
349
+ if (attribute.value && types.isJSXExpressionContainer(attribute.value) && !types.isJSXEmptyExpression(attribute.value.expression)) {
350
+ return {
351
+ styleAttribute: attribute,
352
+ styleExpr: attribute.value.expression
353
+ };
354
+ }
355
+ return { styleAttribute: attribute };
356
+ }
258
357
  }
358
+ return {};
359
+ }
360
+ function processProps(path, file, originalAttributes) {
361
+ const { styleExpr } = extractStyleAttribute(originalAttributes);
259
362
  const hasA11y = hasAccessibilityProperty(path, originalAttributes);
260
363
  if (styleExpr && hasA11y) {
261
- const accessibilityAttributes = originalAttributes.filter((attribute) => {
262
- if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "style" })) {
263
- return false;
264
- }
265
- return true;
266
- });
364
+ const accessibilityAttributes = originalAttributes.filter(
365
+ (attribute) => !(types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "style" }))
366
+ );
267
367
  const normalizeIdentifier = addFileImportHint({
268
368
  file,
269
369
  nameHint: "normalizeAccessibilityProps",
270
370
  path,
271
371
  importName: "normalizeAccessibilityProps",
272
- moduleName: "react-native-boost"
372
+ moduleName: RUNTIME_MODULE_NAME
273
373
  });
274
374
  const accessibilityObject = buildPropertiesFromAttributes(accessibilityAttributes);
275
375
  const accessibilityExpr = types.callExpression(types.identifier(normalizeIdentifier.name), [accessibilityObject]);
@@ -278,7 +378,7 @@ const textOptimizer = (path, log = () => {
278
378
  nameHint: "flattenTextStyle",
279
379
  path,
280
380
  importName: "flattenTextStyle",
281
- moduleName: "react-native-boost"
381
+ moduleName: RUNTIME_MODULE_NAME
282
382
  });
283
383
  const flattenedStyleExpr = types.callExpression(types.identifier(flattenIdentifier.name), [styleExpr]);
284
384
  path.node.attributes = [types.jsxSpreadAttribute(accessibilityExpr), types.jsxSpreadAttribute(flattenedStyleExpr)];
@@ -288,7 +388,7 @@ const textOptimizer = (path, log = () => {
288
388
  nameHint: "flattenTextStyle",
289
389
  path,
290
390
  importName: "flattenTextStyle",
291
- moduleName: "react-native-boost"
391
+ moduleName: RUNTIME_MODULE_NAME
292
392
  });
293
393
  const flattened = types.callExpression(types.identifier(flattenIdentifier.name), [styleExpr]);
294
394
  path.node.attributes = [types.jsxSpreadAttribute(flattened)];
@@ -298,77 +398,12 @@ const textOptimizer = (path, log = () => {
298
398
  nameHint: "normalizeAccessibilityProps",
299
399
  path,
300
400
  importName: "normalizeAccessibilityProps",
301
- moduleName: "react-native-boost"
401
+ moduleName: RUNTIME_MODULE_NAME
302
402
  });
303
403
  const propsObject = buildPropertiesFromAttributes(originalAttributes);
304
404
  const normalized = types.callExpression(types.identifier(normalizeIdentifier.name), [propsObject]);
305
405
  path.node.attributes = [types.jsxSpreadAttribute(normalized)];
306
406
  }
307
- const nativeTextIdentifier = addFileImportHint({
308
- file,
309
- nameHint: "NativeText",
310
- path,
311
- importName: "NativeText",
312
- moduleName: "react-native-boost"
313
- });
314
- path.node.name.name = nativeTextIdentifier.name;
315
- if (!path.node.selfClosing && parent.closingElement && types.isJSXIdentifier(parent.closingElement.name) && parent.closingElement.name.name === "Text") {
316
- parent.closingElement.name.name = nativeTextIdentifier.name;
317
- }
318
- };
319
- function hasOnlyStringChildren(path, node) {
320
- return node.children.every((child) => isStringNode(path, child));
321
- }
322
- function isStringNode(path, child) {
323
- if (types.isJSXText(child) || types.isStringLiteral(child)) return true;
324
- if (types.isJSXExpressionContainer(child)) {
325
- const expression = child.expression;
326
- if (types.isIdentifier(expression)) {
327
- const binding = path.scope.getBinding(expression.name);
328
- if (binding && binding.path.node && types.isVariableDeclarator(binding.path.node)) {
329
- return !!binding.path.node.init && types.isStringLiteral(binding.path.node.init);
330
- }
331
- return false;
332
- }
333
- if (types.isStringLiteral(expression)) return true;
334
- }
335
- return false;
336
- }
337
- function fixNegativeNumberOfLines({
338
- path,
339
- log
340
- }) {
341
- for (const attribute of path.node.attributes) {
342
- if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "numberOfLines" }) && attribute.value && types.isJSXExpressionContainer(attribute.value)) {
343
- let originalValue;
344
- if (types.isNumericLiteral(attribute.value.expression)) {
345
- originalValue = attribute.value.expression.value;
346
- } else if (types.isUnaryExpression(attribute.value.expression) && attribute.value.expression.operator === "-" && types.isNumericLiteral(attribute.value.expression.argument)) {
347
- originalValue = -attribute.value.expression.argument.value;
348
- }
349
- if (originalValue !== void 0 && originalValue < 0) {
350
- log(
351
- `Warning: 'numberOfLines' in <Text> must be a non-negative number, received: ${originalValue}. The value will be set to 0.`
352
- );
353
- attribute.value.expression = types.numericLiteral(0);
354
- }
355
- }
356
- }
357
- }
358
- function hasInvalidChildren(path) {
359
- for (const attribute of path.node.attributes) {
360
- if (types.isJSXSpreadAttribute(attribute)) continue;
361
- if (types.isJSXIdentifier(attribute.name) && attribute.value) {
362
- if (attribute.name.name === "children") {
363
- if (!isStringNode(path, attribute.value)) {
364
- return true;
365
- }
366
- } else if (textBlacklistedProperties.has(attribute.name.name)) {
367
- return true;
368
- }
369
- }
370
- }
371
- return false;
372
407
  }
373
408
 
374
409
  const log = (message) => {
@@ -410,20 +445,9 @@ const viewBlacklistedProperties = /* @__PURE__ */ new Set([
410
445
  const viewOptimizer = (path, log = () => {
411
446
  }) => {
412
447
  var _a, _b, _c;
413
- if (!types.isJSXIdentifier(path.node.name)) return;
414
- const parent = path.parent;
415
- if (!types.isJSXElement(parent)) return;
416
- const elementName = path.node.name.name;
417
- if (elementName !== "View") return;
418
- if (shouldIgnoreOptimization(path)) return;
419
- const binding = path.scope.getBinding(elementName);
420
- if (!binding) return;
421
- if (binding.kind === "module") {
422
- const parentNode = binding.path.parent;
423
- if (!types.isImportDeclaration(parentNode) || parentNode.source.value !== "react-native") {
424
- return;
425
- }
426
- }
448
+ if (isIgnoredLine(path)) return;
449
+ if (!isValidJSXComponent(path, "View")) return;
450
+ if (!isReactNativeImport(path, "View")) return;
427
451
  if (hasBlacklistedProperty(path, viewBlacklistedProperties)) return;
428
452
  if (hasTextAncestor(path)) return;
429
453
  const hub = path.hub;
@@ -434,23 +458,55 @@ const viewOptimizer = (path, log = () => {
434
458
  const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
435
459
  const lineNumber = (_c = (_b = path.node.loc) == null ? void 0 : _b.start.line) != null ? _c : "unknown line";
436
460
  log(`Optimizing View component in ${filename}:${lineNumber}`);
437
- const viewNativeIdentifier = addFileImportHint({
438
- file,
439
- path,
440
- importName: "NativeView",
441
- moduleName: "react-native-boost",
442
- importType: "named",
443
- nameHint: "NativeView"
444
- });
445
- path.node.name.name = viewNativeIdentifier.name;
446
- if (!path.node.selfClosing && parent.closingElement && types.isJSXIdentifier(parent.closingElement.name) && parent.closingElement.name.name === "View") {
447
- parent.closingElement.name.name = viewNativeIdentifier.name;
448
- }
461
+ const parent = path.parent;
462
+ replaceWithNativeComponent(path, parent, file, "NativeView");
449
463
  };
450
464
  function hasTextAncestor(path) {
451
- return !!path.findParent((parentPath) => {
465
+ const directTextAncestor = path.findParent((parentPath) => {
452
466
  return types.isJSXElement(parentPath.node) && types.isJSXIdentifier(parentPath.node.openingElement.name, { name: "Text" });
453
467
  });
468
+ if (directTextAncestor) return true;
469
+ return !!path.findParent((parentPath) => {
470
+ if (!types.isJSXElement(parentPath.node)) return false;
471
+ const openingElement = parentPath.node.openingElement;
472
+ if (!types.isJSXIdentifier(openingElement.name)) return false;
473
+ const componentName = openingElement.name.name;
474
+ if (componentName === "Text" || componentName === "View" || componentName === "Fragment" || componentName[0] === componentName[0].toLowerCase()) {
475
+ return false;
476
+ }
477
+ const binding = parentPath.scope.getBinding(componentName);
478
+ if (!binding) return false;
479
+ if (types.isVariableDeclarator(binding.path.node)) {
480
+ const init = binding.path.node.init;
481
+ if (types.isArrowFunctionExpression(init) || types.isFunctionExpression(init)) {
482
+ return types.isBlockStatement(init.body) ? hasTextInReturnStatement(init.body) : hasTextInExpression(init.body);
483
+ }
484
+ } else if (types.isFunctionDeclaration(binding.path.node)) {
485
+ return hasTextInReturnStatement(binding.path.node.body);
486
+ }
487
+ return false;
488
+ });
489
+ }
490
+ function hasTextInReturnStatement(blockStatement) {
491
+ for (const statement of blockStatement.body) {
492
+ if (types.isReturnStatement(statement) && statement.argument && hasTextInExpression(statement.argument)) {
493
+ return true;
494
+ }
495
+ }
496
+ return false;
497
+ }
498
+ function hasTextInExpression(expression) {
499
+ if (types.isJSXElement(expression)) {
500
+ if (types.isJSXIdentifier(expression.openingElement.name, { name: "Text" })) {
501
+ return true;
502
+ }
503
+ for (const child of expression.children) {
504
+ if (types.isJSXElement(child) && types.isJSXIdentifier(child.openingElement.name, { name: "Text" })) {
505
+ return true;
506
+ }
507
+ }
508
+ }
509
+ return false;
454
510
  }
455
511
 
456
512
  var index = declare((api) => {