react-native-boost 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -4
- package/dist/plugin/esm/index.mjs +167 -92
- package/dist/plugin/esm/index.mjs.map +1 -1
- package/dist/plugin/index.d.ts +10 -0
- package/dist/plugin/index.js +167 -92
- package/dist/plugin/index.js.map +1 -1
- package/dist/runtime/esm/index.mjs +16 -4
- package/dist/runtime/esm/index.mjs.map +1 -1
- package/dist/runtime/esm/index.web.mjs +2 -1
- package/dist/runtime/esm/index.web.mjs.map +1 -1
- package/dist/runtime/index.d.ts +16 -3
- package/dist/runtime/index.js +15 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/index.web.d.ts +2 -1
- package/dist/runtime/index.web.js +2 -0
- package/dist/runtime/index.web.js.map +1 -1
- package/package.json +6 -1
- package/src/plugin/index.ts +5 -1
- package/src/plugin/optimizers/text/index.ts +118 -50
- package/src/plugin/optimizers/view/index.ts +37 -41
- package/src/plugin/types/index.ts +18 -1
- package/src/plugin/utils/common/attributes.ts +71 -12
- package/src/plugin/utils/common/validation.ts +52 -32
- package/src/plugin/utils/logger.ts +16 -2
- package/src/runtime/index.ts +39 -5
- package/src/runtime/index.web.ts +5 -0
package/README.md
CHANGED
|
@@ -20,10 +20,22 @@ The documentation is available at [react-native-boost.oss.kuatsu.de](https://rea
|
|
|
20
20
|
The app in the `apps/example` directory serves as a benchmark for the performance of the plugin.
|
|
21
21
|
|
|
22
22
|
<div align="center">
|
|
23
|
-
<
|
|
23
|
+
<picture>
|
|
24
|
+
<source media="(prefers-color-scheme: dark)" srcset="./apps/docs/content/docs/information/img/fps-ios.svg" />
|
|
25
|
+
<img alt="React Native Boost — iOS frame rate vs render load" src="./apps/docs/content/docs/information/img/fps-ios-light.svg" width="640" />
|
|
26
|
+
</picture>
|
|
24
27
|
</div>
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
See the [benchmarks page](https://react-native-boost.oss.kuatsu.de/docs/information/benchmarks) for Android results and the full methodology.
|
|
30
|
+
|
|
31
|
+
## Compatibility
|
|
32
|
+
|
|
33
|
+
| `react-native-boost` | React Native |
|
|
34
|
+
| -------------------- | ---------------- |
|
|
35
|
+
| `0.x` | All versions[^1] |
|
|
36
|
+
| `1.x` | `>=0.83` |
|
|
37
|
+
|
|
38
|
+
[^1]: Starting from React Native `0.80`, `react-native-boost@0` prints import deprecation warnings.
|
|
27
39
|
|
|
28
40
|
## Installation
|
|
29
41
|
|
|
@@ -59,11 +71,11 @@ yarn start --clear
|
|
|
59
71
|
|
|
60
72
|
That's it! No imports in your code, rebuilding, or anything else is required.
|
|
61
73
|
|
|
62
|
-
Optionally, you can configure the Babel plugin with a few options described in the [documentation](https://react-native-boost.oss.kuatsu.de/docs/
|
|
74
|
+
Optionally, you can configure the Babel plugin with a few options described in the [documentation](https://react-native-boost.oss.kuatsu.de/docs/configuration/configure).
|
|
63
75
|
|
|
64
76
|
## How it works
|
|
65
77
|
|
|
66
|
-
A technical rundown of how the plugin works can be found in the [docs](https://react-native-boost.oss.kuatsu.de/docs/
|
|
78
|
+
A technical rundown of how the plugin works can be found in the [docs](https://react-native-boost.oss.kuatsu.de/docs/information/how-it-works).
|
|
67
79
|
|
|
68
80
|
## Contributing
|
|
69
81
|
|
|
@@ -40,17 +40,23 @@ const isIgnoredFile = (path, ignores) => {
|
|
|
40
40
|
}
|
|
41
41
|
return false;
|
|
42
42
|
};
|
|
43
|
+
const isForcedLine = (path) => {
|
|
44
|
+
return hasDecoratorComment(path, "@boost-force");
|
|
45
|
+
};
|
|
43
46
|
const isIgnoredLine = (path) => {
|
|
47
|
+
return hasDecoratorComment(path, "@boost-ignore");
|
|
48
|
+
};
|
|
49
|
+
function hasDecoratorComment(path, decorator) {
|
|
44
50
|
var _a, _b, _c;
|
|
45
|
-
if ((_a = path.node.leadingComments) == null ? void 0 : _a.some((comment) => comment.value.includes(
|
|
51
|
+
if ((_a = path.node.leadingComments) == null ? void 0 : _a.some((comment) => comment.value.includes(decorator))) {
|
|
46
52
|
return true;
|
|
47
53
|
}
|
|
48
54
|
const jsxElementPath = path.parentPath;
|
|
49
|
-
if ((_b = jsxElementPath.node.leadingComments) == null ? void 0 : _b.some((comment) => comment.value.includes(
|
|
55
|
+
if ((_b = jsxElementPath.node.leadingComments) == null ? void 0 : _b.some((comment) => comment.value.includes(decorator))) {
|
|
50
56
|
return true;
|
|
51
57
|
}
|
|
52
58
|
const propertyPath = jsxElementPath.parentPath;
|
|
53
|
-
if (propertyPath && propertyPath.isObjectProperty() && ((_c = propertyPath.node.leadingComments) == null ? void 0 : _c.some((comment) => comment.value.includes(
|
|
59
|
+
if (propertyPath && propertyPath.isObjectProperty() && ((_c = propertyPath.node.leadingComments) == null ? void 0 : _c.some((comment) => comment.value.includes(decorator)))) {
|
|
54
60
|
return true;
|
|
55
61
|
}
|
|
56
62
|
if (!jsxElementPath.parentPath) return false;
|
|
@@ -71,18 +77,18 @@ const isIgnoredLine = (path) => {
|
|
|
71
77
|
...expression.node.trailingComments || [],
|
|
72
78
|
...expression.node.innerComments || []
|
|
73
79
|
].map((comment) => comment.value.trim());
|
|
74
|
-
if (comments.some((comment) => comment.includes(
|
|
80
|
+
if (comments.some((comment) => comment.includes(decorator))) {
|
|
75
81
|
return true;
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
|
-
if (sibling.node.leadingComments && sibling.node.leadingComments.some((comment) => comment.value.includes(
|
|
85
|
+
if (sibling.node.leadingComments && sibling.node.leadingComments.some((comment) => comment.value.includes(decorator))) {
|
|
80
86
|
return true;
|
|
81
87
|
}
|
|
82
88
|
break;
|
|
83
89
|
}
|
|
84
90
|
return false;
|
|
85
|
-
}
|
|
91
|
+
}
|
|
86
92
|
const isValidJSXComponent = (path, componentName) => {
|
|
87
93
|
if (!types.isJSXIdentifier(path.node.name)) return false;
|
|
88
94
|
const parent = path.parent;
|
|
@@ -119,10 +125,7 @@ const isReactNativeImport = (path, expectedImportedName) => {
|
|
|
119
125
|
}
|
|
120
126
|
return false;
|
|
121
127
|
};
|
|
122
|
-
const
|
|
123
|
-
return classifyViewAncestors(path);
|
|
124
|
-
};
|
|
125
|
-
function classifyViewAncestors(path) {
|
|
128
|
+
const getAncestorClassification = (path) => {
|
|
126
129
|
const context = {
|
|
127
130
|
componentCache: /* @__PURE__ */ new WeakMap(),
|
|
128
131
|
componentInProgress: /* @__PURE__ */ new WeakSet(),
|
|
@@ -139,7 +142,21 @@ function classifyViewAncestors(path) {
|
|
|
139
142
|
ancestorPath = ancestorPath.parentPath;
|
|
140
143
|
}
|
|
141
144
|
return classification;
|
|
142
|
-
}
|
|
145
|
+
};
|
|
146
|
+
const ancestorBailoutChecks = (path, dangerousOptimizationEnabled) => {
|
|
147
|
+
let classification;
|
|
148
|
+
const classify = () => classification != null ? classification : classification = getAncestorClassification(path);
|
|
149
|
+
return [
|
|
150
|
+
{
|
|
151
|
+
reason: "has Text ancestor",
|
|
152
|
+
shouldBail: () => classify() === "text"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
reason: "has unresolved ancestor and dangerous optimization is disabled",
|
|
156
|
+
shouldBail: () => classify() === "unknown" && !dangerousOptimizationEnabled
|
|
157
|
+
}
|
|
158
|
+
];
|
|
159
|
+
};
|
|
143
160
|
function classifyJSXElementAsAncestor(path, context) {
|
|
144
161
|
const openingElementName = path.node.openingElement.name;
|
|
145
162
|
if (types.isJSXIdentifier(openingElementName)) {
|
|
@@ -667,18 +684,37 @@ function extractSelectableAndUpdateStyle(styleExpr) {
|
|
|
667
684
|
}
|
|
668
685
|
return void 0;
|
|
669
686
|
}
|
|
670
|
-
const
|
|
671
|
-
if (types.
|
|
672
|
-
if (types.
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
687
|
+
const isPrimitiveExpression = (path, expression, resolved = /* @__PURE__ */ new Set()) => {
|
|
688
|
+
if (types.isStringLiteral(expression) || types.isNumericLiteral(expression)) return true;
|
|
689
|
+
if (types.isTemplateLiteral(expression)) return true;
|
|
690
|
+
if (types.isBinaryExpression(expression)) {
|
|
691
|
+
const { left, right } = expression;
|
|
692
|
+
return types.isExpression(left) && isPrimitiveExpression(path, left, resolved) && isPrimitiveExpression(path, right, resolved);
|
|
693
|
+
}
|
|
694
|
+
if (types.isConditionalExpression(expression)) {
|
|
695
|
+
return isPrimitiveExpression(path, expression.consequent, resolved) && isPrimitiveExpression(path, expression.alternate, resolved);
|
|
696
|
+
}
|
|
697
|
+
if (types.isLogicalExpression(expression)) {
|
|
698
|
+
return isPrimitiveExpression(path, expression.left, resolved) && isPrimitiveExpression(path, expression.right, resolved);
|
|
699
|
+
}
|
|
700
|
+
if (types.isIdentifier(expression)) {
|
|
701
|
+
if (resolved.has(expression.name)) return false;
|
|
702
|
+
const binding = path.scope.getBinding(expression.name);
|
|
703
|
+
if (binding && binding.constant && binding.path.node && types.isVariableDeclarator(binding.path.node)) {
|
|
704
|
+
const init = binding.path.node.init;
|
|
705
|
+
return !!init && types.isExpression(init) && isPrimitiveExpression(path, init, new Set(resolved).add(expression.name));
|
|
680
706
|
}
|
|
681
|
-
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
return false;
|
|
710
|
+
};
|
|
711
|
+
const isPrimitiveChild = (path, child) => {
|
|
712
|
+
if (types.isJSXText(child)) return true;
|
|
713
|
+
if (types.isStringLiteral(child)) return true;
|
|
714
|
+
if (types.isJSXExpressionContainer(child)) {
|
|
715
|
+
const { expression } = child;
|
|
716
|
+
if (types.isJSXEmptyExpression(expression)) return false;
|
|
717
|
+
return isPrimitiveExpression(path, expression);
|
|
682
718
|
}
|
|
683
719
|
return false;
|
|
684
720
|
};
|
|
@@ -717,6 +753,10 @@ const replaceWithNativeComponent = (path, parent, file, nativeComponentName) =>
|
|
|
717
753
|
};
|
|
718
754
|
|
|
719
755
|
const textBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
756
|
+
// The `Text` wrapper translates `aria-hidden` into `accessibilityElementsHidden` /
|
|
757
|
+
// `importantForAccessibility`, which `processAccessibilityProps` does not yet handle. Passing it
|
|
758
|
+
// through would drop it, so bail. TODO: handle this in the runtime helper instead.
|
|
759
|
+
"aria-hidden",
|
|
720
760
|
"id",
|
|
721
761
|
"nativeID",
|
|
722
762
|
"onLongPress",
|
|
@@ -734,18 +774,14 @@ const textBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
|
734
774
|
"selectionColor"
|
|
735
775
|
// TODO: we can use react-native's internal `processColor` to process this at runtime
|
|
736
776
|
]);
|
|
737
|
-
const
|
|
777
|
+
const NORMALIZED_PROPERTIES = /* @__PURE__ */ new Set([...ACCESSIBILITY_PROPERTIES, "disabled"]);
|
|
778
|
+
const isNormalizedProperty = (attribute) => types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && NORMALIZED_PROPERTIES.has(attribute.name.name);
|
|
779
|
+
const textOptimizer = (path, logger, options, platform) => {
|
|
738
780
|
if (!isValidJSXComponent(path, "Text")) return;
|
|
781
|
+
if (!isReactNativeImport(path, "Text")) return;
|
|
739
782
|
const parent = path.parent;
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
reason: "line is marked with @boost-ignore",
|
|
743
|
-
shouldBail: () => isIgnoredLine(path)
|
|
744
|
-
},
|
|
745
|
-
{
|
|
746
|
-
reason: "Text is not imported from react-native",
|
|
747
|
-
shouldBail: () => !isReactNativeImport(path, "Text")
|
|
748
|
-
},
|
|
783
|
+
const forced = isForcedLine(path);
|
|
784
|
+
const overridableChecks = [
|
|
749
785
|
{
|
|
750
786
|
reason: "contains blacklisted props",
|
|
751
787
|
shouldBail: () => hasBlacklistedProperty(path, textBlacklistedProperties)
|
|
@@ -754,18 +790,31 @@ const textOptimizer = (path, logger) => {
|
|
|
754
790
|
reason: "is a direct child of expo-router Link with asChild",
|
|
755
791
|
shouldBail: () => hasExpoRouterLinkParentWithAsChild(path)
|
|
756
792
|
},
|
|
793
|
+
// The local children check runs before the ancestor checks because it is cheap and prunes the
|
|
794
|
+
// common nested-element `Text` before the unbounded ancestor walk those checks trigger.
|
|
757
795
|
{
|
|
758
|
-
reason: "contains non-
|
|
796
|
+
reason: "contains non-primitive children",
|
|
759
797
|
shouldBail: () => hasInvalidChildren(path, parent)
|
|
798
|
+
},
|
|
799
|
+
...ancestorBailoutChecks(path, (options == null ? void 0 : options.dangerouslyOptimizeTextWithUnknownAncestors) === true)
|
|
800
|
+
];
|
|
801
|
+
if (forced) {
|
|
802
|
+
const overriddenReason = getFirstBailoutReason(overridableChecks);
|
|
803
|
+
if (overriddenReason) {
|
|
804
|
+
logger.forced({ component: "Text", path, reason: overriddenReason });
|
|
805
|
+
}
|
|
806
|
+
} else {
|
|
807
|
+
const skipReason = getFirstBailoutReason([
|
|
808
|
+
{
|
|
809
|
+
reason: "line is marked with @boost-ignore",
|
|
810
|
+
shouldBail: () => isIgnoredLine(path)
|
|
811
|
+
},
|
|
812
|
+
...overridableChecks
|
|
813
|
+
]);
|
|
814
|
+
if (skipReason) {
|
|
815
|
+
logger.skipped({ component: "Text", path, reason: skipReason });
|
|
816
|
+
return;
|
|
760
817
|
}
|
|
761
|
-
]);
|
|
762
|
-
if (skipReason) {
|
|
763
|
-
logger.skipped({
|
|
764
|
-
component: "Text",
|
|
765
|
-
path,
|
|
766
|
-
reason: skipReason
|
|
767
|
-
});
|
|
768
|
-
return;
|
|
769
818
|
}
|
|
770
819
|
const hub = path.hub;
|
|
771
820
|
const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
|
|
@@ -779,18 +828,18 @@ const textOptimizer = (path, logger) => {
|
|
|
779
828
|
fixNegativeNumberOfLines({ path, logger });
|
|
780
829
|
addDefaultProperty(path, "allowFontScaling", types.booleanLiteral(true));
|
|
781
830
|
addDefaultProperty(path, "ellipsizeMode", types.stringLiteral("tail"));
|
|
782
|
-
processProps(path, file);
|
|
831
|
+
processProps(path, file, platform);
|
|
783
832
|
replaceWithNativeComponent(path, parent, file, "NativeText");
|
|
784
833
|
};
|
|
785
834
|
function hasInvalidChildren(path, parent) {
|
|
786
835
|
for (const attribute of path.node.attributes) {
|
|
787
836
|
if (types.isJSXSpreadAttribute(attribute)) continue;
|
|
788
|
-
if (types.isJSXIdentifier(attribute.name) && attribute.value && // For a "children" attribute, optimization is allowed only if it is a
|
|
789
|
-
attribute.name.name === "children" && !
|
|
837
|
+
if (types.isJSXIdentifier(attribute.name) && attribute.value && // For a "children" attribute, optimization is allowed only if it is a provable primitive
|
|
838
|
+
attribute.name.name === "children" && !isPrimitiveChild(path, attribute.value)) {
|
|
790
839
|
return true;
|
|
791
840
|
}
|
|
792
841
|
}
|
|
793
|
-
return !parent.children.every((child) =>
|
|
842
|
+
return !parent.children.every((child) => isPrimitiveChild(path, child));
|
|
794
843
|
}
|
|
795
844
|
function fixNegativeNumberOfLines({ path, logger }) {
|
|
796
845
|
for (const attribute of path.node.attributes) {
|
|
@@ -812,16 +861,15 @@ function fixNegativeNumberOfLines({ path, logger }) {
|
|
|
812
861
|
}
|
|
813
862
|
}
|
|
814
863
|
}
|
|
815
|
-
function processProps(path, file) {
|
|
864
|
+
function processProps(path, file, platform) {
|
|
816
865
|
const currentAttributes = [...path.node.attributes];
|
|
817
866
|
const { styleExpr, styleAttribute } = extractStyleAttribute(currentAttributes);
|
|
818
|
-
const
|
|
867
|
+
const shouldNormalize = hasAccessibilityProperty(path, currentAttributes) || currentAttributes.some(
|
|
868
|
+
(attribute) => types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name, { name: "disabled" })
|
|
869
|
+
);
|
|
819
870
|
const spreadAttributes = [];
|
|
820
|
-
if (
|
|
821
|
-
const
|
|
822
|
-
if (!types.isJSXAttribute(attribute)) return false;
|
|
823
|
-
return types.isJSXIdentifier(attribute.name) && ACCESSIBILITY_PROPERTIES.has(attribute.name.name);
|
|
824
|
-
});
|
|
871
|
+
if (shouldNormalize) {
|
|
872
|
+
const normalizedAttributes = currentAttributes.filter((attribute) => isNormalizedProperty(attribute));
|
|
825
873
|
const normalizeIdentifier = addFileImportHint({
|
|
826
874
|
file,
|
|
827
875
|
nameHint: "processAccessibilityProps",
|
|
@@ -829,7 +877,7 @@ function processProps(path, file) {
|
|
|
829
877
|
importName: "processAccessibilityProps",
|
|
830
878
|
moduleName: RUNTIME_MODULE_NAME
|
|
831
879
|
});
|
|
832
|
-
const accessibilityObject = buildPropertiesFromAttributes(
|
|
880
|
+
const accessibilityObject = buildPropertiesFromAttributes(normalizedAttributes);
|
|
833
881
|
const accessibilityExpr = types.callExpression(types.identifier(normalizeIdentifier.name), [accessibilityObject]);
|
|
834
882
|
spreadAttributes.push(types.jsxSpreadAttribute(accessibilityExpr));
|
|
835
883
|
}
|
|
@@ -855,26 +903,45 @@ function processProps(path, file) {
|
|
|
855
903
|
const remainingAttributes = [];
|
|
856
904
|
for (const attribute of currentAttributes) {
|
|
857
905
|
if (styleAttribute && attribute === styleAttribute) continue;
|
|
858
|
-
if (
|
|
859
|
-
continue;
|
|
860
|
-
}
|
|
906
|
+
if (shouldNormalize && isNormalizedProperty(attribute)) continue;
|
|
861
907
|
remainingAttributes.push(attribute);
|
|
862
908
|
}
|
|
863
|
-
|
|
909
|
+
const accessibleAttribute = shouldNormalize ? void 0 : buildAccessibleDefault(path, file, platform);
|
|
910
|
+
path.node.attributes = [...spreadAttributes, selectableAttribute, ...remainingAttributes, accessibleAttribute].filter(
|
|
864
911
|
(attribute) => attribute !== void 0
|
|
865
912
|
);
|
|
866
913
|
}
|
|
914
|
+
function buildAccessibleDefault(path, file, platform) {
|
|
915
|
+
if (platform === "web") return void 0;
|
|
916
|
+
let value;
|
|
917
|
+
if (platform === "ios" || platform === "android") {
|
|
918
|
+
value = types.booleanLiteral(platform === "ios");
|
|
919
|
+
} else {
|
|
920
|
+
const accessibleIdentifier = addFileImportHint({
|
|
921
|
+
file,
|
|
922
|
+
nameHint: "getDefaultTextAccessible",
|
|
923
|
+
path,
|
|
924
|
+
importName: "getDefaultTextAccessible",
|
|
925
|
+
moduleName: RUNTIME_MODULE_NAME
|
|
926
|
+
});
|
|
927
|
+
value = types.callExpression(types.identifier(accessibleIdentifier.name), []);
|
|
928
|
+
}
|
|
929
|
+
return types.jsxAttribute(types.jsxIdentifier("accessible"), types.jsxExpressionContainer(value));
|
|
930
|
+
}
|
|
867
931
|
|
|
868
932
|
const LOG_PREFIX = "[react-native-boost]";
|
|
869
933
|
const ANSI_RESET = "\x1B[0m";
|
|
870
934
|
const ANSI_GREEN = "\x1B[32m";
|
|
871
935
|
const ANSI_YELLOW = "\x1B[33m";
|
|
872
936
|
const ANSI_MAGENTA = "\x1B[35m";
|
|
937
|
+
const ANSI_RED = "\x1B[31m";
|
|
873
938
|
const noopLogger = {
|
|
874
939
|
optimized() {
|
|
875
940
|
},
|
|
876
941
|
skipped() {
|
|
877
942
|
},
|
|
943
|
+
forced() {
|
|
944
|
+
},
|
|
878
945
|
warning() {
|
|
879
946
|
}
|
|
880
947
|
};
|
|
@@ -888,6 +955,12 @@ const createLogger = ({ verbose, silent }) => {
|
|
|
888
955
|
if (!verbose) return;
|
|
889
956
|
writeLog("skipped", `Skipped ${payload.component} in ${formatPathLocation(payload.path)} (${payload.reason})`);
|
|
890
957
|
},
|
|
958
|
+
forced(payload) {
|
|
959
|
+
writeLog(
|
|
960
|
+
"forced",
|
|
961
|
+
`Force-optimized ${payload.component} in ${formatPathLocation(payload.path)} (skipped bailout: ${payload.reason})`
|
|
962
|
+
);
|
|
963
|
+
},
|
|
891
964
|
warning(payload) {
|
|
892
965
|
const context = formatWarningContext(payload);
|
|
893
966
|
const message = context.length > 0 ? `${context}: ${payload.message}` : payload.message;
|
|
@@ -916,6 +989,9 @@ function formatLevel(level) {
|
|
|
916
989
|
if (level === "skipped") {
|
|
917
990
|
return colorize("[skipped]", ANSI_YELLOW);
|
|
918
991
|
}
|
|
992
|
+
if (level === "forced") {
|
|
993
|
+
return colorize("[forced]", ANSI_RED);
|
|
994
|
+
}
|
|
919
995
|
return colorize("[warning]", ANSI_MAGENTA);
|
|
920
996
|
}
|
|
921
997
|
function colorize(value, colorCode) {
|
|
@@ -950,7 +1026,9 @@ function formatPathLocation(payloadPath) {
|
|
|
950
1026
|
}
|
|
951
1027
|
|
|
952
1028
|
const viewBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
953
|
-
//
|
|
1029
|
+
// The `View` wrapper translates these into native props (e.g. `aria-*` → `accessibility*`,
|
|
1030
|
+
// `tabIndex` → `focusable`). The native host does not understand them, so passing them through
|
|
1031
|
+
// would silently drop them. TODO: process these at runtime instead of bailing.
|
|
954
1032
|
"accessible",
|
|
955
1033
|
"accessibilityLabel",
|
|
956
1034
|
"accessibilityState",
|
|
@@ -958,51 +1036,47 @@ const viewBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
|
958
1036
|
"aria-checked",
|
|
959
1037
|
"aria-disabled",
|
|
960
1038
|
"aria-expanded",
|
|
1039
|
+
"aria-hidden",
|
|
961
1040
|
"aria-label",
|
|
1041
|
+
"aria-labelledby",
|
|
1042
|
+
"aria-live",
|
|
962
1043
|
"aria-selected",
|
|
1044
|
+
"aria-valuemax",
|
|
1045
|
+
"aria-valuemin",
|
|
1046
|
+
"aria-valuenow",
|
|
1047
|
+
"aria-valuetext",
|
|
963
1048
|
"id",
|
|
964
1049
|
"nativeID",
|
|
965
|
-
"
|
|
966
|
-
// TODO: process style at runtime
|
|
1050
|
+
"tabIndex"
|
|
967
1051
|
]);
|
|
968
1052
|
const viewOptimizer = (path, logger, options) => {
|
|
969
1053
|
if (!isValidJSXComponent(path, "View")) return;
|
|
970
|
-
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
ancestorClassification = getViewAncestorClassification(path);
|
|
974
|
-
}
|
|
975
|
-
return ancestorClassification;
|
|
976
|
-
};
|
|
977
|
-
const skipReason = getFirstBailoutReason([
|
|
978
|
-
{
|
|
979
|
-
reason: "line is marked with @boost-ignore",
|
|
980
|
-
shouldBail: () => isIgnoredLine(path)
|
|
981
|
-
},
|
|
982
|
-
{
|
|
983
|
-
reason: "View is not imported from react-native",
|
|
984
|
-
shouldBail: () => !isReactNativeImport(path, "View")
|
|
985
|
-
},
|
|
1054
|
+
if (!isReactNativeImport(path, "View")) return;
|
|
1055
|
+
const forced = isForcedLine(path);
|
|
1056
|
+
const overridableChecks = [
|
|
986
1057
|
{
|
|
987
1058
|
reason: "contains blacklisted props",
|
|
988
1059
|
shouldBail: () => hasBlacklistedProperty(path, viewBlacklistedProperties)
|
|
989
1060
|
},
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
{
|
|
995
|
-
|
|
996
|
-
|
|
1061
|
+
...ancestorBailoutChecks(path, (options == null ? void 0 : options.dangerouslyOptimizeViewWithUnknownAncestors) === true)
|
|
1062
|
+
];
|
|
1063
|
+
if (forced) {
|
|
1064
|
+
const overriddenReason = getFirstBailoutReason(overridableChecks);
|
|
1065
|
+
if (overriddenReason) {
|
|
1066
|
+
logger.forced({ component: "View", path, reason: overriddenReason });
|
|
1067
|
+
}
|
|
1068
|
+
} else {
|
|
1069
|
+
const skipReason = getFirstBailoutReason([
|
|
1070
|
+
{
|
|
1071
|
+
reason: "line is marked with @boost-ignore",
|
|
1072
|
+
shouldBail: () => isIgnoredLine(path)
|
|
1073
|
+
},
|
|
1074
|
+
...overridableChecks
|
|
1075
|
+
]);
|
|
1076
|
+
if (skipReason) {
|
|
1077
|
+
logger.skipped({ component: "View", path, reason: skipReason });
|
|
1078
|
+
return;
|
|
997
1079
|
}
|
|
998
|
-
]);
|
|
999
|
-
if (skipReason) {
|
|
1000
|
-
logger.skipped({
|
|
1001
|
-
component: "View",
|
|
1002
|
-
path,
|
|
1003
|
-
reason: skipReason
|
|
1004
|
-
});
|
|
1005
|
-
return;
|
|
1006
1080
|
}
|
|
1007
1081
|
const hub = path.hub;
|
|
1008
1082
|
const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
|
|
@@ -1019,6 +1093,7 @@ const viewOptimizer = (path, logger, options) => {
|
|
|
1019
1093
|
|
|
1020
1094
|
var index = declare((api) => {
|
|
1021
1095
|
api.assertVersion(7);
|
|
1096
|
+
const platform = api.caller((caller) => caller == null ? void 0 : caller.platform);
|
|
1022
1097
|
return {
|
|
1023
1098
|
name: "react-native-boost",
|
|
1024
1099
|
visitor: {
|
|
@@ -1028,7 +1103,7 @@ var index = declare((api) => {
|
|
|
1028
1103
|
const options = (_a = pluginState.opts) != null ? _a : {};
|
|
1029
1104
|
const logger = getOrCreateLogger(pluginState, options);
|
|
1030
1105
|
if (isIgnoredFile(path, (_b = options.ignores) != null ? _b : [])) return;
|
|
1031
|
-
if (((_c = options.optimizations) == null ? void 0 : _c.text) !== false) textOptimizer(path, logger);
|
|
1106
|
+
if (((_c = options.optimizations) == null ? void 0 : _c.text) !== false) textOptimizer(path, logger, options, platform);
|
|
1032
1107
|
if (((_d = options.optimizations) == null ? void 0 : _d.view) !== false) viewOptimizer(path, logger, options);
|
|
1033
1108
|
}
|
|
1034
1109
|
}
|