react-native-boost 0.2.0 โ†’ 0.4.0

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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  A powerful Babel plugin that automatically optimizes React Native apps through source code analysis and optimization. It identifies safe micro-optimization opportunities, which can lead to significant performance improvements.
6
6
 
7
7
  > [!WARNING]
8
- > This project is highly experimental and under active development. **Your app might break** and the optimization strategies used can change significantly between versions. Use with caution!
8
+ > The library and its Babel plugin are still experimental. You should expect things to break. Please report any issues you encounter in the issues tab with reproducible examples, and use the library's ignore mechanisms to disable optimizations for problematic files or lines of code.
9
9
 
10
10
  - โšก Automatic performance optimization through source code analysis
11
11
  - ๐Ÿ”’ Safe optimizations that don't break your app
@@ -14,20 +14,22 @@ A powerful Babel plugin that automatically optimizes React Native apps through s
14
14
  - ๐Ÿงช Works seamlessly with Expo
15
15
  - ๐ŸŽจ Configurable optimization strategies
16
16
 
17
+ ## Documentation
18
+
19
+ The documentation is available at [react-native-boost.oss.kuatsu.de](https://react-native-boost.oss.kuatsu.de).
20
+
17
21
  ## Benchmark
18
22
 
19
23
  The example app in the `apps/example` directory is a benchmark for the performance of the plugin.
20
24
 
21
25
  <div align="center">
22
- <img src="./packages/react-native-boost/img/benchmark.png" width="500" />
26
+ <img src="https://react-native-boost.oss.kuatsu.de/assets/images/benchmark-d60fdc78daeb03798d41c220bfed1f64.png" width="500" />
23
27
  <p>
24
28
  <b>1,000 Text components</b>: Render time of 1,000 Text components with and without React Native Boost.<br/>
25
- Measured in milliseconds on an iPhone 16 Pro in development mode, lower is better.
29
+ Measured in milliseconds on an iPhone 16 Pro in development mode and using New Architecture, lower is better.
26
30
  </p>
27
31
  </div>
28
32
 
29
- The data is averaged over 10 runs on an iPhone 16 Pro Simulator running the app in development mode.
30
-
31
33
  ## Installation
32
34
 
33
35
  Install the package using your favorite package manager. Please **do not** install the package as a dev dependency. While the Babel plugin itself would work as a dev dependency, some optimizations import minimal helpers into your code, which requires the package to be installed as a regular dependency.
@@ -46,63 +48,11 @@ module.exports = {
46
48
  };
47
49
  ```
48
50
 
49
- That's it! No imports in your code, pod installing, or anything else is required.
50
-
51
- Optionally, you can configure the plugin to log optimized files to the console and disable specific optimizations:
52
-
53
- ```js
54
- module.exports = {
55
- plugins: [
56
- [
57
- 'react-native-boost/plugin',
58
- {
59
- verbose: true,
60
- optimizations: {
61
- text: false,
62
- },
63
- },
64
- ],
65
- ],
66
- };
67
- ```
68
-
69
- You can also skip optimization for a specific comment using a decorator comment above the component's opening tag:
70
-
71
- ```jsx
72
- // @boost-ignore
73
- <Text>This will not be optimized.</Text>
74
- ```
51
+ That's it! No imports in your code, pod installing, or anything else is required. Optionally, you can configure the plugin with a few options. See the [documentation](https://react-native-boost.oss.kuatsu.de/docs/babel-plugin/configure) for more information.
75
52
 
76
53
  ## How It Works
77
54
 
78
- Several standard components in React Native are actually wrappers around their native counterparts. These wrappers often only handle edge cases and aren't needed in most cases. However, they add additional runtime overhead and depth to the component tree, which can lead to performance bottlenecks.
79
-
80
- React Native Boost replaces these wrapper components directly with their respective native components, flattening the component tree. It intelligently analyzes your code and only optimizes components that are used in a way where they can be optimized without breaking the app.
81
-
82
- Here's an example of how it works:
83
-
84
- ```jsx
85
- // Your original code ๐ŸŒ
86
- import React from 'react';
87
- import { View, Text } from 'react-native';
88
-
89
- const MyComponent = () => (
90
- <View>
91
- <Text>Hello, World!</Text>
92
- </View>
93
- );
94
-
95
- // Automagically transformed to โœจ
96
- import React from 'react';
97
- import { View } from 'react-native';
98
- import { NativeText } from 'react-native/Libraries/Text/TextNativeComponent';
99
-
100
- const MyComponent = () => (
101
- <View>
102
- <NativeText>Hello, World!</NativeText>
103
- </View>
104
- );
105
- ```
55
+ A technical rundown of how the plugin works can be found in the [docs](https://react-native-boost.oss.kuatsu.de/docs/introduction/how-it-works).
106
56
 
107
57
  ## Contributing
108
58
 
@@ -1,6 +1,8 @@
1
1
  import { declare } from '@babel/helper-plugin-utils';
2
2
  import { types } from '@babel/core';
3
- import { addNamed } from '@babel/helper-module-imports';
3
+ import { addNamed, addDefault } from '@babel/helper-module-imports';
4
+ import { minimatch } from 'minimatch';
5
+ import path from 'node:path';
4
6
 
5
7
  class PluginError extends Error {
6
8
  constructor(message) {
@@ -14,12 +16,28 @@ const ensureArray = (value) => {
14
16
  return [value];
15
17
  };
16
18
 
17
- const shouldIgnoreOptimization = (path) => {
19
+ const isIgnoredFile = (p, ignores) => {
20
+ const hub = p.hub;
21
+ const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
22
+ if (!file) {
23
+ throw new PluginError("No file found in Babel hub");
24
+ }
25
+ const fileName = file.opts.filename;
26
+ const baseDirectory = "cwd" in file.opts ? file.opts.cwd : process.cwd();
27
+ for (const pattern of ignores) {
28
+ const absolutePattern = path.isAbsolute(pattern) ? pattern : path.join(baseDirectory, pattern);
29
+ if (minimatch(fileName, absolutePattern, { dot: true })) {
30
+ return true;
31
+ }
32
+ }
33
+ return false;
34
+ };
35
+ const shouldIgnoreOptimization = (path2) => {
18
36
  var _a, _b, _c;
19
- if ((_a = path.node.leadingComments) == null ? void 0 : _a.some((comment) => comment.value.includes("@boost-ignore"))) {
37
+ if ((_a = path2.node.leadingComments) == null ? void 0 : _a.some((comment) => comment.value.includes("@boost-ignore"))) {
20
38
  return true;
21
39
  }
22
- const jsxElementPath = path.parentPath;
40
+ const jsxElementPath = path2.parentPath;
23
41
  if ((_b = jsxElementPath.node.leadingComments) == null ? void 0 : _b.some((comment) => comment.value.includes("@boost-ignore"))) {
24
42
  return true;
25
43
  }
@@ -57,7 +75,63 @@ const shouldIgnoreOptimization = (path) => {
57
75
  }
58
76
  return false;
59
77
  };
78
+ const hasBlacklistedProperty = (path2, blacklist) => {
79
+ return path2.node.attributes.some((attribute) => {
80
+ if (types.isJSXSpreadAttribute(attribute)) {
81
+ if (types.isIdentifier(attribute.argument)) {
82
+ const binding = path2.scope.getBinding(attribute.argument.name);
83
+ let objectExpression;
84
+ if (binding) {
85
+ if (types.isVariableDeclarator(binding.path.node)) {
86
+ objectExpression = binding.path.node.init;
87
+ } else if (types.isObjectExpression(binding.path.node)) {
88
+ objectExpression = binding.path.node;
89
+ }
90
+ }
91
+ if (objectExpression && types.isObjectExpression(objectExpression)) {
92
+ return objectExpression.properties.some((property) => {
93
+ if (types.isObjectProperty(property) && types.isIdentifier(property.key)) {
94
+ return blacklist.has(property.key.name);
95
+ }
96
+ return false;
97
+ });
98
+ }
99
+ }
100
+ return true;
101
+ }
102
+ return false;
103
+ });
104
+ };
60
105
 
106
+ const textBlacklistedProperties = /* @__PURE__ */ new Set([
107
+ "accessible",
108
+ "accessibilityLabel",
109
+ "accessibilityState",
110
+ "allowFontScaling",
111
+ "aria-busy",
112
+ "aria-checked",
113
+ "aria-disabled",
114
+ "aria-expanded",
115
+ "aria-label",
116
+ "aria-selected",
117
+ "ellipsizeMode",
118
+ "id",
119
+ "nativeID",
120
+ "onLongPress",
121
+ "onPress",
122
+ "onPressIn",
123
+ "onPressOut",
124
+ "onResponderGrant",
125
+ "onResponderMove",
126
+ "onResponderRelease",
127
+ "onResponderTerminate",
128
+ "onResponderTerminationRequest",
129
+ "onStartShouldSetResponder",
130
+ "pressRetentionOffset",
131
+ "suppressHighlighting",
132
+ "selectable",
133
+ "selectionColor"
134
+ ]);
61
135
  const textOptimizer = (path, log = () => {
62
136
  }) => {
63
137
  var _a, _b, _c;
@@ -77,7 +151,8 @@ const textOptimizer = (path, log = () => {
77
151
  return;
78
152
  }
79
153
  }
80
- if (hasBlacklistedProperties(path)) return;
154
+ if (hasBlacklistedProperty(path, textBlacklistedProperties)) return;
155
+ if (hasInvalidChildren(path)) return;
81
156
  if (!hasOnlyStringChildren(path, parent)) return;
82
157
  const hub = path.hub;
83
158
  const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
@@ -87,6 +162,7 @@ const textOptimizer = (path, log = () => {
87
162
  const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
88
163
  const lineNumber = (_c = (_b = path.node.loc) == null ? void 0 : _b.start.line) != null ? _c : "unknown line";
89
164
  log(`Optimizing Text component in ${filename}:${lineNumber}`);
165
+ fixNegativeNumberOfLines({ path, log });
90
166
  optimizeStyleTag({ path, file });
91
167
  if (!file.__hasImports) {
92
168
  file.__hasImports = {};
@@ -114,7 +190,64 @@ function isStringNode(path, child) {
114
190
  }
115
191
  return false;
116
192
  }
117
- const blacklistedProperties = /* @__PURE__ */ new Set([
193
+ function fixNegativeNumberOfLines({
194
+ path,
195
+ log
196
+ }) {
197
+ for (const attribute of path.node.attributes) {
198
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "numberOfLines" }) && attribute.value && types.isJSXExpressionContainer(attribute.value)) {
199
+ let originalValue;
200
+ if (types.isNumericLiteral(attribute.value.expression)) {
201
+ originalValue = attribute.value.expression.value;
202
+ } else if (types.isUnaryExpression(attribute.value.expression) && attribute.value.expression.operator === "-" && types.isNumericLiteral(attribute.value.expression.argument)) {
203
+ originalValue = -attribute.value.expression.argument.value;
204
+ }
205
+ if (originalValue !== void 0 && originalValue < 0) {
206
+ log(
207
+ `Warning: 'numberOfLines' in <Text> must be a non-negative number, received: ${originalValue}. The value will be set to 0.`
208
+ );
209
+ attribute.value.expression = types.numericLiteral(0);
210
+ }
211
+ }
212
+ }
213
+ }
214
+ function optimizeStyleTag({ path, file }) {
215
+ var _a;
216
+ let shouldImportFlattenTextStyle = false;
217
+ const nameHint = "_flattenTextStyle";
218
+ for (const [index, attribute] of path.node.attributes.entries()) {
219
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "style" })) {
220
+ shouldImportFlattenTextStyle = true;
221
+ if (types.isJSXExpressionContainer(attribute.value) && !types.isJSXEmptyExpression(attribute.value.expression)) {
222
+ path.node.attributes[index] = types.jsxSpreadAttribute(
223
+ types.callExpression(types.identifier(nameHint), [attribute.value.expression])
224
+ );
225
+ }
226
+ }
227
+ }
228
+ if (shouldImportFlattenTextStyle && !((_a = file.__hasImports) == null ? void 0 : _a.flattenTextStyle)) {
229
+ if (!file.__hasImports) file.__hasImports = {};
230
+ file.__hasImports.flattenTextStyle = addNamed(path, "flattenTextStyle", "react-native-boost", { nameHint });
231
+ }
232
+ }
233
+ function hasInvalidChildren(path) {
234
+ for (const attribute of path.node.attributes) {
235
+ if (types.isJSXSpreadAttribute(attribute)) return false;
236
+ if (types.isJSXIdentifier(attribute.name) && attribute.value) {
237
+ if (attribute.name.name === "children") {
238
+ return isStringNode(path, attribute.value);
239
+ }
240
+ return textBlacklistedProperties.has(attribute.name.name);
241
+ }
242
+ }
243
+ return false;
244
+ }
245
+
246
+ const log = (message) => {
247
+ console.log(`[react-native-boost] ${message}`);
248
+ };
249
+
250
+ const viewBlacklistedProperties = /* @__PURE__ */ new Set([
118
251
  "accessible",
119
252
  "accessibilityLabel",
120
253
  "accessibilityState",
@@ -126,8 +259,10 @@ const blacklistedProperties = /* @__PURE__ */ new Set([
126
259
  "aria-label",
127
260
  "aria-selected",
128
261
  "ellipsizeMode",
262
+ "disabled",
129
263
  "id",
130
264
  "nativeID",
265
+ "numberOfLines",
131
266
  "onLongPress",
132
267
  "onPress",
133
268
  "onPressIn",
@@ -139,76 +274,71 @@ const blacklistedProperties = /* @__PURE__ */ new Set([
139
274
  "onResponderTerminationRequest",
140
275
  "onStartShouldSetResponder",
141
276
  "pressRetentionOffset",
142
- "suppressHighlighting"
277
+ "selectable",
278
+ "selectionColor",
279
+ "suppressHighlighting",
280
+ "style"
143
281
  ]);
144
- function optimizeStyleTag({ path, file }) {
145
- var _a;
146
- let shouldImportFlattenTextStyle = false;
147
- const nameHint = "_flattenTextStyle";
148
- for (const [index, attribute] of path.node.attributes.entries()) {
149
- if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "style" })) {
150
- shouldImportFlattenTextStyle = true;
151
- if (types.isJSXExpressionContainer(attribute.value) && !types.isJSXEmptyExpression(attribute.value.expression)) {
152
- path.node.attributes[index] = types.jsxSpreadAttribute(
153
- types.callExpression(types.identifier(nameHint), [attribute.value.expression])
154
- );
155
- }
282
+ const viewOptimizer = (path, log = () => {
283
+ }) => {
284
+ var _a, _b, _c;
285
+ if (!types.isJSXIdentifier(path.node.name)) return;
286
+ const parent = path.parent;
287
+ if (!types.isJSXElement(parent)) return;
288
+ const elementName = path.node.name.name;
289
+ if (elementName !== "View") return;
290
+ if (shouldIgnoreOptimization(path)) return;
291
+ const binding = path.scope.getBinding(elementName);
292
+ if (!binding) return;
293
+ if (binding.kind === "module") {
294
+ const parentNode = binding.path.parent;
295
+ if (!types.isImportDeclaration(parentNode) || parentNode.source.value !== "react-native") {
296
+ return;
156
297
  }
157
298
  }
158
- if (shouldImportFlattenTextStyle && !((_a = file.__hasImports) == null ? void 0 : _a.flattenTextStyle)) {
159
- if (!file.__hasImports) file.__hasImports = {};
160
- file.__hasImports.flattenTextStyle = addNamed(path, "flattenTextStyle", "react-native-boost", { nameHint });
299
+ if (hasBlacklistedProperty(path, viewBlacklistedProperties)) return;
300
+ if (hasTextAncestor(path)) return;
301
+ const hub = path.hub;
302
+ const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
303
+ if (!file) {
304
+ throw new PluginError("No file found in Babel hub");
161
305
  }
162
- }
163
- function hasBlacklistedProperties(path) {
164
- return path.node.attributes.some((attribute) => {
165
- if (types.isJSXSpreadAttribute(attribute)) {
166
- if (types.isIdentifier(attribute.argument)) {
167
- const binding = path.scope.getBinding(attribute.argument.name);
168
- let objectExpression;
169
- if (binding) {
170
- if (types.isVariableDeclarator(binding.path.node)) {
171
- objectExpression = binding.path.node.init;
172
- } else if (types.isObjectExpression(binding.path.node)) {
173
- objectExpression = binding.path.node;
174
- }
175
- }
176
- if (objectExpression && types.isObjectExpression(objectExpression)) {
177
- return objectExpression.properties.some((property) => {
178
- if (types.isObjectProperty(property) && types.isIdentifier(property.key)) {
179
- return blacklistedProperties.has(property.key.name);
180
- }
181
- return false;
182
- });
183
- }
184
- }
185
- return true;
186
- }
187
- if (types.isJSXIdentifier(attribute.name) && attribute.value) {
188
- if (attribute.name.name === "children") {
189
- return isStringNode(path, attribute.value);
190
- }
191
- return blacklistedProperties.has(attribute.name.name);
192
- }
193
- return false;
306
+ const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
307
+ const lineNumber = (_c = (_b = path.node.loc) == null ? void 0 : _b.start.line) != null ? _c : "unknown line";
308
+ log(`Optimizing View component in ${filename}:${lineNumber}`);
309
+ if (!file.__hasImports) {
310
+ file.__hasImports = {};
311
+ }
312
+ if (!file.__hasImports.ViewNativeComponent) {
313
+ file.__hasImports.NativeView = addDefault(path, "react-native/Libraries/Components/View/ViewNativeComponent", {
314
+ nameHint: "NativeView"
315
+ });
316
+ }
317
+ const viewNativeIdentifier = file.__hasImports.NativeView;
318
+ path.node.name.name = viewNativeIdentifier.name;
319
+ if (!path.node.selfClosing && parent.closingElement && types.isJSXIdentifier(parent.closingElement.name) && parent.closingElement.name.name === "View") {
320
+ parent.closingElement.name.name = viewNativeIdentifier.name;
321
+ }
322
+ };
323
+ function hasTextAncestor(path) {
324
+ return !!path.findParent((parentPath) => {
325
+ return types.isJSXElement(parentPath.node) && types.isJSXIdentifier(parentPath.node.openingElement.name, { name: "Text" });
194
326
  });
195
327
  }
196
328
 
197
- const log = (message) => {
198
- console.log(`[react-native-boost] ${message}`);
199
- };
200
-
201
329
  var index = declare((api) => {
202
330
  api.assertVersion(7);
203
331
  return {
204
332
  name: "react-native-boost",
205
333
  visitor: {
206
334
  JSXOpeningElement(path, state) {
207
- var _a, _b;
335
+ var _a, _b, _c, _d;
208
336
  const options = (_a = state.opts) != null ? _a : {};
209
337
  const logger = options.verbose ? log : () => {
210
338
  };
211
- if (((_b = options.optimizations) == null ? void 0 : _b.text) !== false) textOptimizer(path, logger);
339
+ if (isIgnoredFile(path, (_b = options.ignores) != null ? _b : [])) return;
340
+ if (((_c = options.optimizations) == null ? void 0 : _c.text) !== false) textOptimizer(path, logger);
341
+ if (((_d = options.optimizations) == null ? void 0 : _d.view) !== false) viewOptimizer(path, logger);
212
342
  }
213
343
  }
214
344
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../../../src/plugin/utils/plugin-error.ts","../../../src/plugin/utils/helpers.ts","../../../src/plugin/utils/common.ts","../../../src/plugin/optimizers/text/index.ts","../../../src/plugin/utils/logger.ts","../../../src/plugin/index.ts"],"sourcesContent":["export default class PluginError extends Error {\n constructor(message: string) {\n super(`[react-native-boost] Babel plugin exception: ${message}`);\n this.name = 'PluginError';\n }\n}\n","export const ensureArray = <T>(value: T | T[]): T[] => {\n if (Array.isArray(value)) return value;\n return [value];\n};\n","import { NodePath, types as t } from '@babel/core';\nimport { ensureArray } from './helpers';\n\n/**\n * Checks if the JSX element should be ignored based on a preceding comment.\n *\n * The function looks up the JSXOpeningElement's own leading comments as well as\n * the parent element's comments before falling back to inspect siblings.\n */\nexport const shouldIgnoreOptimization = (path: NodePath<t.JSXOpeningElement>): boolean => {\n // Check for @boost-ignore in the leading comments on the JSX opening element.\n if (path.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))) {\n return true;\n }\n\n // Check for @boost-ignore in the leading comments on the parent JSX element.\n const jsxElementPath = path.parentPath;\n if (jsxElementPath.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))) {\n return true;\n }\n\n // NEW: Check for @boost-ignore in the leading comments on the ObjectProperty (if it exists)\n // This handles cases where the JSX element is used as a value inside an object literal.\n const propertyPath = jsxElementPath.parentPath;\n if (\n propertyPath &&\n propertyPath.isObjectProperty() &&\n propertyPath.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))\n ) {\n return true;\n }\n\n if (!jsxElementPath.parentPath) return false;\n\n // Get the container that holds this element (for example, a JSX fragment or JSX element)\n const containerPath = jsxElementPath.parentPath;\n const siblings = ensureArray(containerPath.get('children'));\n const index = siblings.findIndex((sibling) => sibling.node === jsxElementPath.node);\n if (index === -1) return false;\n\n // Look backward from the current element for a non-empty node.\n for (let index_ = index - 1; index_ >= 0; index_--) {\n const sibling = siblings[index_];\n // Skip over any whitespace (only in JSXText nodes)\n if (sibling.isJSXText() && sibling.node.value.trim() === '') {\n continue;\n }\n // If the sibling is a JSX expression container, check its empty expression's comments.\n if (sibling.isJSXExpressionContainer()) {\n const expression = sibling.get('expression');\n if (expression && expression.node) {\n const comments = [\n ...(expression.node.leadingComments || []),\n ...(expression.node.trailingComments || []),\n ...(expression.node.innerComments || []),\n ].map((comment) => comment.value.trim());\n if (comments.some((comment) => comment.includes('@boost-ignore'))) {\n return true;\n }\n }\n }\n // Also check if the node itself carries a leadingComments property.\n if (\n sibling.node.leadingComments &&\n sibling.node.leadingComments.some((comment) => comment.value.includes('@boost-ignore'))\n ) {\n return true;\n }\n break; // if the immediate non-whitespace node is not our ignore marker, stop\n }\n return false;\n};\n","import { NodePath, types as t } from '@babel/core';\nimport { addNamed } from '@babel/helper-module-imports';\nimport { HubFile, Optimizer } from '../../types';\nimport PluginError from '../../utils/plugin-error';\nimport { shouldIgnoreOptimization } from '../../utils/common';\n\nexport const textOptimizer: Optimizer = (path, log = () => {}) => {\n // Ensure we're processing a JSX Text element\n if (!t.isJSXIdentifier(path.node.name)) return;\n\n const parent = path.parent;\n if (!t.isJSXElement(parent)) return;\n\n const elementName = path.node.name.name;\n if (elementName !== 'Text') return;\n\n // If the component is preceded by an ignore comment, do not optimize.\n if (shouldIgnoreOptimization(path)) {\n return;\n }\n\n // Ensure Text element comes from react-native\n const binding = path.scope.getBinding(elementName);\n if (!binding) return;\n if (binding.kind === 'module') {\n const parentNode = binding.path.parent;\n if (!t.isImportDeclaration(parentNode) || parentNode.source.value !== 'react-native') {\n return;\n }\n }\n\n // Bail if the element has any blacklisted properties or non-string children props\n if (hasBlacklistedProperties(path)) return;\n if (!hasOnlyStringChildren(path, parent)) return;\n\n // Extract the file from the Babel hub and add flags for logging & import caching\n const hub = path.hub as unknown;\n const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;\n\n if (!file) {\n throw new PluginError('No file found in Babel hub');\n }\n\n const filename = file.opts?.filename || 'unknown file';\n const lineNumber = path.node.loc?.start.line ?? 'unknown line';\n log(`Optimizing Text component in ${filename}:${lineNumber}`);\n\n // Optimize props\n optimizeStyleTag({ path, file });\n\n // Add TextNativeComponent import (cached on file) so we only add it once per file\n if (!file.__hasImports) {\n file.__hasImports = {};\n }\n if (!file.__hasImports.NativeText) {\n file.__hasImports.NativeText = addNamed(path, 'NativeText', 'react-native/Libraries/Text/TextNativeComponent');\n }\n const nativeTextIdentifier = file.__hasImports.NativeText;\n path.node.name.name = nativeTextIdentifier.name;\n\n // If the element is not self-closing, update the closing element as well\n if (\n !path.node.selfClosing &&\n parent.closingElement &&\n t.isJSXIdentifier(parent.closingElement.name) &&\n parent.closingElement.name.name === 'Text'\n ) {\n parent.closingElement.name.name = nativeTextIdentifier.name;\n }\n};\n\nfunction hasOnlyStringChildren(path: NodePath<t.JSXOpeningElement>, node: t.JSXElement): boolean {\n return node.children.every((child) => isStringNode(path, child));\n}\n\nfunction isStringNode(path: NodePath<t.JSXOpeningElement>, child: t.Node): boolean {\n if (t.isJSXText(child)) return true;\n\n // Check for JSX expressions\n if (t.isJSXExpressionContainer(child)) {\n const expression = child.expression;\n\n // If the expression is an identifier, look it up in the current scope\n if (t.isIdentifier(expression)) {\n const binding = path.scope.getBinding(expression.name);\n return binding ? t.isStringLiteral(binding.path.node) : false;\n }\n }\n return false;\n}\n\nconst blacklistedProperties = new Set([\n 'accessible',\n 'accessibilityLabel',\n 'accessibilityState',\n 'allowFontScaling',\n 'aria-busy',\n 'aria-checked',\n 'aria-disabled',\n 'aria-expanded',\n 'aria-label',\n 'aria-selected',\n 'ellipsizeMode',\n 'id',\n 'nativeID',\n 'onLongPress',\n 'onPress',\n 'onPressIn',\n 'onPressOut',\n 'onResponderGrant',\n 'onResponderMove',\n 'onResponderRelease',\n 'onResponderTerminate',\n 'onResponderTerminationRequest',\n 'onStartShouldSetResponder',\n 'pressRetentionOffset',\n 'suppressHighlighting',\n]);\n\nfunction optimizeStyleTag({ path, file }: { path: NodePath<t.JSXOpeningElement>; file: HubFile }) {\n let shouldImportFlattenTextStyle = false;\n const nameHint = '_flattenTextStyle';\n\n for (const [index, attribute] of path.node.attributes.entries()) {\n if (t.isJSXAttribute(attribute) && t.isJSXIdentifier(attribute.name, { name: 'style' })) {\n shouldImportFlattenTextStyle = true;\n\n if (t.isJSXExpressionContainer(attribute.value) && !t.isJSXEmptyExpression(attribute.value.expression)) {\n path.node.attributes[index] = t.jsxSpreadAttribute(\n t.callExpression(t.identifier(nameHint), [attribute.value.expression])\n );\n }\n }\n }\n\n if (shouldImportFlattenTextStyle && !file.__hasImports?.flattenTextStyle) {\n if (!file.__hasImports) file.__hasImports = {};\n file.__hasImports.flattenTextStyle = addNamed(path, 'flattenTextStyle', 'react-native-boost', { nameHint });\n }\n}\n\nfunction hasBlacklistedProperties(path: NodePath<t.JSXOpeningElement>): boolean {\n return path.node.attributes.some((attribute) => {\n // Check if we can resolve the spread attribute\n if (t.isJSXSpreadAttribute(attribute)) {\n if (t.isIdentifier(attribute.argument)) {\n const binding = path.scope.getBinding(attribute.argument.name);\n let objectExpression: t.ObjectExpression | undefined;\n if (binding) {\n // If the binding node is a VariableDeclarator, use its initializer\n if (t.isVariableDeclarator(binding.path.node)) {\n objectExpression = binding.path.node.init as t.ObjectExpression;\n } else if (t.isObjectExpression(binding.path.node)) {\n objectExpression = binding.path.node;\n }\n }\n if (objectExpression && t.isObjectExpression(objectExpression)) {\n return objectExpression.properties.some((property) => {\n if (t.isObjectProperty(property) && t.isIdentifier(property.key)) {\n return blacklistedProperties.has(property.key.name);\n }\n return false;\n });\n }\n }\n // Bail if we can't resolve the spread attribute\n return true;\n }\n\n if (t.isJSXIdentifier(attribute.name) && attribute.value) {\n // For a \"children\" attribute, optimization is allowed only if it is a string\n if (attribute.name.name === 'children') {\n return isStringNode(path, attribute.value);\n }\n return blacklistedProperties.has(attribute.name.name);\n }\n\n // For other attribute types (e.g. namespaced), assume no blacklisting\n return false;\n });\n}\n","export const log = (message: string) => {\n console.log(`[react-native-boost] ${message}`);\n};\n","import { declare } from '@babel/helper-plugin-utils';\nimport { textOptimizer } from './optimizers/text';\nimport { PluginOptions } from './types';\nimport { log } from './utils/logger';\n\nexport default declare((api) => {\n api.assertVersion(7);\n\n return {\n name: 'react-native-boost',\n visitor: {\n JSXOpeningElement(path, state) {\n const options = (state.opts ?? {}) as PluginOptions;\n const logger = options.verbose ? log : () => {};\n if (options.optimizations?.text !== false) textOptimizer(path, logger);\n },\n },\n };\n});\n"],"names":["t"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../../../src/plugin/utils/plugin-error.ts","../../../src/plugin/utils/helpers.ts","../../../src/plugin/utils/common.ts","../../../src/plugin/optimizers/text/index.ts","../../../src/plugin/utils/logger.ts","../../../src/plugin/optimizers/view/index.ts","../../../src/plugin/index.ts"],"sourcesContent":["export default class PluginError extends Error {\n constructor(message: string) {\n super(`[react-native-boost] Babel plugin exception: ${message}`);\n this.name = 'PluginError';\n }\n}\n","export const ensureArray = <T>(value: T | T[]): T[] => {\n if (Array.isArray(value)) return value;\n return [value];\n};\n","import { NodePath, types as t } from '@babel/core';\nimport { ensureArray } from './helpers';\nimport { HubFile } from '../types';\nimport { minimatch } from 'minimatch';\nimport path from 'node:path';\nimport PluginError from './plugin-error';\n\n/**\n * Checks if the file is in the list of ignored files.\n *\n * @param p - The path to the JSXOpeningElement.\n * @param ignores - List of glob paths (absolute or relative to import.meta.dirname).\n * @returns true if the file matches any of the ignore patterns.\n */\nexport const isIgnoredFile = (p: NodePath<t.JSXOpeningElement>, ignores: string[]): boolean => {\n const hub = p.hub as unknown;\n const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;\n\n if (!file) {\n throw new PluginError('No file found in Babel hub');\n }\n\n const fileName = file.opts.filename;\n\n // Use the current working directory which typically corresponds to the user's project root.\n const baseDirectory = 'cwd' in file.opts ? (file.opts.cwd as string) : process.cwd();\n\n // Iterate through the ignore patterns.\n for (const pattern of ignores) {\n // If the pattern is not absolute, join it with the baseDir\n const absolutePattern = path.isAbsolute(pattern) ? pattern : path.join(baseDirectory, pattern);\n\n // Check if the file name matches the glob pattern.\n if (minimatch(fileName, absolutePattern, { dot: true })) {\n return true;\n }\n }\n\n return false;\n};\n\n/**\n * Checks if the JSX element should be ignored based on a preceding comment.\n *\n * The function looks up the JSXOpeningElement's own leading comments as well as\n * the parent element's comments before falling back to inspect siblings.\n */\nexport const shouldIgnoreOptimization = (path: NodePath<t.JSXOpeningElement>): boolean => {\n // Check for @boost-ignore in the leading comments on the JSX opening element.\n if (path.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))) {\n return true;\n }\n\n // Check for @boost-ignore in the leading comments on the parent JSX element.\n const jsxElementPath = path.parentPath;\n if (jsxElementPath.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))) {\n return true;\n }\n\n // NEW: Check for @boost-ignore in the leading comments on the ObjectProperty (if it exists)\n // This handles cases where the JSX element is used as a value inside an object literal.\n const propertyPath = jsxElementPath.parentPath;\n if (\n propertyPath &&\n propertyPath.isObjectProperty() &&\n propertyPath.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))\n ) {\n return true;\n }\n\n if (!jsxElementPath.parentPath) return false;\n\n // Get the container that holds this element (for example, a JSX fragment or JSX element)\n const containerPath = jsxElementPath.parentPath;\n const siblings = ensureArray(containerPath.get('children'));\n const index = siblings.findIndex((sibling) => sibling.node === jsxElementPath.node);\n if (index === -1) return false;\n\n // Look backward from the current element for a non-empty node.\n for (let index_ = index - 1; index_ >= 0; index_--) {\n const sibling = siblings[index_];\n // Skip over any whitespace (only in JSXText nodes)\n if (sibling.isJSXText() && sibling.node.value.trim() === '') {\n continue;\n }\n // If the sibling is a JSX expression container, check its empty expression's comments.\n if (sibling.isJSXExpressionContainer()) {\n const expression = sibling.get('expression');\n if (expression && expression.node) {\n const comments = [\n ...(expression.node.leadingComments || []),\n ...(expression.node.trailingComments || []),\n ...(expression.node.innerComments || []),\n ].map((comment) => comment.value.trim());\n if (comments.some((comment) => comment.includes('@boost-ignore'))) {\n return true;\n }\n }\n }\n // Also check if the node itself carries a leadingComments property.\n if (\n sibling.node.leadingComments &&\n sibling.node.leadingComments.some((comment) => comment.value.includes('@boost-ignore'))\n ) {\n return true;\n }\n break; // if the immediate non-whitespace node is not our ignore marker, stop\n }\n return false;\n};\n\nexport const hasBlacklistedProperty = (path: NodePath<t.JSXOpeningElement>, blacklist: Set<string>): boolean => {\n return path.node.attributes.some((attribute) => {\n // Check if we can resolve the spread attribute\n if (t.isJSXSpreadAttribute(attribute)) {\n if (t.isIdentifier(attribute.argument)) {\n const binding = path.scope.getBinding(attribute.argument.name);\n let objectExpression: t.ObjectExpression | undefined;\n if (binding) {\n // If the binding node is a VariableDeclarator, use its initializer\n if (t.isVariableDeclarator(binding.path.node)) {\n objectExpression = binding.path.node.init as t.ObjectExpression;\n } else if (t.isObjectExpression(binding.path.node)) {\n objectExpression = binding.path.node;\n }\n }\n if (objectExpression && t.isObjectExpression(objectExpression)) {\n return objectExpression.properties.some((property) => {\n if (t.isObjectProperty(property) && t.isIdentifier(property.key)) {\n return blacklist.has(property.key.name);\n }\n return false;\n });\n }\n }\n // Bail if we can't resolve the spread attribute\n return true;\n }\n\n // For other attribute types (e.g. namespaced), assume no blacklisting\n return false;\n });\n};\n","import { NodePath, types as t } from '@babel/core';\nimport { addNamed } from '@babel/helper-module-imports';\nimport { HubFile, Optimizer } from '../../types';\nimport PluginError from '../../utils/plugin-error';\nimport { hasBlacklistedProperty, shouldIgnoreOptimization } from '../../utils/common';\n\nexport const textBlacklistedProperties = new Set([\n 'accessible',\n 'accessibilityLabel',\n 'accessibilityState',\n 'allowFontScaling',\n 'aria-busy',\n 'aria-checked',\n 'aria-disabled',\n 'aria-expanded',\n 'aria-label',\n 'aria-selected',\n 'ellipsizeMode',\n 'id',\n 'nativeID',\n 'onLongPress',\n 'onPress',\n 'onPressIn',\n 'onPressOut',\n 'onResponderGrant',\n 'onResponderMove',\n 'onResponderRelease',\n 'onResponderTerminate',\n 'onResponderTerminationRequest',\n 'onStartShouldSetResponder',\n 'pressRetentionOffset',\n 'suppressHighlighting',\n 'selectable',\n 'selectionColor',\n]);\n\nexport const textOptimizer: Optimizer = (path, log = () => {}) => {\n // Ensure we're processing a JSX Text element\n if (!t.isJSXIdentifier(path.node.name)) return;\n\n const parent = path.parent;\n if (!t.isJSXElement(parent)) return;\n\n const elementName = path.node.name.name;\n if (elementName !== 'Text') return;\n\n // If the component is preceded by an ignore comment, do not optimize.\n if (shouldIgnoreOptimization(path)) {\n return;\n }\n\n // Ensure Text element comes from react-native\n const binding = path.scope.getBinding(elementName);\n if (!binding) return;\n if (binding.kind === 'module') {\n const parentNode = binding.path.parent;\n if (!t.isImportDeclaration(parentNode) || parentNode.source.value !== 'react-native') {\n return;\n }\n }\n\n // Bail if the element has any blacklisted properties or non-string children props\n if (hasBlacklistedProperty(path, textBlacklistedProperties)) return;\n if (hasInvalidChildren(path)) return;\n if (!hasOnlyStringChildren(path, parent)) return;\n\n // Extract the file from the Babel hub and add flags for logging & import caching\n const hub = path.hub as unknown;\n const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;\n\n if (!file) {\n throw new PluginError('No file found in Babel hub');\n }\n\n const filename = file.opts?.filename || 'unknown file';\n const lineNumber = path.node.loc?.start.line ?? 'unknown line';\n log(`Optimizing Text component in ${filename}:${lineNumber}`);\n\n // Optimize props\n fixNegativeNumberOfLines({ path, log });\n optimizeStyleTag({ path, file });\n\n // Add TextNativeComponent import (cached on file) so we only add it once per file\n if (!file.__hasImports) {\n file.__hasImports = {};\n }\n if (!file.__hasImports.NativeText) {\n file.__hasImports.NativeText = addNamed(path, 'NativeText', 'react-native/Libraries/Text/TextNativeComponent');\n }\n const nativeTextIdentifier = file.__hasImports.NativeText;\n path.node.name.name = nativeTextIdentifier.name;\n\n // If the element is not self-closing, update the closing element as well\n if (\n !path.node.selfClosing &&\n parent.closingElement &&\n t.isJSXIdentifier(parent.closingElement.name) &&\n parent.closingElement.name.name === 'Text'\n ) {\n parent.closingElement.name.name = nativeTextIdentifier.name;\n }\n};\n\nfunction hasOnlyStringChildren(path: NodePath<t.JSXOpeningElement>, node: t.JSXElement): boolean {\n return node.children.every((child) => isStringNode(path, child));\n}\n\nfunction isStringNode(path: NodePath<t.JSXOpeningElement>, child: t.Node): boolean {\n if (t.isJSXText(child)) return true;\n\n // Check for JSX expressions\n if (t.isJSXExpressionContainer(child)) {\n const expression = child.expression;\n\n // If the expression is an identifier, look it up in the current scope\n if (t.isIdentifier(expression)) {\n const binding = path.scope.getBinding(expression.name);\n return binding ? t.isStringLiteral(binding.path.node) : false;\n }\n }\n return false;\n}\n\nfunction fixNegativeNumberOfLines({\n path,\n log,\n}: {\n path: NodePath<t.JSXOpeningElement>;\n log: (message: string) => void;\n}) {\n for (const attribute of path.node.attributes) {\n if (\n t.isJSXAttribute(attribute) &&\n t.isJSXIdentifier(attribute.name, { name: 'numberOfLines' }) &&\n attribute.value &&\n t.isJSXExpressionContainer(attribute.value)\n ) {\n let originalValue: number | undefined;\n if (t.isNumericLiteral(attribute.value.expression)) {\n originalValue = attribute.value.expression.value;\n } else if (\n t.isUnaryExpression(attribute.value.expression) &&\n attribute.value.expression.operator === '-' &&\n t.isNumericLiteral(attribute.value.expression.argument)\n ) {\n originalValue = -attribute.value.expression.argument.value;\n }\n if (originalValue !== undefined && originalValue < 0) {\n log(\n `Warning: 'numberOfLines' in <Text> must be a non-negative number, received: ${originalValue}. The value will be set to 0.`\n );\n attribute.value.expression = t.numericLiteral(0);\n }\n }\n }\n}\n\nfunction optimizeStyleTag({ path, file }: { path: NodePath<t.JSXOpeningElement>; file: HubFile }) {\n let shouldImportFlattenTextStyle = false;\n const nameHint = '_flattenTextStyle';\n\n for (const [index, attribute] of path.node.attributes.entries()) {\n if (t.isJSXAttribute(attribute) && t.isJSXIdentifier(attribute.name, { name: 'style' })) {\n shouldImportFlattenTextStyle = true;\n\n if (t.isJSXExpressionContainer(attribute.value) && !t.isJSXEmptyExpression(attribute.value.expression)) {\n path.node.attributes[index] = t.jsxSpreadAttribute(\n t.callExpression(t.identifier(nameHint), [attribute.value.expression])\n );\n }\n }\n }\n\n if (shouldImportFlattenTextStyle && !file.__hasImports?.flattenTextStyle) {\n if (!file.__hasImports) file.__hasImports = {};\n file.__hasImports.flattenTextStyle = addNamed(path, 'flattenTextStyle', 'react-native-boost', { nameHint });\n }\n}\n\nfunction hasInvalidChildren(path: NodePath<t.JSXOpeningElement>): boolean {\n for (const attribute of path.node.attributes) {\n if (t.isJSXSpreadAttribute(attribute)) return false; // spread attributes are handled in hasBlacklistedProperty\n\n if (t.isJSXIdentifier(attribute.name) && attribute.value) {\n // For a \"children\" attribute, optimization is allowed only if it is a string\n if (attribute.name.name === 'children') {\n return isStringNode(path, attribute.value);\n }\n return textBlacklistedProperties.has(attribute.name.name);\n }\n }\n return false;\n}\n","export const log = (message: string) => {\n console.log(`[react-native-boost] ${message}`);\n};\n","import { NodePath, types as t } from '@babel/core';\nimport { addDefault } from '@babel/helper-module-imports';\nimport { HubFile, Optimizer } from '../../types';\nimport PluginError from '../../utils/plugin-error';\nimport { hasBlacklistedProperty, shouldIgnoreOptimization } from '../../utils/common';\n\nexport const viewBlacklistedProperties = new Set([\n 'accessible',\n 'accessibilityLabel',\n 'accessibilityState',\n 'allowFontScaling',\n 'aria-busy',\n 'aria-checked',\n 'aria-disabled',\n 'aria-expanded',\n 'aria-label',\n 'aria-selected',\n 'ellipsizeMode',\n 'disabled',\n 'id',\n 'nativeID',\n 'numberOfLines',\n 'onLongPress',\n 'onPress',\n 'onPressIn',\n 'onPressOut',\n 'onResponderGrant',\n 'onResponderMove',\n 'onResponderRelease',\n 'onResponderTerminate',\n 'onResponderTerminationRequest',\n 'onStartShouldSetResponder',\n 'pressRetentionOffset',\n 'selectable',\n 'selectionColor',\n 'suppressHighlighting',\n 'style',\n]);\n\nexport const viewOptimizer: Optimizer = (path, log = () => {}) => {\n // Ensure we're processing a JSX element identifier.\n if (!t.isJSXIdentifier(path.node.name)) return;\n\n const parent = path.parent;\n if (!t.isJSXElement(parent)) return;\n\n const elementName = path.node.name.name;\n if (elementName !== 'View') return;\n\n // Respect comments that disable optimization.\n if (shouldIgnoreOptimization(path)) return;\n\n // Ensure the View element comes from react-native.\n const binding = path.scope.getBinding(elementName);\n if (!binding) return;\n if (binding.kind === 'module') {\n const parentNode = binding.path.parent;\n if (!t.isImportDeclaration(parentNode) || parentNode.source.value !== 'react-native') {\n return;\n }\n }\n\n // Bail if any blacklisted props are present.\n if (hasBlacklistedProperty(path, viewBlacklistedProperties)) return;\n\n // Bail if a <TextAncestor /> component exists as an ancestor.\n if (hasTextAncestor(path)) return;\n\n // Extract the file from the Babel hub and add flags for logging & import caching.\n const hub = path.hub as unknown;\n const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;\n\n if (!file) {\n throw new PluginError('No file found in Babel hub');\n }\n\n const filename = file.opts?.filename || 'unknown file';\n const lineNumber = path.node.loc?.start.line ?? 'unknown line';\n log(`Optimizing View component in ${filename}:${lineNumber}`);\n\n // Add ViewNativeComponent import (cached on the file) to prevent duplicate imports.\n if (!file.__hasImports) {\n file.__hasImports = {};\n }\n if (!file.__hasImports.ViewNativeComponent) {\n file.__hasImports.NativeView = addDefault(path, 'react-native/Libraries/Components/View/ViewNativeComponent', {\n nameHint: 'NativeView',\n });\n }\n const viewNativeIdentifier = file.__hasImports.NativeView;\n\n // Replace the component with its native counterpart.\n path.node.name.name = viewNativeIdentifier.name;\n\n // If the element is not self-closing, update the closing element as well.\n if (\n !path.node.selfClosing &&\n parent.closingElement &&\n t.isJSXIdentifier(parent.closingElement.name) &&\n parent.closingElement.name.name === 'View'\n ) {\n parent.closingElement.name.name = viewNativeIdentifier.name;\n }\n};\n\n/**\n * Returns true if any ancestor element is a <Text />.\n * TODO: This is dangerous as we can't resolve custom components and check if they have a <Text /> ancestor in the tree\n */\nfunction hasTextAncestor(path: NodePath<t.JSXOpeningElement>): boolean {\n return !!path.findParent((parentPath) => {\n return t.isJSXElement(parentPath.node) && t.isJSXIdentifier(parentPath.node.openingElement.name, { name: 'Text' });\n });\n}\n","import { declare } from '@babel/helper-plugin-utils';\nimport { textOptimizer } from './optimizers/text';\nimport { PluginOptions } from './types';\nimport { log } from './utils/logger';\nimport { viewOptimizer } from './optimizers/view';\nimport { isIgnoredFile } from './utils/common';\n\nexport default declare((api) => {\n api.assertVersion(7);\n\n return {\n name: 'react-native-boost',\n visitor: {\n JSXOpeningElement(path, state) {\n const options = (state.opts ?? {}) as PluginOptions;\n const logger = options.verbose ? log : () => {};\n if (isIgnoredFile(path, options.ignores ?? [])) return;\n if (options.optimizations?.text !== false) textOptimizer(path, logger);\n if (options.optimizations?.view !== false) viewOptimizer(path, logger);\n },\n },\n };\n});\n"],"names":["path","t"],"mappings":"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"}