react-native-boost 0.4.1 → 0.5.1

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
@@ -23,13 +23,11 @@ The documentation is available at [react-native-boost.oss.kuatsu.de](https://rea
23
23
  The example app in the `apps/example` directory is a benchmark for the performance of the plugin.
24
24
 
25
25
  <div align="center">
26
- <img src="https://react-native-boost.oss.kuatsu.de/assets/images/benchmark-d60fdc78daeb03798d41c220bfed1f64.png" width="500" />
27
- <p>
28
- <b>1,000 Text components</b>: Render time of 1,000 Text components with and without React Native Boost.<br/>
29
- Measured in milliseconds on an iPhone 16 Pro in development mode and using New Architecture, lower is better.
30
- </p>
26
+ <img src="./apps/docs/docs/introduction/img/benchmark-ios.png" width="500" />
31
27
  </div>
32
28
 
29
+ More benchmarks are available in the [docs](https://react-native-boost.oss.kuatsu.de/docs/introduction/benchmarks).
30
+
33
31
  ## Installation
34
32
 
35
33
  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.
@@ -1,4 +1,4 @@
1
- import { flattenStyle } from 'react-native/Libraries/StyleSheet/flattenStyle';
1
+ import flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle';
2
2
 
3
3
  const propsCache = /* @__PURE__ */ new WeakMap();
4
4
  function flattenTextStyle(style) {
@@ -36,6 +36,44 @@ const verticalAlignToTextAlignVerticalMap = {
36
36
  bottom: "bottom",
37
37
  middle: "center"
38
38
  };
39
+ function normalizeAccessibilityProperties(props) {
40
+ const {
41
+ accessibilityLabel,
42
+ ["aria-label"]: ariaLabel,
43
+ accessibilityState,
44
+ ["aria-busy"]: ariaBusy,
45
+ ["aria-checked"]: ariaChecked,
46
+ ["aria-disabled"]: ariaDisabled,
47
+ ["aria-expanded"]: ariaExpanded,
48
+ ["aria-selected"]: ariaSelected,
49
+ accessible,
50
+ ...restProperties
51
+ } = props;
52
+ const normalizedLabel = ariaLabel != null ? ariaLabel : accessibilityLabel;
53
+ let normalizedState = accessibilityState;
54
+ if (ariaBusy != null || ariaChecked != null || ariaDisabled != null || ariaExpanded != null || ariaSelected != null) {
55
+ normalizedState = normalizedState == null ? {
56
+ busy: ariaBusy,
57
+ checked: ariaChecked,
58
+ disabled: ariaDisabled,
59
+ expanded: ariaExpanded,
60
+ selected: ariaSelected
61
+ } : {
62
+ busy: ariaBusy != null ? ariaBusy : normalizedState.busy,
63
+ checked: ariaChecked != null ? ariaChecked : normalizedState.checked,
64
+ disabled: ariaDisabled != null ? ariaDisabled : normalizedState.disabled,
65
+ expanded: ariaExpanded != null ? ariaExpanded : normalizedState.expanded,
66
+ selected: ariaSelected != null ? ariaSelected : normalizedState.selected
67
+ };
68
+ }
69
+ const normalizedAccessible = accessible == null ? true : accessible;
70
+ return {
71
+ ...restProperties,
72
+ accessibilityLabel: normalizedLabel,
73
+ accessibilityState: normalizedState,
74
+ accessible: normalizedAccessible
75
+ };
76
+ }
39
77
 
40
- export { flattenTextStyle, userSelectToSelectableMap, verticalAlignToTextAlignVerticalMap };
78
+ export { flattenTextStyle, normalizeAccessibilityProperties, userSelectToSelectableMap, verticalAlignToTextAlignVerticalMap };
41
79
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../../src/runtime/index.ts"],"sourcesContent":["import { TextStyle } from 'react-native';\nimport { flattenStyle } from 'react-native/Libraries/StyleSheet/flattenStyle';\nimport { GenericStyleProp } from './types';\n\nconst propsCache = new WeakMap();\n\nexport function flattenTextStyle(style: GenericStyleProp<TextStyle>) {\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 = flattenStyle(style);\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// 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\nexport * from './types';\n"],"names":[],"mappings":";;AAIA,MAAM,UAAA,uBAAiB,OAAQ,EAAA;AAExB,SAAS,iBAAiB,KAAoC,EAAA;AACnE,EAAI,IAAA,CAAC,KAAO,EAAA,OAAO,EAAC;AAGpB,EAAI,IAAA,KAAA,GAAQ,UAAW,CAAA,GAAA,CAAI,KAAK,CAAA;AAChC,EAAA,IAAI,OAAc,OAAA,KAAA;AAElB,EAAA,KAAA,GAAQ,EAAC;AACT,EAAW,UAAA,CAAA,GAAA,CAAI,OAAO,KAAK,CAAA;AAE3B,EAAA,KAAA,GAAQ,aAAa,KAAK,CAAA;AAE1B,EAAI,IAAA,CAAC,KAAO,EAAA,OAAO,EAAC;AAEpB,EAAI,IAAA,QAAO,KAAO,IAAA,IAAA,GAAA,MAAA,GAAA,KAAA,CAAA,UAAA,CAAA,KAAe,QAAU,EAAA;AACzC,IAAM,KAAA,CAAA,UAAA,GAAa,KAAM,CAAA,UAAA,CAAW,QAAS,EAAA;AAAA;AAG/C,EAAI,IAAA,CAAA,KAAA,IAAA,IAAA,GAAA,MAAA,GAAA,KAAA,CAAO,eAAc,IAAM,EAAA;AAC7B,IAAM,KAAA,CAAA,UAAA,GAAa,yBAA0B,CAAA,KAAA,CAAM,UAAU,CAAA;AAC7D,IAAA,OAAO,KAAM,CAAA,UAAA;AAAA;AAGf,EAAI,IAAA,CAAA,KAAA,IAAA,IAAA,GAAA,MAAA,GAAA,KAAA,CAAO,kBAAiB,IAAM,EAAA;AAChC,IAAM,KAAA,CAAA,iBAAA,GAAoB,mCACxB,CAAA,KAAA,CAAM,aACR,CAAA;AACA,IAAA,OAAO,KAAM,CAAA,aAAA;AAAA;AAGf,EAAA,KAAA,CAAM,KAAQ,GAAA,KAAA;AACd,EAAO,OAAA,KAAA;AACT;AAGO,MAAM,yBAA4B,GAAA;AAAA,EACvC,IAAM,EAAA,IAAA;AAAA,EACN,IAAM,EAAA,IAAA;AAAA,EACN,IAAM,EAAA,KAAA;AAAA,EACN,OAAS,EAAA,IAAA;AAAA,EACT,GAAK,EAAA;AACP;AAGO,MAAM,mCAAsC,GAAA;AAAA,EACjD,IAAM,EAAA,MAAA;AAAA,EACN,GAAK,EAAA,KAAA;AAAA,EACL,MAAQ,EAAA,QAAA;AAAA,EACR,MAAQ,EAAA;AACV;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":["../../src/runtime/index.ts"],"sourcesContent":["import { TextStyle } from 'react-native';\nimport flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle';\nimport { GenericStyleProp } from './types';\n\nconst propsCache = new WeakMap();\n\nexport function flattenTextStyle(style: GenericStyleProp<TextStyle>) {\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 = flattenStyle(style);\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// 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\n/**\n * Normalizes accessibility props.\n *\n * @param props - The props to normalize.\n * @returns The normalized props.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function normalizeAccessibilityProperties(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';\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -16,5 +16,12 @@ declare const verticalAlignToTextAlignVerticalMap: {
16
16
  bottom: string;
17
17
  middle: string;
18
18
  };
19
+ /**
20
+ * Normalizes accessibility props.
21
+ *
22
+ * @param props - The props to normalize.
23
+ * @returns The normalized props.
24
+ */
25
+ declare function normalizeAccessibilityProperties(props: Record<string, any>): Record<string, any>;
19
26
 
20
- export { type GenericStyleProp, flattenTextStyle, userSelectToSelectableMap, verticalAlignToTextAlignVerticalMap };
27
+ export { type GenericStyleProp, flattenTextStyle, normalizeAccessibilityProperties, userSelectToSelectableMap, verticalAlignToTextAlignVerticalMap };
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ function flattenTextStyle(style) {
9
9
  if (props) return props;
10
10
  props = {};
11
11
  propsCache.set(style, props);
12
- style = flattenStyle.flattenStyle(style);
12
+ style = flattenStyle(style);
13
13
  if (!style) return {};
14
14
  if (typeof (style == null ? void 0 : style.fontWeight) === "number") {
15
15
  style.fontWeight = style.fontWeight.toString();
@@ -38,8 +38,47 @@ const verticalAlignToTextAlignVerticalMap = {
38
38
  bottom: "bottom",
39
39
  middle: "center"
40
40
  };
41
+ function normalizeAccessibilityProperties(props) {
42
+ const {
43
+ accessibilityLabel,
44
+ ["aria-label"]: ariaLabel,
45
+ accessibilityState,
46
+ ["aria-busy"]: ariaBusy,
47
+ ["aria-checked"]: ariaChecked,
48
+ ["aria-disabled"]: ariaDisabled,
49
+ ["aria-expanded"]: ariaExpanded,
50
+ ["aria-selected"]: ariaSelected,
51
+ accessible,
52
+ ...restProperties
53
+ } = props;
54
+ const normalizedLabel = ariaLabel != null ? ariaLabel : accessibilityLabel;
55
+ let normalizedState = accessibilityState;
56
+ if (ariaBusy != null || ariaChecked != null || ariaDisabled != null || ariaExpanded != null || ariaSelected != null) {
57
+ normalizedState = normalizedState == null ? {
58
+ busy: ariaBusy,
59
+ checked: ariaChecked,
60
+ disabled: ariaDisabled,
61
+ expanded: ariaExpanded,
62
+ selected: ariaSelected
63
+ } : {
64
+ busy: ariaBusy != null ? ariaBusy : normalizedState.busy,
65
+ checked: ariaChecked != null ? ariaChecked : normalizedState.checked,
66
+ disabled: ariaDisabled != null ? ariaDisabled : normalizedState.disabled,
67
+ expanded: ariaExpanded != null ? ariaExpanded : normalizedState.expanded,
68
+ selected: ariaSelected != null ? ariaSelected : normalizedState.selected
69
+ };
70
+ }
71
+ const normalizedAccessible = accessible == null ? true : accessible;
72
+ return {
73
+ ...restProperties,
74
+ accessibilityLabel: normalizedLabel,
75
+ accessibilityState: normalizedState,
76
+ accessible: normalizedAccessible
77
+ };
78
+ }
41
79
 
42
80
  exports.flattenTextStyle = flattenTextStyle;
81
+ exports.normalizeAccessibilityProperties = normalizeAccessibilityProperties;
43
82
  exports.userSelectToSelectableMap = userSelectToSelectableMap;
44
83
  exports.verticalAlignToTextAlignVerticalMap = verticalAlignToTextAlignVerticalMap;
45
84
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/runtime/index.ts"],"sourcesContent":["import { TextStyle } from 'react-native';\nimport { flattenStyle } from 'react-native/Libraries/StyleSheet/flattenStyle';\nimport { GenericStyleProp } from './types';\n\nconst propsCache = new WeakMap();\n\nexport function flattenTextStyle(style: GenericStyleProp<TextStyle>) {\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 = flattenStyle(style);\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// 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\nexport * from './types';\n"],"names":["flattenStyle"],"mappings":";;;;AAIA,MAAM,UAAA,uBAAiB,OAAQ,EAAA;AAExB,SAAS,iBAAiB,KAAoC,EAAA;AACnE,EAAI,IAAA,CAAC,KAAO,EAAA,OAAO,EAAC;AAGpB,EAAI,IAAA,KAAA,GAAQ,UAAW,CAAA,GAAA,CAAI,KAAK,CAAA;AAChC,EAAA,IAAI,OAAc,OAAA,KAAA;AAElB,EAAA,KAAA,GAAQ,EAAC;AACT,EAAW,UAAA,CAAA,GAAA,CAAI,OAAO,KAAK,CAAA;AAE3B,EAAA,KAAA,GAAQA,0BAAa,KAAK,CAAA;AAE1B,EAAI,IAAA,CAAC,KAAO,EAAA,OAAO,EAAC;AAEpB,EAAI,IAAA,QAAO,KAAO,IAAA,IAAA,GAAA,MAAA,GAAA,KAAA,CAAA,UAAA,CAAA,KAAe,QAAU,EAAA;AACzC,IAAM,KAAA,CAAA,UAAA,GAAa,KAAM,CAAA,UAAA,CAAW,QAAS,EAAA;AAAA;AAG/C,EAAI,IAAA,CAAA,KAAA,IAAA,IAAA,GAAA,MAAA,GAAA,KAAA,CAAO,eAAc,IAAM,EAAA;AAC7B,IAAM,KAAA,CAAA,UAAA,GAAa,yBAA0B,CAAA,KAAA,CAAM,UAAU,CAAA;AAC7D,IAAA,OAAO,KAAM,CAAA,UAAA;AAAA;AAGf,EAAI,IAAA,CAAA,KAAA,IAAA,IAAA,GAAA,MAAA,GAAA,KAAA,CAAO,kBAAiB,IAAM,EAAA;AAChC,IAAM,KAAA,CAAA,iBAAA,GAAoB,mCACxB,CAAA,KAAA,CAAM,aACR,CAAA;AACA,IAAA,OAAO,KAAM,CAAA,aAAA;AAAA;AAGf,EAAA,KAAA,CAAM,KAAQ,GAAA,KAAA;AACd,EAAO,OAAA,KAAA;AACT;AAGO,MAAM,yBAA4B,GAAA;AAAA,EACvC,IAAM,EAAA,IAAA;AAAA,EACN,IAAM,EAAA,IAAA;AAAA,EACN,IAAM,EAAA,KAAA;AAAA,EACN,OAAS,EAAA,IAAA;AAAA,EACT,GAAK,EAAA;AACP;AAGO,MAAM,mCAAsC,GAAA;AAAA,EACjD,IAAM,EAAA,MAAA;AAAA,EACN,GAAK,EAAA,KAAA;AAAA,EACL,MAAQ,EAAA,QAAA;AAAA,EACR,MAAQ,EAAA;AACV;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../src/runtime/index.ts"],"sourcesContent":["import { TextStyle } from 'react-native';\nimport flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle';\nimport { GenericStyleProp } from './types';\n\nconst propsCache = new WeakMap();\n\nexport function flattenTextStyle(style: GenericStyleProp<TextStyle>) {\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 = flattenStyle(style);\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// 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\n/**\n * Normalizes accessibility props.\n *\n * @param props - The props to normalize.\n * @returns The normalized props.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function normalizeAccessibilityProperties(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';\n"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { declare } from '@babel/helper-plugin-utils';
2
2
  import { types } from '@babel/core';
3
- import { addNamed, addDefault } from '@babel/helper-module-imports';
4
3
  import { minimatch } from 'minimatch';
5
4
  import path from 'node:path';
5
+ import { addDefault, addNamed } from '@babel/helper-module-imports';
6
6
 
7
7
  class PluginError extends Error {
8
8
  constructor(message) {
@@ -16,6 +16,21 @@ const ensureArray = (value) => {
16
16
  return [value];
17
17
  };
18
18
 
19
+ function addFileImportHint({
20
+ file,
21
+ nameHint,
22
+ path: path2,
23
+ importName,
24
+ moduleName,
25
+ importType = "named"
26
+ }) {
27
+ var _a;
28
+ if (!((_a = file.__hasImports) == null ? void 0 : _a[nameHint])) {
29
+ file.__hasImports = file.__hasImports || {};
30
+ file.__hasImports[nameHint] = importType === "default" ? addDefault(path2, moduleName, { nameHint }) : addNamed(path2, importName, moduleName, { nameHint });
31
+ }
32
+ return file.__hasImports[nameHint];
33
+ }
19
34
  const isIgnoredFile = (p, ignores) => {
20
35
  const hub = p.hub;
21
36
  const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
@@ -102,18 +117,85 @@ const hasBlacklistedProperty = (path2, blacklist) => {
102
117
  return false;
103
118
  });
104
119
  };
105
-
106
- const textBlacklistedProperties = /* @__PURE__ */ new Set([
107
- "accessible",
120
+ const buildPropertiesFromAttributes = (attributes) => {
121
+ const arguments_ = [];
122
+ for (const attribute of attributes) {
123
+ if (types.isJSXSpreadAttribute(attribute)) {
124
+ arguments_.push(attribute.argument);
125
+ } else if (types.isJSXAttribute(attribute)) {
126
+ const key = attribute.name.name;
127
+ let value;
128
+ if (!attribute.value) {
129
+ value = types.booleanLiteral(true);
130
+ } else if (types.isStringLiteral(attribute.value)) {
131
+ value = attribute.value;
132
+ } else if (types.isJSXExpressionContainer(attribute.value)) {
133
+ value = types.isJSXEmptyExpression(attribute.value.expression) ? types.booleanLiteral(true) : attribute.value.expression;
134
+ } else {
135
+ value = types.nullLiteral();
136
+ }
137
+ const validIdentifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
138
+ const keyNode = typeof key === "string" && validIdentifierRegex.test(key) ? types.identifier(key) : types.stringLiteral(key.toString());
139
+ arguments_.push(types.objectExpression([types.objectProperty(keyNode, value)]));
140
+ }
141
+ }
142
+ if (arguments_.length === 0) {
143
+ return types.objectExpression([]);
144
+ }
145
+ return types.callExpression(types.memberExpression(types.identifier("Object"), types.identifier("assign")), [
146
+ types.objectExpression([]),
147
+ ...arguments_
148
+ ]);
149
+ };
150
+ const accessibilityProperties = /* @__PURE__ */ new Set([
108
151
  "accessibilityLabel",
152
+ "aria-label",
109
153
  "accessibilityState",
110
- "allowFontScaling",
111
154
  "aria-busy",
112
155
  "aria-checked",
113
156
  "aria-disabled",
114
157
  "aria-expanded",
115
- "aria-label",
116
158
  "aria-selected",
159
+ "accessible"
160
+ ]);
161
+ const hasAccessibilityProperty = (path2, attributes) => {
162
+ for (const attribute of attributes) {
163
+ if (types.isJSXAttribute(attribute)) {
164
+ const key = attribute.name.name;
165
+ if (typeof key === "string" && accessibilityProperties.has(key)) {
166
+ return true;
167
+ }
168
+ } else if (types.isJSXSpreadAttribute(attribute)) {
169
+ if (types.isObjectExpression(attribute.argument)) {
170
+ for (const property of attribute.argument.properties) {
171
+ if (types.isObjectProperty(property) && types.isIdentifier(property.key) && accessibilityProperties.has(property.key.name)) {
172
+ return true;
173
+ }
174
+ }
175
+ } else if (types.isIdentifier(attribute.argument)) {
176
+ const binding = path2.scope.getBinding(attribute.argument.name);
177
+ if (binding && types.isVariableDeclarator(binding.path.node)) {
178
+ const declarator = binding.path.node;
179
+ if (declarator.init && types.isObjectExpression(declarator.init)) {
180
+ for (const property of declarator.init.properties) {
181
+ if (types.isObjectProperty(property) && types.isIdentifier(property.key) && accessibilityProperties.has(property.key.name)) {
182
+ return true;
183
+ }
184
+ }
185
+ continue;
186
+ }
187
+ }
188
+ return true;
189
+ } else {
190
+ return true;
191
+ }
192
+ }
193
+ }
194
+ return false;
195
+ };
196
+
197
+ const textBlacklistedProperties = /* @__PURE__ */ new Set([
198
+ "allowFontScaling",
117
199
  "ellipsizeMode",
118
200
  "id",
119
201
  "nativeID",
@@ -163,14 +245,72 @@ const textOptimizer = (path, log = () => {
163
245
  const lineNumber = (_c = (_b = path.node.loc) == null ? void 0 : _b.start.line) != null ? _c : "unknown line";
164
246
  log(`Optimizing Text component in ${filename}:${lineNumber}`);
165
247
  fixNegativeNumberOfLines({ path, log });
166
- optimizeStyleTag({ path, file });
167
- if (!file.__hasImports) {
168
- file.__hasImports = {};
248
+ const originalAttributes = [...path.node.attributes];
249
+ let styleAttribute, styleExpr;
250
+ for (const attribute of originalAttributes) {
251
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "style" })) {
252
+ styleAttribute = attribute;
253
+ break;
254
+ }
255
+ }
256
+ if (styleAttribute && styleAttribute.value && types.isJSXExpressionContainer(styleAttribute.value) && !types.isJSXEmptyExpression(styleAttribute.value.expression)) {
257
+ styleExpr = styleAttribute.value.expression;
169
258
  }
170
- if (!file.__hasImports.NativeText) {
171
- file.__hasImports.NativeText = addNamed(path, "NativeText", "react-native/Libraries/Text/TextNativeComponent");
259
+ const hasA11y = hasAccessibilityProperty(path, originalAttributes);
260
+ if (styleExpr && hasA11y) {
261
+ const accessibilityAttributes = originalAttributes.filter((attribute) => {
262
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "style" })) {
263
+ return false;
264
+ }
265
+ return true;
266
+ });
267
+ const normalizeIdentifier = addFileImportHint({
268
+ file,
269
+ nameHint: "normalizeAccessibilityProps",
270
+ path,
271
+ importName: "normalizeAccessibilityProps",
272
+ moduleName: "react-native-boost"
273
+ });
274
+ const accessibilityObject = buildPropertiesFromAttributes(accessibilityAttributes);
275
+ const accessibilityExpr = types.callExpression(types.identifier(normalizeIdentifier.name), [accessibilityObject]);
276
+ const flattenIdentifier = addFileImportHint({
277
+ file,
278
+ nameHint: "flattenTextStyle",
279
+ path,
280
+ importName: "flattenTextStyle",
281
+ moduleName: "react-native-boost"
282
+ });
283
+ const flattenedStyleExpr = types.callExpression(types.identifier(flattenIdentifier.name), [styleExpr]);
284
+ path.node.attributes = [types.jsxSpreadAttribute(accessibilityExpr), types.jsxSpreadAttribute(flattenedStyleExpr)];
285
+ } else if (styleExpr) {
286
+ const flattenIdentifier = addFileImportHint({
287
+ file,
288
+ nameHint: "flattenTextStyle",
289
+ path,
290
+ importName: "flattenTextStyle",
291
+ moduleName: "react-native-boost"
292
+ });
293
+ const flattened = types.callExpression(types.identifier(flattenIdentifier.name), [styleExpr]);
294
+ path.node.attributes = [types.jsxSpreadAttribute(flattened)];
295
+ } else if (hasA11y) {
296
+ const normalizeIdentifier = addFileImportHint({
297
+ file,
298
+ nameHint: "normalizeAccessibilityProps",
299
+ path,
300
+ importName: "normalizeAccessibilityProps",
301
+ moduleName: "react-native-boost"
302
+ });
303
+ const propsObject = buildPropertiesFromAttributes(originalAttributes);
304
+ const normalized = types.callExpression(types.identifier(normalizeIdentifier.name), [propsObject]);
305
+ path.node.attributes = [types.jsxSpreadAttribute(normalized)];
172
306
  }
173
- const nativeTextIdentifier = file.__hasImports.NativeText;
307
+ const nativeTextIdentifier = addFileImportHint({
308
+ file,
309
+ nameHint: "NativeText",
310
+ path,
311
+ importName: "NativeText",
312
+ moduleName: "react-native/Libraries/Text/TextNativeComponent"
313
+ });
174
314
  path.node.name.name = nativeTextIdentifier.name;
175
315
  if (!path.node.selfClosing && parent.closingElement && types.isJSXIdentifier(parent.closingElement.name) && parent.closingElement.name.name === "Text") {
176
316
  parent.closingElement.name.name = nativeTextIdentifier.name;
@@ -180,13 +320,14 @@ function hasOnlyStringChildren(path, node) {
180
320
  return node.children.every((child) => isStringNode(path, child));
181
321
  }
182
322
  function isStringNode(path, child) {
183
- if (types.isJSXText(child)) return true;
323
+ if (types.isJSXText(child) || types.isStringLiteral(child)) return true;
184
324
  if (types.isJSXExpressionContainer(child)) {
185
325
  const expression = child.expression;
186
326
  if (types.isIdentifier(expression)) {
187
327
  const binding = path.scope.getBinding(expression.name);
188
328
  return binding ? types.isStringLiteral(binding.path.node) : false;
189
329
  }
330
+ if (types.isStringLiteral(expression)) return true;
190
331
  }
191
332
  return false;
192
333
  }
@@ -211,33 +352,17 @@ function fixNegativeNumberOfLines({
211
352
  }
212
353
  }
213
354
  }
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
355
  function hasInvalidChildren(path) {
234
356
  for (const attribute of path.node.attributes) {
235
- if (types.isJSXSpreadAttribute(attribute)) return false;
357
+ if (types.isJSXSpreadAttribute(attribute)) continue;
236
358
  if (types.isJSXIdentifier(attribute.name) && attribute.value) {
237
359
  if (attribute.name.name === "children") {
238
- return isStringNode(path, attribute.value);
360
+ if (!isStringNode(path, attribute.value)) {
361
+ return true;
362
+ }
363
+ } else if (textBlacklistedProperties.has(attribute.name.name)) {
364
+ return true;
239
365
  }
240
- return textBlacklistedProperties.has(attribute.name.name);
241
366
  }
242
367
  }
243
368
  return false;
@@ -306,15 +431,14 @@ const viewOptimizer = (path, log = () => {
306
431
  const filename = ((_a = file.opts) == null ? void 0 : _a.filename) || "unknown file";
307
432
  const lineNumber = (_c = (_b = path.node.loc) == null ? void 0 : _b.start.line) != null ? _c : "unknown line";
308
433
  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;
434
+ const viewNativeIdentifier = addFileImportHint({
435
+ file,
436
+ path,
437
+ importName: "ViewNativeComponent",
438
+ moduleName: "react-native/Libraries/Components/View/ViewNativeComponent",
439
+ importType: "default",
440
+ nameHint: "NativeView"
441
+ });
318
442
  path.node.name.name = viewNativeIdentifier.name;
319
443
  if (!path.node.selfClosing && parent.closingElement && types.isJSXIdentifier(parent.closingElement.name) && parent.closingElement.name.name === "View") {
320
444
  parent.closingElement.name.name = viewNativeIdentifier.name;