react-native-boost 0.7.0 → 1.1.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.
@@ -1,7 +1,16 @@
1
- import { TextStyle, TextProps } from 'react-native';
1
+ import { TextProps, ViewProps, TextStyle } from 'react-native';
2
+ import { ComponentType } from 'react';
2
3
 
4
+ /**
5
+ * Recursive style prop shape accepted by runtime style helpers.
6
+ *
7
+ * @template T - Style object type.
8
+ */
3
9
  type GenericStyleProp<T> = null | void | T | false | '' | ReadonlyArray<GenericStyleProp<T>>;
4
10
 
11
+ /**
12
+ * Maps CSS-like `userSelect` values to React Native's `selectable` prop.
13
+ */
5
14
  declare const userSelectToSelectableMap: {
6
15
  auto: boolean;
7
16
  text: boolean;
@@ -9,6 +18,9 @@ declare const userSelectToSelectableMap: {
9
18
  contain: boolean;
10
19
  all: boolean;
11
20
  };
21
+ /**
22
+ * Maps CSS-like `verticalAlign` values to React Native's `textAlignVertical`.
23
+ */
12
24
  declare const verticalAlignToTextAlignVerticalMap: {
13
25
  auto: string;
14
26
  top: string;
@@ -16,11 +28,45 @@ declare const verticalAlignToTextAlignVerticalMap: {
16
28
  middle: string;
17
29
  };
18
30
 
19
- declare const NativeText: any;
31
+ /**
32
+ * Native Text component with graceful fallback.
33
+ *
34
+ * @remarks
35
+ * Uses `unstable_NativeText` on supported native runtimes and falls back to `Text`
36
+ * on web or when the unstable export is unavailable.
37
+ */
38
+ declare const NativeText: ComponentType<TextProps>;
20
39
 
21
- declare const NativeView: any;
40
+ /**
41
+ * Native View component with graceful fallback.
42
+ *
43
+ * @remarks
44
+ * Uses `unstable_NativeView` on supported native runtimes and falls back to `View`
45
+ * on web or when the unstable export is unavailable.
46
+ */
47
+ declare const NativeView: ComponentType<ViewProps>;
22
48
 
49
+ /**
50
+ * Normalizes `Text` style values for `NativeText`.
51
+ *
52
+ * @param style - Style prop passed to a text-like component.
53
+ * @returns Native-friendly text props. Returns an empty object when `style` is falsy or cannot be normalized.
54
+ * @remarks
55
+ * - Flattens style arrays via `StyleSheet.flatten`
56
+ * - Converts numeric `fontWeight` values to string values
57
+ * - Maps `userSelect` and `verticalAlign` to native-compatible props
58
+ */
23
59
  declare function processTextStyle(style: GenericStyleProp<TextStyle>): Partial<TextProps>;
60
+ /**
61
+ * Normalizes accessibility and ARIA props for runtime native components.
62
+ *
63
+ * @param props - Accessibility and ARIA props.
64
+ * @returns Props with normalized accessibility fields.
65
+ * @remarks
66
+ * - Merges `aria-label` with `accessibilityLabel`
67
+ * - Merges ARIA state fields into `accessibilityState`
68
+ * - Defaults `accessible` to `true` when omitted
69
+ */
24
70
  declare function processAccessibilityProps(props: Record<string, any>): Record<string, any>;
25
71
 
26
72
  export { NativeText, NativeView, processAccessibilityProps, processTextStyle, userSelectToSelectableMap, verticalAlignToTextAlignVerticalMap };
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var reactNative = require('react-native');
3
+ var reactNative$2 = require('react-native');
4
4
 
5
5
  const userSelectToSelectableMap = {
6
6
  auto: true,
@@ -16,9 +16,21 @@ const verticalAlignToTextAlignVerticalMap = {
16
16
  middle: "center"
17
17
  };
18
18
 
19
- const NativeText = reactNative.Platform.OS === "web" ? require("react-native").Text : require("react-native/Libraries/Text/TextNativeComponent").NativeText;
19
+ const reactNative$1 = require("react-native");
20
+ const isWeb$1 = reactNative$1.Platform.OS === "web";
21
+ let nativeText = reactNative$1.unstable_NativeText;
22
+ if (isWeb$1 || nativeText == null) {
23
+ nativeText = reactNative$1.Text;
24
+ }
25
+ const NativeText = nativeText;
20
26
 
21
- const NativeView = reactNative.Platform.OS === "web" ? require("react-native").View : require("react-native/Libraries/Components/View/ViewNativeComponent").default;
27
+ const reactNative = require("react-native");
28
+ const isWeb = reactNative.Platform.OS === "web";
29
+ let nativeView = reactNative.unstable_NativeView;
30
+ if (isWeb || nativeView == null) {
31
+ nativeView = reactNative.View;
32
+ }
33
+ const NativeView = nativeView;
22
34
 
23
35
  const propsCache = /* @__PURE__ */ new WeakMap();
24
36
  function processTextStyle(style) {
@@ -27,7 +39,7 @@ function processTextStyle(style) {
27
39
  if (props) return props;
28
40
  props = {};
29
41
  propsCache.set(style, props);
30
- style = reactNative.StyleSheet.flatten(style);
42
+ style = reactNative$2.StyleSheet.flatten(style);
31
43
  if (!style) return {};
32
44
  if (typeof (style == null ? void 0 : style.fontWeight) === "number") {
33
45
  style.fontWeight = style.fontWeight.toString();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/runtime/utils/constants.ts","../../src/runtime/components/native-text.tsx","../../src/runtime/components/native-view.tsx","../../src/runtime/index.ts"],"sourcesContent":["// Maps the `userSelect` prop to the native `selectable` prop\nexport const userSelectToSelectableMap = {\n auto: true,\n text: true,\n none: false,\n contain: true,\n all: true,\n};\n\n// Maps the `verticalAlign` prop to the native `textAlignVertical` prop\nexport const verticalAlignToTextAlignVerticalMap = {\n auto: 'auto',\n top: 'top',\n bottom: 'bottom',\n middle: 'center',\n};\n","/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\nimport { Platform } from 'react-native';\n\nexport const NativeText =\n Platform.OS === 'web'\n ? require('react-native').Text\n : require('react-native/Libraries/Text/TextNativeComponent').NativeText;\n","/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\nimport { Platform } from 'react-native';\n\nexport const NativeView =\n Platform.OS === 'web'\n ? require('react-native').View\n : require('react-native/Libraries/Components/View/ViewNativeComponent').default;\n","import { TextProps, TextStyle, StyleSheet } from 'react-native';\nimport { GenericStyleProp } from './types';\nimport { userSelectToSelectableMap, verticalAlignToTextAlignVerticalMap } from './utils/constants';\n\nconst propsCache = new WeakMap();\n\nexport function processTextStyle(style: GenericStyleProp<TextStyle>): Partial<TextProps> {\n if (!style) return {};\n\n // Cache the computed props\n let props = propsCache.get(style);\n if (props) return props;\n\n props = {};\n propsCache.set(style, props);\n\n style = StyleSheet.flatten(style) as TextStyle;\n\n if (!style) return {};\n\n if (typeof style?.fontWeight === 'number') {\n style.fontWeight = style.fontWeight.toString() as TextStyle['fontWeight'];\n }\n\n if (style?.userSelect != null) {\n props.selectable = userSelectToSelectableMap[style.userSelect];\n delete style.userSelect;\n }\n\n if (style?.verticalAlign != null) {\n style.textAlignVertical = verticalAlignToTextAlignVerticalMap[\n style.verticalAlign\n ] as TextStyle['textAlignVertical'];\n delete style.verticalAlign;\n }\n\n props.style = style;\n return props;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function processAccessibilityProps(props: Record<string, any>): Record<string, any> {\n const {\n accessibilityLabel,\n ['aria-label']: ariaLabel,\n accessibilityState,\n ['aria-busy']: ariaBusy,\n ['aria-checked']: ariaChecked,\n ['aria-disabled']: ariaDisabled,\n ['aria-expanded']: ariaExpanded,\n ['aria-selected']: ariaSelected,\n accessible,\n ...restProperties\n } = props;\n\n // Merge label props: prefer the aria-label if defined.\n const normalizedLabel = ariaLabel ?? accessibilityLabel;\n\n // Merge the accessibilityState with any provided ARIA properties.\n let normalizedState = accessibilityState;\n if (ariaBusy != null || ariaChecked != null || ariaDisabled != null || ariaExpanded != null || ariaSelected != null) {\n normalizedState =\n normalizedState == null\n ? {\n busy: ariaBusy,\n checked: ariaChecked,\n disabled: ariaDisabled,\n expanded: ariaExpanded,\n selected: ariaSelected,\n }\n : {\n busy: ariaBusy ?? normalizedState.busy,\n checked: ariaChecked ?? normalizedState.checked,\n disabled: ariaDisabled ?? normalizedState.disabled,\n expanded: ariaExpanded ?? normalizedState.expanded,\n selected: ariaSelected ?? normalizedState.selected,\n };\n }\n\n // For the accessible prop, if not provided, default to `true`\n const normalizedAccessible = accessible == null ? true : accessible;\n\n return {\n ...restProperties,\n accessibilityLabel: normalizedLabel,\n accessibilityState: normalizedState,\n accessible: normalizedAccessible,\n };\n}\n\nexport * from './types';\nexport * from './utils/constants';\nexport * from './components/native-text';\nexport * from './components/native-view';\n"],"names":["Platform","StyleSheet"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/runtime/utils/constants.ts","../../src/runtime/components/native-text.tsx","../../src/runtime/components/native-view.tsx","../../src/runtime/index.ts"],"sourcesContent":["/**\n * Maps CSS-like `userSelect` values to React Native's `selectable` prop.\n */\nexport const userSelectToSelectableMap = {\n auto: true,\n text: true,\n none: false,\n contain: true,\n all: true,\n};\n\n/**\n * Maps CSS-like `verticalAlign` values to React Native's `textAlignVertical`.\n */\nexport const verticalAlignToTextAlignVerticalMap = {\n auto: 'auto',\n top: 'top',\n bottom: 'bottom',\n middle: 'center',\n};\n","/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\n\nimport type { ComponentType } from 'react';\nimport type { TextProps } from 'react-native';\n\nconst reactNative = require('react-native');\nconst isWeb = reactNative.Platform.OS === 'web';\n\nlet nativeText = reactNative.unstable_NativeText;\n\nif (isWeb || nativeText == null) {\n // Fallback to regular Text component if unstable_NativeText is not available or we're on Web\n nativeText = reactNative.Text;\n}\n\n/**\n * Native Text component with graceful fallback.\n *\n * @remarks\n * Uses `unstable_NativeText` on supported native runtimes and falls back to `Text`\n * on web or when the unstable export is unavailable.\n */\nexport const NativeText: ComponentType<TextProps> = nativeText;\n","/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\n\nimport type { ComponentType } from 'react';\nimport type { ViewProps } from 'react-native';\n\nconst reactNative = require('react-native');\nconst isWeb = reactNative.Platform.OS === 'web';\n\nlet nativeView = reactNative.unstable_NativeView;\n\nif (isWeb || nativeView == null) {\n // Fallback to regular View component if unstable_NativeView is not available or we're on Web\n nativeView = reactNative.View;\n}\n\n/**\n * Native View component with graceful fallback.\n *\n * @remarks\n * Uses `unstable_NativeView` on supported native runtimes and falls back to `View`\n * on web or when the unstable export is unavailable.\n */\nexport const NativeView: ComponentType<ViewProps> = nativeView;\n","import { TextProps, TextStyle, StyleSheet } from 'react-native';\nimport { GenericStyleProp } from './types';\nimport { userSelectToSelectableMap, verticalAlignToTextAlignVerticalMap } from './utils/constants';\n\nconst propsCache = new WeakMap();\n\n/**\n * Normalizes `Text` style values for `NativeText`.\n *\n * @param style - Style prop passed to a text-like component.\n * @returns Native-friendly text props. Returns an empty object when `style` is falsy or cannot be normalized.\n * @remarks\n * - Flattens style arrays via `StyleSheet.flatten`\n * - Converts numeric `fontWeight` values to string values\n * - Maps `userSelect` and `verticalAlign` to native-compatible props\n */\nexport function processTextStyle(style: GenericStyleProp<TextStyle>): Partial<TextProps> {\n if (!style) return {};\n\n // Cache the computed props\n let props = propsCache.get(style);\n if (props) return props;\n\n props = {};\n propsCache.set(style, props);\n\n style = StyleSheet.flatten(style) as TextStyle;\n\n if (!style) return {};\n\n if (typeof style?.fontWeight === 'number') {\n style.fontWeight = style.fontWeight.toString() as TextStyle['fontWeight'];\n }\n\n if (style?.userSelect != null) {\n props.selectable = userSelectToSelectableMap[style.userSelect];\n delete style.userSelect;\n }\n\n if (style?.verticalAlign != null) {\n style.textAlignVertical = verticalAlignToTextAlignVerticalMap[\n style.verticalAlign\n ] as TextStyle['textAlignVertical'];\n delete style.verticalAlign;\n }\n\n props.style = style;\n return props;\n}\n\n/**\n * Normalizes accessibility and ARIA props for runtime native components.\n *\n * @param props - Accessibility and ARIA props.\n * @returns Props with normalized accessibility fields.\n * @remarks\n * - Merges `aria-label` with `accessibilityLabel`\n * - Merges ARIA state fields into `accessibilityState`\n * - Defaults `accessible` to `true` when omitted\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function processAccessibilityProps(props: Record<string, any>): Record<string, any> {\n const {\n accessibilityLabel,\n ['aria-label']: ariaLabel,\n accessibilityState,\n ['aria-busy']: ariaBusy,\n ['aria-checked']: ariaChecked,\n ['aria-disabled']: ariaDisabled,\n ['aria-expanded']: ariaExpanded,\n ['aria-selected']: ariaSelected,\n accessible,\n ...restProperties\n } = props;\n\n // Merge label props: prefer the aria-label if defined.\n const normalizedLabel = ariaLabel ?? accessibilityLabel;\n\n // Merge the accessibilityState with any provided ARIA properties.\n let normalizedState = accessibilityState;\n if (ariaBusy != null || ariaChecked != null || ariaDisabled != null || ariaExpanded != null || ariaSelected != null) {\n normalizedState =\n normalizedState == null\n ? {\n busy: ariaBusy,\n checked: ariaChecked,\n disabled: ariaDisabled,\n expanded: ariaExpanded,\n selected: ariaSelected,\n }\n : {\n busy: ariaBusy ?? normalizedState.busy,\n checked: ariaChecked ?? normalizedState.checked,\n disabled: ariaDisabled ?? normalizedState.disabled,\n expanded: ariaExpanded ?? normalizedState.expanded,\n selected: ariaSelected ?? normalizedState.selected,\n };\n }\n\n // For the accessible prop, if not provided, default to `true`\n const normalizedAccessible = accessible == null ? true : accessible;\n\n return {\n ...restProperties,\n accessibilityLabel: normalizedLabel,\n accessibilityState: normalizedState,\n accessible: normalizedAccessible,\n };\n}\n\nexport * from './types';\nexport * from './utils/constants';\nexport * from './components/native-text';\nexport * from './components/native-view';\n"],"names":["reactNative","isWeb","StyleSheet"],"mappings":"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"}
@@ -1,7 +1,15 @@
1
1
  import { TextStyle, TextProps } from 'react-native';
2
2
 
3
+ /**
4
+ * Recursive style prop shape accepted by runtime style helpers.
5
+ *
6
+ * @template T - Style object type.
7
+ */
3
8
  type GenericStyleProp<T> = null | void | T | false | '' | ReadonlyArray<GenericStyleProp<T>>;
4
9
 
10
+ /**
11
+ * Maps CSS-like `userSelect` values to React Native's `selectable` prop.
12
+ */
5
13
  declare const userSelectToSelectableMap: {
6
14
  auto: boolean;
7
15
  text: boolean;
@@ -9,6 +17,9 @@ declare const userSelectToSelectableMap: {
9
17
  contain: boolean;
10
18
  all: boolean;
11
19
  };
20
+ /**
21
+ * Maps CSS-like `verticalAlign` values to React Native's `textAlignVertical`.
22
+ */
12
23
  declare const verticalAlignToTextAlignVerticalMap: {
13
24
  auto: string;
14
25
  top: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.js","sources":["../../src/runtime/utils/constants.ts","../../src/runtime/index.web.ts"],"sourcesContent":["// Maps the `userSelect` prop to the native `selectable` prop\nexport const userSelectToSelectableMap = {\n auto: true,\n text: true,\n none: false,\n contain: true,\n all: true,\n};\n\n// Maps the `verticalAlign` prop to the native `textAlignVertical` prop\nexport const verticalAlignToTextAlignVerticalMap = {\n auto: 'auto',\n top: 'top',\n bottom: 'bottom',\n middle: 'center',\n};\n","// This is a dummy file to ensure that nothing breaks when using the runtime in a web environment.\n\nimport { TextProps, TextStyle } from 'react-native';\nimport { GenericStyleProp } from './types';\n\nexport const processTextStyle = (style: GenericStyleProp<TextStyle>) => ({ style }) as Partial<TextProps>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function processAccessibilityProps(props: Record<string, any>): Record<string, any> {\n return props;\n}\n\nexport * from './types';\nexport * from './utils/constants';\n\n// On Web, the native components are not available, so we use the standard components that'll be replaced by their DOM\n// equivalents by react-native-web.\n/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\nexport const NativeText = require('react-native').Text;\nexport const NativeView = require('react-native').View;\n/* eslint-enable @typescript-eslint/no-require-imports,unicorn/prefer-module */\n"],"names":[],"mappings":";;AACO,MAAM,yBAAA,GAA4B;AAAA,EACvC,IAAA,EAAM,IAAA;AAAA,EACN,IAAA,EAAM,IAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,OAAA,EAAS,IAAA;AAAA,EACT,GAAA,EAAK;AACP;AAGO,MAAM,mCAAA,GAAsC;AAAA,EACjD,IAAA,EAAM,MAAA;AAAA,EACN,GAAA,EAAK,KAAA;AAAA,EACL,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ;AACV;;ACVO,MAAM,gBAAA,GAAmB,CAAC,KAAA,MAAwC,EAAE,KAAA,EAAM;AAG1E,SAAS,0BAA0B,KAAA,EAAiD;AACzF,EAAA,OAAO,KAAA;AACT;AAQO,MAAM,UAAA,GAAa,OAAA,CAAQ,cAAc,CAAA,CAAE;AAC3C,MAAM,UAAA,GAAa,OAAA,CAAQ,cAAc,CAAA,CAAE;;;;;;;;;"}
1
+ {"version":3,"file":"index.web.js","sources":["../../src/runtime/utils/constants.ts","../../src/runtime/index.web.ts"],"sourcesContent":["/**\n * Maps CSS-like `userSelect` values to React Native's `selectable` prop.\n */\nexport const userSelectToSelectableMap = {\n auto: true,\n text: true,\n none: false,\n contain: true,\n all: true,\n};\n\n/**\n * Maps CSS-like `verticalAlign` values to React Native's `textAlignVertical`.\n */\nexport const verticalAlignToTextAlignVerticalMap = {\n auto: 'auto',\n top: 'top',\n bottom: 'bottom',\n middle: 'center',\n};\n","// This is a dummy file to ensure that nothing breaks when using the runtime in a web environment.\n\nimport { TextProps, TextStyle } from 'react-native';\nimport { GenericStyleProp } from './types';\n\nexport const processTextStyle = (style: GenericStyleProp<TextStyle>) => ({ style }) as Partial<TextProps>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function processAccessibilityProps(props: Record<string, any>): Record<string, any> {\n return props;\n}\n\nexport * from './types';\nexport * from './utils/constants';\n\n// On Web, the native components are not available, so we use the standard components that'll be replaced by their DOM\n// equivalents by react-native-web.\n/* eslint-disable @typescript-eslint/no-require-imports,unicorn/prefer-module */\nexport const NativeText = require('react-native').Text;\nexport const NativeView = require('react-native').View;\n/* eslint-enable @typescript-eslint/no-require-imports,unicorn/prefer-module */\n"],"names":[],"mappings":";;AAGO,MAAM,yBAAA,GAA4B;AAAA,EACvC,IAAA,EAAM,IAAA;AAAA,EACN,IAAA,EAAM,IAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,OAAA,EAAS,IAAA;AAAA,EACT,GAAA,EAAK;AACP;AAKO,MAAM,mCAAA,GAAsC;AAAA,EACjD,IAAA,EAAM,MAAA;AAAA,EACN,GAAA,EAAK,KAAA;AAAA,EACL,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ;AACV;;ACdO,MAAM,gBAAA,GAAmB,CAAC,KAAA,MAAwC,EAAE,KAAA,EAAM;AAG1E,SAAS,0BAA0B,KAAA,EAAiD;AACzF,EAAA,OAAO,KAAA;AACT;AAQO,MAAM,UAAA,GAAa,OAAA,CAAQ,cAAc,CAAA,CAAE;AAC3C,MAAM,UAAA,GAAa,OAAA,CAAQ,cAAc,CAAA,CAAE;;;;;;;;;"}
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.7.0",
4
+ "version": "1.1.0",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -109,7 +109,7 @@
109
109
  },
110
110
  "peerDependencies": {
111
111
  "react": "*",
112
- "react-native": "*"
112
+ "react-native": ">=0.83.0"
113
113
  },
114
114
  "release-it": {
115
115
  "git": {
@@ -118,6 +118,7 @@
118
118
  },
119
119
  "npm": {
120
120
  "publish": true,
121
+ "skipChecks": true,
121
122
  "versionArgs": [
122
123
  "--workspaces-update=false"
123
124
  ]
@@ -1,10 +1,17 @@
1
1
  import { declare } from '@babel/helper-plugin-utils';
2
2
  import { textOptimizer } from './optimizers/text';
3
- import { PluginOptions } from './types';
4
- import { log } from './utils/logger';
3
+ import { PluginLogger, PluginOptions } from './types';
4
+ import { createLogger } from './utils/logger';
5
5
  import { viewOptimizer } from './optimizers/view';
6
6
  import { isIgnoredFile } from './utils/common';
7
7
 
8
+ export type { PluginOptimizationOptions, PluginOptions } from './types';
9
+
10
+ type PluginState = {
11
+ opts?: PluginOptions;
12
+ __reactNativeBoostLogger?: PluginLogger;
13
+ };
14
+
8
15
  export default declare((api) => {
9
16
  api.assertVersion(7);
10
17
 
@@ -12,8 +19,10 @@ export default declare((api) => {
12
19
  name: 'react-native-boost',
13
20
  visitor: {
14
21
  JSXOpeningElement(path, state) {
15
- const options = (state.opts ?? {}) as PluginOptions;
16
- const logger = options.verbose ? log : () => {};
22
+ const pluginState = state as PluginState;
23
+ const options = (pluginState.opts ?? {}) as PluginOptions;
24
+ const logger = getOrCreateLogger(pluginState, options);
25
+
17
26
  if (isIgnoredFile(path, options.ignores ?? [])) return;
18
27
  if (options.optimizations?.text !== false) textOptimizer(path, logger);
19
28
  if (options.optimizations?.view !== false) viewOptimizer(path, logger, options);
@@ -21,3 +30,16 @@ export default declare((api) => {
21
30
  },
22
31
  };
23
32
  });
33
+
34
+ function getOrCreateLogger(state: PluginState, options: PluginOptions): PluginLogger {
35
+ if (state.__reactNativeBoostLogger) {
36
+ return state.__reactNativeBoostLogger;
37
+ }
38
+
39
+ state.__reactNativeBoostLogger = createLogger({
40
+ verbose: options.verbose === true,
41
+ silent: options.silent === true,
42
+ });
43
+
44
+ return state.__reactNativeBoostLogger;
45
+ }
@@ -1,12 +1,14 @@
1
1
  import { NodePath, types as t } from '@babel/core';
2
- import { HubFile, Optimizer } from '../../types';
2
+ import { HubFile, Optimizer, PluginLogger } from '../../types';
3
3
  import PluginError from '../../utils/plugin-error';
4
+ import { BailoutCheck, getFirstBailoutReason } from '../../utils/helpers';
4
5
  import {
5
6
  addDefaultProperty,
6
7
  addFileImportHint,
7
8
  buildPropertiesFromAttributes,
8
9
  hasAccessibilityProperty,
9
10
  hasBlacklistedProperty,
11
+ isForcedLine,
10
12
  isIgnoredLine,
11
13
  isValidJSXComponent,
12
14
  isReactNativeImport,
@@ -36,16 +38,48 @@ export const textBlacklistedProperties = new Set([
36
38
  'selectionColor', // TODO: we can use react-native's internal `processColor` to process this at runtime
37
39
  ]);
38
40
 
39
- export const textOptimizer: Optimizer = (path, log = () => {}) => {
40
- if (isIgnoredLine(path)) return;
41
+ export const textOptimizer: Optimizer = (path, logger) => {
41
42
  if (!isValidJSXComponent(path, 'Text')) return;
42
43
  if (!isReactNativeImport(path, 'Text')) return;
43
- if (hasBlacklistedProperty(path, textBlacklistedProperties)) return;
44
- if (hasExpoRouterLinkParentWithAsChild(path)) return;
45
44
 
46
- // Verify that the Text only has string children
47
45
  const parent = path.parent as t.JSXElement;
48
- if (hasInvalidChildren(path, parent)) return;
46
+ const forced = isForcedLine(path);
47
+
48
+ const overridableChecks: BailoutCheck[] = [
49
+ {
50
+ reason: 'contains blacklisted props',
51
+ shouldBail: () => hasBlacklistedProperty(path, textBlacklistedProperties),
52
+ },
53
+ {
54
+ reason: 'is a direct child of expo-router Link with asChild',
55
+ shouldBail: () => hasExpoRouterLinkParentWithAsChild(path),
56
+ },
57
+ {
58
+ reason: 'contains non-string children',
59
+ shouldBail: () => hasInvalidChildren(path, parent),
60
+ },
61
+ ];
62
+
63
+ if (forced) {
64
+ const overriddenReason = getFirstBailoutReason(overridableChecks);
65
+
66
+ if (overriddenReason) {
67
+ logger.forced({ component: 'Text', path, reason: overriddenReason });
68
+ }
69
+ } else {
70
+ const skipReason = getFirstBailoutReason([
71
+ {
72
+ reason: 'line is marked with @boost-ignore',
73
+ shouldBail: () => isIgnoredLine(path),
74
+ },
75
+ ...overridableChecks,
76
+ ]);
77
+
78
+ if (skipReason) {
79
+ logger.skipped({ component: 'Text', path, reason: skipReason });
80
+ return;
81
+ }
82
+ }
49
83
 
50
84
  const hub = path.hub as unknown;
51
85
  const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;
@@ -54,12 +88,13 @@ export const textOptimizer: Optimizer = (path, log = () => {}) => {
54
88
  throw new PluginError('No file found in Babel hub');
55
89
  }
56
90
 
57
- const filename = file.opts?.filename || 'unknown file';
58
- const lineNumber = path.node.loc?.start.line ?? 'unknown line';
59
- log(`Optimizing Text component in ${filename}:${lineNumber}`);
91
+ logger.optimized({
92
+ component: 'Text',
93
+ path,
94
+ });
60
95
 
61
96
  // Process props
62
- fixNegativeNumberOfLines({ path, log });
97
+ fixNegativeNumberOfLines({ path, logger });
63
98
  addDefaultProperty(path, 'allowFontScaling', t.booleanLiteral(true));
64
99
  addDefaultProperty(path, 'ellipsizeMode', t.stringLiteral('tail'));
65
100
  processProps(path, file);
@@ -98,13 +133,7 @@ function hasInvalidChildren(path: NodePath<t.JSXOpeningElement>, parent: t.JSXEl
98
133
  /**
99
134
  * Fixes negative numberOfLines values by setting them to 0.
100
135
  */
101
- function fixNegativeNumberOfLines({
102
- path,
103
- log,
104
- }: {
105
- path: NodePath<t.JSXOpeningElement>;
106
- log: (message: string) => void;
107
- }) {
136
+ function fixNegativeNumberOfLines({ path, logger }: { path: NodePath<t.JSXOpeningElement>; logger: PluginLogger }) {
108
137
  for (const attribute of path.node.attributes) {
109
138
  if (
110
139
  t.isJSXAttribute(attribute) &&
@@ -123,9 +152,11 @@ function fixNegativeNumberOfLines({
123
152
  originalValue = -attribute.value.expression.argument.value;
124
153
  }
125
154
  if (originalValue !== undefined && originalValue < 0) {
126
- log(
127
- `Warning: 'numberOfLines' in <Text> must be a non-negative number, received: ${originalValue}. The value will be set to 0.`
128
- );
155
+ logger.warning({
156
+ component: 'Text',
157
+ path,
158
+ message: `'numberOfLines' must be a non-negative number, received: ${originalValue}. The value will be set to 0.`,
159
+ });
129
160
  attribute.value.expression = t.numericLiteral(0);
130
161
  }
131
162
  }
@@ -1,13 +1,16 @@
1
1
  import { types as t } from '@babel/core';
2
2
  import { HubFile, Optimizer } from '../../types';
3
3
  import PluginError from '../../utils/plugin-error';
4
+ import { BailoutCheck, getFirstBailoutReason } from '../../utils/helpers';
4
5
  import {
5
6
  hasBlacklistedProperty,
7
+ isForcedLine,
6
8
  isIgnoredLine,
7
9
  isValidJSXComponent,
8
10
  isReactNativeImport,
9
11
  replaceWithNativeComponent,
10
- hasUnsafeViewAncestor,
12
+ getViewAncestorClassification,
13
+ ViewAncestorClassification,
11
14
  } from '../../utils/common';
12
15
 
13
16
  export const viewBlacklistedProperties = new Set([
@@ -26,14 +29,58 @@ export const viewBlacklistedProperties = new Set([
26
29
  'style', // TODO: process style at runtime
27
30
  ]);
28
31
 
29
- export const viewOptimizer: Optimizer = (path, log = () => {}, options) => {
30
- if (isIgnoredLine(path)) return;
32
+ export const viewOptimizer: Optimizer = (path, logger, options) => {
31
33
  if (!isValidJSXComponent(path, 'View')) return;
32
34
  if (!isReactNativeImport(path, 'View')) return;
33
- if (hasBlacklistedProperty(path, viewBlacklistedProperties)) return;
34
- if (hasUnsafeViewAncestor(path, options?.dangerouslyOptimizeViewWithUnknownAncestors === true)) return;
35
35
 
36
- // Extract the file from the Babel hub
36
+ let ancestorClassification: ViewAncestorClassification | undefined;
37
+ const getAncestorClassification = () => {
38
+ if (!ancestorClassification) {
39
+ ancestorClassification = getViewAncestorClassification(path);
40
+ }
41
+
42
+ return ancestorClassification;
43
+ };
44
+
45
+ const forced = isForcedLine(path);
46
+
47
+ const overridableChecks: BailoutCheck[] = [
48
+ {
49
+ reason: 'contains blacklisted props',
50
+ shouldBail: () => hasBlacklistedProperty(path, viewBlacklistedProperties),
51
+ },
52
+ {
53
+ reason: 'has Text ancestor',
54
+ shouldBail: () => getAncestorClassification() === 'text',
55
+ },
56
+ {
57
+ reason: 'has unresolved ancestor and dangerous optimization is disabled',
58
+ shouldBail: () =>
59
+ getAncestorClassification() === 'unknown' && options?.dangerouslyOptimizeViewWithUnknownAncestors !== true,
60
+ },
61
+ ];
62
+
63
+ if (forced) {
64
+ const overriddenReason = getFirstBailoutReason(overridableChecks);
65
+
66
+ if (overriddenReason) {
67
+ logger.forced({ component: 'View', path, reason: overriddenReason });
68
+ }
69
+ } else {
70
+ const skipReason = getFirstBailoutReason([
71
+ {
72
+ reason: 'line is marked with @boost-ignore',
73
+ shouldBail: () => isIgnoredLine(path),
74
+ },
75
+ ...overridableChecks,
76
+ ]);
77
+
78
+ if (skipReason) {
79
+ logger.skipped({ component: 'View', path, reason: skipReason });
80
+ return;
81
+ }
82
+ }
83
+
37
84
  const hub = path.hub as unknown;
38
85
  const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;
39
86
 
@@ -41,12 +88,12 @@ export const viewOptimizer: Optimizer = (path, log = () => {}, options) => {
41
88
  throw new PluginError('No file found in Babel hub');
42
89
  }
43
90
 
44
- const filename = file.opts?.filename || 'unknown file';
45
- const lineNumber = path.node.loc?.start.line ?? 'unknown line';
46
- log(`Optimizing View component in ${filename}:${lineNumber}`);
91
+ logger.optimized({
92
+ component: 'View',
93
+ path,
94
+ });
47
95
 
48
96
  const parent = path.parent as t.JSXElement;
49
97
 
50
- // Replace the View component with NativeView
51
98
  replaceWithNativeComponent(path, parent, file, 'NativeView');
52
99
  };
@@ -1,44 +1,84 @@
1
1
  import { NodePath, types as t } from '@babel/core';
2
2
 
3
+ export interface PluginOptimizationOptions {
4
+ /**
5
+ * Whether to optimize the `Text` component.
6
+ * @default true
7
+ */
8
+ text?: boolean;
9
+ /**
10
+ * Whether to optimize the `View` component.
11
+ * @default true
12
+ */
13
+ view?: boolean;
14
+ }
15
+
3
16
  export interface PluginOptions {
4
17
  /**
5
- * Paths to ignore from optimization. Relative to the Babel configuration file.
18
+ * Paths to ignore from optimization.
19
+ *
20
+ * Patterns are resolved from Babel's current working directory.
21
+ * In nested monorepo apps, parent segments may be needed, for example `../../node_modules/**`.
22
+ * @default []
6
23
  */
7
24
  ignores?: string[];
8
25
  /**
9
- * Whether or not to log optimized files to the console.
26
+ * Enables verbose logging.
27
+ *
28
+ * With `silent: false`, optimized components are logged by default.
29
+ * When enabled, skipped components and their skip reasons are also logged.
10
30
  * @default false
11
31
  */
12
32
  verbose?: boolean;
13
33
  /**
14
- * The optimizations to apply to the plugin.
34
+ * Disables all plugin logs.
35
+ *
36
+ * When set to `true`, this overrides `verbose`.
37
+ * @default false
15
38
  */
16
- optimizations?: {
17
- /**
18
- * Whether or not to optimize the Text component.
19
- * @default true
20
- */
21
- text?: boolean;
22
- /**
23
- * Whether or not to optimize the View component.
24
- * @default true
25
- */
26
- view?: boolean;
27
- };
39
+ silent?: boolean;
40
+ /**
41
+ * Toggle individual optimizers.
42
+ *
43
+ * If omitted, all available optimizers are enabled.
44
+ */
45
+ optimizations?: PluginOptimizationOptions;
28
46
  /**
29
47
  * Opt-in flag that allows View optimization when ancestor components cannot be statically resolved.
30
48
  *
31
- * This may introduce behavioral changes when unresolved ancestors render react-native Text wrappers.
49
+ * This increases optimization coverage, but may introduce behavioral differences
50
+ * when unresolved ancestors render React Native `Text` wrappers.
51
+ * Prefer targeted ignores first, and enable this only after verifying affected screens.
32
52
  * @default false
33
53
  */
34
54
  dangerouslyOptimizeViewWithUnknownAncestors?: boolean;
35
55
  }
36
56
 
37
- export type Optimizer = (
38
- path: NodePath<t.JSXOpeningElement>,
39
- log?: (message: string) => void,
40
- options?: PluginOptions
41
- ) => void;
57
+ export type OptimizableComponent = 'Text' | 'View';
58
+
59
+ export interface OptimizationLogPayload {
60
+ component: OptimizableComponent;
61
+ path: NodePath<t.JSXOpeningElement>;
62
+ }
63
+
64
+ export interface SkippedOptimizationLogPayload extends OptimizationLogPayload {
65
+ reason: string;
66
+ }
67
+
68
+ export interface WarningLogPayload {
69
+ message: string;
70
+ component?: OptimizableComponent;
71
+ path?: NodePath<t.JSXOpeningElement>;
72
+ }
73
+
74
+ export interface PluginLogger {
75
+ optimized: (payload: OptimizationLogPayload) => void;
76
+ skipped: (payload: SkippedOptimizationLogPayload) => void;
77
+ forced: (payload: SkippedOptimizationLogPayload) => void;
78
+ warning: (payload: WarningLogPayload) => void;
79
+ }
80
+
81
+ export type Optimizer = (path: NodePath<t.JSXOpeningElement>, logger: PluginLogger, options?: PluginOptions) => void;
42
82
 
43
83
  export type HubFile = t.File & {
44
84
  opts: {