react-native-boost 0.1.0 โ†’ 0.3.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
@@ -14,6 +14,18 @@ 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
+ ## Benchmark
18
+
19
+ The example app in the `apps/example` directory is a benchmark for the performance of the plugin.
20
+
21
+ <div align="center">
22
+ <img src="./packages/react-native-boost/img/benchmark.png" width="500" />
23
+ <p>
24
+ <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 and using New Architecture, lower is better.
26
+ </p>
27
+ </div>
28
+
17
29
  ## Installation
18
30
 
19
31
  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.
@@ -15,7 +15,18 @@ const ensureArray = (value) => {
15
15
  };
16
16
 
17
17
  const shouldIgnoreOptimization = (path) => {
18
+ var _a, _b, _c;
19
+ if ((_a = path.node.leadingComments) == null ? void 0 : _a.some((comment) => comment.value.includes("@boost-ignore"))) {
20
+ return true;
21
+ }
18
22
  const jsxElementPath = path.parentPath;
23
+ if ((_b = jsxElementPath.node.leadingComments) == null ? void 0 : _b.some((comment) => comment.value.includes("@boost-ignore"))) {
24
+ return true;
25
+ }
26
+ const propertyPath = jsxElementPath.parentPath;
27
+ if (propertyPath && propertyPath.isObjectProperty() && ((_c = propertyPath.node.leadingComments) == null ? void 0 : _c.some((comment) => comment.value.includes("@boost-ignore")))) {
28
+ return true;
29
+ }
19
30
  if (!jsxElementPath.parentPath) return false;
20
31
  const containerPath = jsxElementPath.parentPath;
21
32
  const siblings = ensureArray(containerPath.get("children"));
@@ -49,7 +60,7 @@ const shouldIgnoreOptimization = (path) => {
49
60
 
50
61
  const textOptimizer = (path, log = () => {
51
62
  }) => {
52
- var _a;
63
+ var _a, _b, _c;
53
64
  if (!types.isJSXIdentifier(path.node.name)) return;
54
65
  const parent = path.parent;
55
66
  if (!types.isJSXElement(parent)) return;
@@ -73,11 +84,10 @@ const textOptimizer = (path, log = () => {
73
84
  if (!file) {
74
85
  throw new PluginError("No file found in Babel hub");
75
86
  }
76
- if (!file.__optimized) {
77
- const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
78
- log(`Optimizing file: ${filename}`);
79
- file.__optimized = true;
80
- }
87
+ const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
88
+ const lineNumber = (_c = (_b = path.node.loc) == null ? void 0 : _b.start.line) != null ? _c : "unknown line";
89
+ log(`Optimizing Text component in ${filename}:${lineNumber}`);
90
+ fixNegativeNumberOfLines({ path, log });
81
91
  optimizeStyleTag({ path, file });
82
92
  if (!file.__hasImports) {
83
93
  file.__hasImports = {};
@@ -130,8 +140,30 @@ const blacklistedProperties = /* @__PURE__ */ new Set([
130
140
  "onResponderTerminationRequest",
131
141
  "onStartShouldSetResponder",
132
142
  "pressRetentionOffset",
133
- "suppressHighlighting"
143
+ "suppressHighlighting",
144
+ "selectionColor"
134
145
  ]);
146
+ function fixNegativeNumberOfLines({
147
+ path,
148
+ log
149
+ }) {
150
+ for (const attribute of path.node.attributes) {
151
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "numberOfLines" }) && attribute.value && types.isJSXExpressionContainer(attribute.value)) {
152
+ let originalValue;
153
+ if (types.isNumericLiteral(attribute.value.expression)) {
154
+ originalValue = attribute.value.expression.value;
155
+ } else if (types.isUnaryExpression(attribute.value.expression) && attribute.value.expression.operator === "-" && types.isNumericLiteral(attribute.value.expression.argument)) {
156
+ originalValue = -attribute.value.expression.argument.value;
157
+ }
158
+ if (originalValue !== void 0 && originalValue < 0) {
159
+ log(
160
+ `Warning: 'numberOfLines' in <Text> must be a non-negative number, received: ${originalValue}. The value will be set to 0.`
161
+ );
162
+ attribute.value.expression = types.numericLiteral(0);
163
+ }
164
+ }
165
+ }
166
+ }
135
167
  function optimizeStyleTag({ path, file }) {
136
168
  var _a;
137
169
  let shouldImportFlattenTextStyle = false;
@@ -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 parent container's children, finds the current\n * JSX element, and then (ignoring whitespace-only nodes) inspects the previous\n * sibling for a comment that includes \"@boost-ignore\".\n */\nexport const shouldIgnoreOptimization = (path: NodePath<t.JSXOpeningElement>): boolean => {\n // Get the JSX element by going up one level (opening element -> JSXElement)\n const jsxElementPath = path.parentPath;\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 // Log the file's optimized status only once\n if (!file.__optimized) {\n const filename = file.opts?.filename || 'unknown file';\n log(`Optimizing file: ${filename}`);\n file.__optimized = true;\n }\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/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 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\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 'selectionColor',\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 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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"}
@@ -17,7 +17,18 @@ const ensureArray = (value) => {
17
17
  };
18
18
 
19
19
  const shouldIgnoreOptimization = (path) => {
20
+ var _a, _b, _c;
21
+ if ((_a = path.node.leadingComments) == null ? void 0 : _a.some((comment) => comment.value.includes("@boost-ignore"))) {
22
+ return true;
23
+ }
20
24
  const jsxElementPath = path.parentPath;
25
+ if ((_b = jsxElementPath.node.leadingComments) == null ? void 0 : _b.some((comment) => comment.value.includes("@boost-ignore"))) {
26
+ return true;
27
+ }
28
+ const propertyPath = jsxElementPath.parentPath;
29
+ if (propertyPath && propertyPath.isObjectProperty() && ((_c = propertyPath.node.leadingComments) == null ? void 0 : _c.some((comment) => comment.value.includes("@boost-ignore")))) {
30
+ return true;
31
+ }
21
32
  if (!jsxElementPath.parentPath) return false;
22
33
  const containerPath = jsxElementPath.parentPath;
23
34
  const siblings = ensureArray(containerPath.get("children"));
@@ -51,7 +62,7 @@ const shouldIgnoreOptimization = (path) => {
51
62
 
52
63
  const textOptimizer = (path, log = () => {
53
64
  }) => {
54
- var _a;
65
+ var _a, _b, _c;
55
66
  if (!core.types.isJSXIdentifier(path.node.name)) return;
56
67
  const parent = path.parent;
57
68
  if (!core.types.isJSXElement(parent)) return;
@@ -75,11 +86,10 @@ const textOptimizer = (path, log = () => {
75
86
  if (!file) {
76
87
  throw new PluginError("No file found in Babel hub");
77
88
  }
78
- if (!file.__optimized) {
79
- const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
80
- log(`Optimizing file: ${filename}`);
81
- file.__optimized = true;
82
- }
89
+ const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
90
+ const lineNumber = (_c = (_b = path.node.loc) == null ? void 0 : _b.start.line) != null ? _c : "unknown line";
91
+ log(`Optimizing Text component in ${filename}:${lineNumber}`);
92
+ fixNegativeNumberOfLines({ path, log });
83
93
  optimizeStyleTag({ path, file });
84
94
  if (!file.__hasImports) {
85
95
  file.__hasImports = {};
@@ -132,8 +142,30 @@ const blacklistedProperties = /* @__PURE__ */ new Set([
132
142
  "onResponderTerminationRequest",
133
143
  "onStartShouldSetResponder",
134
144
  "pressRetentionOffset",
135
- "suppressHighlighting"
145
+ "suppressHighlighting",
146
+ "selectionColor"
136
147
  ]);
148
+ function fixNegativeNumberOfLines({
149
+ path,
150
+ log
151
+ }) {
152
+ for (const attribute of path.node.attributes) {
153
+ if (core.types.isJSXAttribute(attribute) && core.types.isJSXIdentifier(attribute.name, { name: "numberOfLines" }) && attribute.value && core.types.isJSXExpressionContainer(attribute.value)) {
154
+ let originalValue;
155
+ if (core.types.isNumericLiteral(attribute.value.expression)) {
156
+ originalValue = attribute.value.expression.value;
157
+ } else if (core.types.isUnaryExpression(attribute.value.expression) && attribute.value.expression.operator === "-" && core.types.isNumericLiteral(attribute.value.expression.argument)) {
158
+ originalValue = -attribute.value.expression.argument.value;
159
+ }
160
+ if (originalValue !== void 0 && originalValue < 0) {
161
+ log(
162
+ `Warning: 'numberOfLines' in <Text> must be a non-negative number, received: ${originalValue}. The value will be set to 0.`
163
+ );
164
+ attribute.value.expression = core.types.numericLiteral(0);
165
+ }
166
+ }
167
+ }
168
+ }
137
169
  function optimizeStyleTag({ path, file }) {
138
170
  var _a;
139
171
  let shouldImportFlattenTextStyle = false;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","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 parent container's children, finds the current\n * JSX element, and then (ignoring whitespace-only nodes) inspects the previous\n * sibling for a comment that includes \"@boost-ignore\".\n */\nexport const shouldIgnoreOptimization = (path: NodePath<t.JSXOpeningElement>): boolean => {\n // Get the JSX element by going up one level (opening element -> JSXElement)\n const jsxElementPath = path.parentPath;\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 // Log the file's optimized status only once\n if (!file.__optimized) {\n const filename = file.opts?.filename || 'unknown file';\n log(`Optimizing file: ${filename}`);\n file.__optimized = true;\n }\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","addNamed","declare"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","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 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\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 'selectionColor',\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 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","addNamed","declare"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-boost",
3
3
  "description": "๐Ÿš€ Boost your React Native app's performance with a single line of code",
4
- "version": "0.1.0",
4
+ "version": "0.3.0",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -41,14 +41,12 @@ export const textOptimizer: Optimizer = (path, log = () => {}) => {
41
41
  throw new PluginError('No file found in Babel hub');
42
42
  }
43
43
 
44
- // Log the file's optimized status only once
45
- if (!file.__optimized) {
46
- const filename = file.opts?.filename || 'unknown file';
47
- log(`Optimizing file: ${filename}`);
48
- file.__optimized = true;
49
- }
44
+ const filename = file.opts?.filename || 'unknown file';
45
+ const lineNumber = path.node.loc?.start.line ?? 'unknown line';
46
+ log(`Optimizing Text component in ${filename}:${lineNumber}`);
50
47
 
51
48
  // Optimize props
49
+ fixNegativeNumberOfLines({ path, log });
52
50
  optimizeStyleTag({ path, file });
53
51
 
54
52
  // Add TextNativeComponent import (cached on file) so we only add it once per file
@@ -118,8 +116,43 @@ const blacklistedProperties = new Set([
118
116
  'onStartShouldSetResponder',
119
117
  'pressRetentionOffset',
120
118
  'suppressHighlighting',
119
+ 'selectionColor',
121
120
  ]);
122
121
 
122
+ function fixNegativeNumberOfLines({
123
+ path,
124
+ log,
125
+ }: {
126
+ path: NodePath<t.JSXOpeningElement>;
127
+ log: (message: string) => void;
128
+ }) {
129
+ for (const attribute of path.node.attributes) {
130
+ if (
131
+ t.isJSXAttribute(attribute) &&
132
+ t.isJSXIdentifier(attribute.name, { name: 'numberOfLines' }) &&
133
+ attribute.value &&
134
+ t.isJSXExpressionContainer(attribute.value)
135
+ ) {
136
+ let originalValue: number | undefined;
137
+ if (t.isNumericLiteral(attribute.value.expression)) {
138
+ originalValue = attribute.value.expression.value;
139
+ } else if (
140
+ t.isUnaryExpression(attribute.value.expression) &&
141
+ attribute.value.expression.operator === '-' &&
142
+ t.isNumericLiteral(attribute.value.expression.argument)
143
+ ) {
144
+ originalValue = -attribute.value.expression.argument.value;
145
+ }
146
+ if (originalValue !== undefined && originalValue < 0) {
147
+ log(
148
+ `Warning: 'numberOfLines' in <Text> must be a non-negative number, received: ${originalValue}. The value will be set to 0.`
149
+ );
150
+ attribute.value.expression = t.numericLiteral(0);
151
+ }
152
+ }
153
+ }
154
+ }
155
+
123
156
  function optimizeStyleTag({ path, file }: { path: NodePath<t.JSXOpeningElement>; file: HubFile }) {
124
157
  let shouldImportFlattenTextStyle = false;
125
158
  const nameHint = '_flattenTextStyle';
@@ -4,13 +4,32 @@ import { ensureArray } from './helpers';
4
4
  /**
5
5
  * Checks if the JSX element should be ignored based on a preceding comment.
6
6
  *
7
- * The function looks up the parent container's children, finds the current
8
- * JSX element, and then (ignoring whitespace-only nodes) inspects the previous
9
- * sibling for a comment that includes "@boost-ignore".
7
+ * The function looks up the JSXOpeningElement's own leading comments as well as
8
+ * the parent element's comments before falling back to inspect siblings.
10
9
  */
11
10
  export const shouldIgnoreOptimization = (path: NodePath<t.JSXOpeningElement>): boolean => {
12
- // Get the JSX element by going up one level (opening element -> JSXElement)
11
+ // Check for @boost-ignore in the leading comments on the JSX opening element.
12
+ if (path.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))) {
13
+ return true;
14
+ }
15
+
16
+ // Check for @boost-ignore in the leading comments on the parent JSX element.
13
17
  const jsxElementPath = path.parentPath;
18
+ if (jsxElementPath.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))) {
19
+ return true;
20
+ }
21
+
22
+ // NEW: Check for @boost-ignore in the leading comments on the ObjectProperty (if it exists)
23
+ // This handles cases where the JSX element is used as a value inside an object literal.
24
+ const propertyPath = jsxElementPath.parentPath;
25
+ if (
26
+ propertyPath &&
27
+ propertyPath.isObjectProperty() &&
28
+ propertyPath.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))
29
+ ) {
30
+ return true;
31
+ }
32
+
14
33
  if (!jsxElementPath.parentPath) return false;
15
34
 
16
35
  // Get the container that holds this element (for example, a JSX fragment or JSX element)