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 +3 -5
- package/dist/esm/index.mjs +40 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +40 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin/esm/index.mjs +168 -44
- package/dist/plugin/esm/index.mjs.map +1 -1
- package/dist/plugin/index.js +168 -44
- package/dist/plugin/index.js.map +1 -1
- package/package.json +5 -5
- package/src/plugin/optimizers/text/index.ts +99 -46
- package/src/plugin/optimizers/view/index.ts +9 -11
- package/src/plugin/types/index.ts +23 -0
- package/src/plugin/utils/common.ts +154 -1
- package/src/runtime/index.ts +57 -1
- package/src/runtime/types/react-native.d.ts +1 -1
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="
|
|
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.
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
package/dist/esm/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/runtime/index.ts"],"sourcesContent":["import { TextStyle } from 'react-native';\nimport
|
|
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
|
|
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
|
|
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
|
|
107
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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 =
|
|
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))
|
|
357
|
+
if (types.isJSXSpreadAttribute(attribute)) continue;
|
|
236
358
|
if (types.isJSXIdentifier(attribute.name) && attribute.value) {
|
|
237
359
|
if (attribute.name.name === "children") {
|
|
238
|
-
|
|
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
|
-
|
|
310
|
-
file
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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;
|