react-native-boost 1.1.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 +5 -2
- package/dist/plugin/esm/index.mjs +103 -54
- package/dist/plugin/esm/index.mjs.map +1 -1
- package/dist/plugin/index.d.ts +10 -0
- package/dist/plugin/index.js +103 -54
- 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 +5 -1
- package/src/plugin/index.ts +5 -1
- package/src/plugin/optimizers/text/index.ts +93 -31
- package/src/plugin/optimizers/view/index.ts +13 -22
- package/src/plugin/types/index.ts +17 -1
- package/src/plugin/utils/common/attributes.ts +71 -12
- package/src/plugin/utils/common/validation.ts +32 -9
- package/src/runtime/index.ts +39 -5
- package/src/runtime/index.web.ts +5 -0
package/dist/plugin/index.js
CHANGED
|
@@ -127,10 +127,7 @@ const isReactNativeImport = (path, expectedImportedName) => {
|
|
|
127
127
|
}
|
|
128
128
|
return false;
|
|
129
129
|
};
|
|
130
|
-
const
|
|
131
|
-
return classifyViewAncestors(path);
|
|
132
|
-
};
|
|
133
|
-
function classifyViewAncestors(path) {
|
|
130
|
+
const getAncestorClassification = (path) => {
|
|
134
131
|
const context = {
|
|
135
132
|
componentCache: /* @__PURE__ */ new WeakMap(),
|
|
136
133
|
componentInProgress: /* @__PURE__ */ new WeakSet(),
|
|
@@ -147,7 +144,21 @@ function classifyViewAncestors(path) {
|
|
|
147
144
|
ancestorPath = ancestorPath.parentPath;
|
|
148
145
|
}
|
|
149
146
|
return classification;
|
|
150
|
-
}
|
|
147
|
+
};
|
|
148
|
+
const ancestorBailoutChecks = (path, dangerousOptimizationEnabled) => {
|
|
149
|
+
let classification;
|
|
150
|
+
const classify = () => classification != null ? classification : classification = getAncestorClassification(path);
|
|
151
|
+
return [
|
|
152
|
+
{
|
|
153
|
+
reason: "has Text ancestor",
|
|
154
|
+
shouldBail: () => classify() === "text"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
reason: "has unresolved ancestor and dangerous optimization is disabled",
|
|
158
|
+
shouldBail: () => classify() === "unknown" && !dangerousOptimizationEnabled
|
|
159
|
+
}
|
|
160
|
+
];
|
|
161
|
+
};
|
|
151
162
|
function classifyJSXElementAsAncestor(path, context) {
|
|
152
163
|
const openingElementName = path.node.openingElement.name;
|
|
153
164
|
if (core.types.isJSXIdentifier(openingElementName)) {
|
|
@@ -675,18 +686,37 @@ function extractSelectableAndUpdateStyle(styleExpr) {
|
|
|
675
686
|
}
|
|
676
687
|
return void 0;
|
|
677
688
|
}
|
|
678
|
-
const
|
|
679
|
-
if (core.types.
|
|
680
|
-
if (core.types.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
689
|
+
const isPrimitiveExpression = (path, expression, resolved = /* @__PURE__ */ new Set()) => {
|
|
690
|
+
if (core.types.isStringLiteral(expression) || core.types.isNumericLiteral(expression)) return true;
|
|
691
|
+
if (core.types.isTemplateLiteral(expression)) return true;
|
|
692
|
+
if (core.types.isBinaryExpression(expression)) {
|
|
693
|
+
const { left, right } = expression;
|
|
694
|
+
return core.types.isExpression(left) && isPrimitiveExpression(path, left, resolved) && isPrimitiveExpression(path, right, resolved);
|
|
695
|
+
}
|
|
696
|
+
if (core.types.isConditionalExpression(expression)) {
|
|
697
|
+
return isPrimitiveExpression(path, expression.consequent, resolved) && isPrimitiveExpression(path, expression.alternate, resolved);
|
|
698
|
+
}
|
|
699
|
+
if (core.types.isLogicalExpression(expression)) {
|
|
700
|
+
return isPrimitiveExpression(path, expression.left, resolved) && isPrimitiveExpression(path, expression.right, resolved);
|
|
701
|
+
}
|
|
702
|
+
if (core.types.isIdentifier(expression)) {
|
|
703
|
+
if (resolved.has(expression.name)) return false;
|
|
704
|
+
const binding = path.scope.getBinding(expression.name);
|
|
705
|
+
if (binding && binding.constant && binding.path.node && core.types.isVariableDeclarator(binding.path.node)) {
|
|
706
|
+
const init = binding.path.node.init;
|
|
707
|
+
return !!init && core.types.isExpression(init) && isPrimitiveExpression(path, init, new Set(resolved).add(expression.name));
|
|
688
708
|
}
|
|
689
|
-
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
return false;
|
|
712
|
+
};
|
|
713
|
+
const isPrimitiveChild = (path, child) => {
|
|
714
|
+
if (core.types.isJSXText(child)) return true;
|
|
715
|
+
if (core.types.isStringLiteral(child)) return true;
|
|
716
|
+
if (core.types.isJSXExpressionContainer(child)) {
|
|
717
|
+
const { expression } = child;
|
|
718
|
+
if (core.types.isJSXEmptyExpression(expression)) return false;
|
|
719
|
+
return isPrimitiveExpression(path, expression);
|
|
690
720
|
}
|
|
691
721
|
return false;
|
|
692
722
|
};
|
|
@@ -725,6 +755,10 @@ const replaceWithNativeComponent = (path, parent, file, nativeComponentName) =>
|
|
|
725
755
|
};
|
|
726
756
|
|
|
727
757
|
const textBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
758
|
+
// The `Text` wrapper translates `aria-hidden` into `accessibilityElementsHidden` /
|
|
759
|
+
// `importantForAccessibility`, which `processAccessibilityProps` does not yet handle. Passing it
|
|
760
|
+
// through would drop it, so bail. TODO: handle this in the runtime helper instead.
|
|
761
|
+
"aria-hidden",
|
|
728
762
|
"id",
|
|
729
763
|
"nativeID",
|
|
730
764
|
"onLongPress",
|
|
@@ -742,7 +776,9 @@ const textBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
|
742
776
|
"selectionColor"
|
|
743
777
|
// TODO: we can use react-native's internal `processColor` to process this at runtime
|
|
744
778
|
]);
|
|
745
|
-
const
|
|
779
|
+
const NORMALIZED_PROPERTIES = /* @__PURE__ */ new Set([...ACCESSIBILITY_PROPERTIES, "disabled"]);
|
|
780
|
+
const isNormalizedProperty = (attribute) => core.types.isJSXAttribute(attribute) && core.types.isJSXIdentifier(attribute.name) && NORMALIZED_PROPERTIES.has(attribute.name.name);
|
|
781
|
+
const textOptimizer = (path, logger, options, platform) => {
|
|
746
782
|
if (!isValidJSXComponent(path, "Text")) return;
|
|
747
783
|
if (!isReactNativeImport(path, "Text")) return;
|
|
748
784
|
const parent = path.parent;
|
|
@@ -756,10 +792,13 @@ const textOptimizer = (path, logger) => {
|
|
|
756
792
|
reason: "is a direct child of expo-router Link with asChild",
|
|
757
793
|
shouldBail: () => hasExpoRouterLinkParentWithAsChild(path)
|
|
758
794
|
},
|
|
795
|
+
// The local children check runs before the ancestor checks because it is cheap and prunes the
|
|
796
|
+
// common nested-element `Text` before the unbounded ancestor walk those checks trigger.
|
|
759
797
|
{
|
|
760
|
-
reason: "contains non-
|
|
798
|
+
reason: "contains non-primitive children",
|
|
761
799
|
shouldBail: () => hasInvalidChildren(path, parent)
|
|
762
|
-
}
|
|
800
|
+
},
|
|
801
|
+
...ancestorBailoutChecks(path, (options == null ? void 0 : options.dangerouslyOptimizeTextWithUnknownAncestors) === true)
|
|
763
802
|
];
|
|
764
803
|
if (forced) {
|
|
765
804
|
const overriddenReason = getFirstBailoutReason(overridableChecks);
|
|
@@ -791,18 +830,18 @@ const textOptimizer = (path, logger) => {
|
|
|
791
830
|
fixNegativeNumberOfLines({ path, logger });
|
|
792
831
|
addDefaultProperty(path, "allowFontScaling", core.types.booleanLiteral(true));
|
|
793
832
|
addDefaultProperty(path, "ellipsizeMode", core.types.stringLiteral("tail"));
|
|
794
|
-
processProps(path, file);
|
|
833
|
+
processProps(path, file, platform);
|
|
795
834
|
replaceWithNativeComponent(path, parent, file, "NativeText");
|
|
796
835
|
};
|
|
797
836
|
function hasInvalidChildren(path, parent) {
|
|
798
837
|
for (const attribute of path.node.attributes) {
|
|
799
838
|
if (core.types.isJSXSpreadAttribute(attribute)) continue;
|
|
800
|
-
if (core.types.isJSXIdentifier(attribute.name) && attribute.value && // For a "children" attribute, optimization is allowed only if it is a
|
|
801
|
-
attribute.name.name === "children" && !
|
|
839
|
+
if (core.types.isJSXIdentifier(attribute.name) && attribute.value && // For a "children" attribute, optimization is allowed only if it is a provable primitive
|
|
840
|
+
attribute.name.name === "children" && !isPrimitiveChild(path, attribute.value)) {
|
|
802
841
|
return true;
|
|
803
842
|
}
|
|
804
843
|
}
|
|
805
|
-
return !parent.children.every((child) =>
|
|
844
|
+
return !parent.children.every((child) => isPrimitiveChild(path, child));
|
|
806
845
|
}
|
|
807
846
|
function fixNegativeNumberOfLines({ path, logger }) {
|
|
808
847
|
for (const attribute of path.node.attributes) {
|
|
@@ -824,16 +863,15 @@ function fixNegativeNumberOfLines({ path, logger }) {
|
|
|
824
863
|
}
|
|
825
864
|
}
|
|
826
865
|
}
|
|
827
|
-
function processProps(path, file) {
|
|
866
|
+
function processProps(path, file, platform) {
|
|
828
867
|
const currentAttributes = [...path.node.attributes];
|
|
829
868
|
const { styleExpr, styleAttribute } = extractStyleAttribute(currentAttributes);
|
|
830
|
-
const
|
|
869
|
+
const shouldNormalize = hasAccessibilityProperty(path, currentAttributes) || currentAttributes.some(
|
|
870
|
+
(attribute) => core.types.isJSXAttribute(attribute) && core.types.isJSXIdentifier(attribute.name, { name: "disabled" })
|
|
871
|
+
);
|
|
831
872
|
const spreadAttributes = [];
|
|
832
|
-
if (
|
|
833
|
-
const
|
|
834
|
-
if (!core.types.isJSXAttribute(attribute)) return false;
|
|
835
|
-
return core.types.isJSXIdentifier(attribute.name) && ACCESSIBILITY_PROPERTIES.has(attribute.name.name);
|
|
836
|
-
});
|
|
873
|
+
if (shouldNormalize) {
|
|
874
|
+
const normalizedAttributes = currentAttributes.filter((attribute) => isNormalizedProperty(attribute));
|
|
837
875
|
const normalizeIdentifier = addFileImportHint({
|
|
838
876
|
file,
|
|
839
877
|
nameHint: "processAccessibilityProps",
|
|
@@ -841,7 +879,7 @@ function processProps(path, file) {
|
|
|
841
879
|
importName: "processAccessibilityProps",
|
|
842
880
|
moduleName: RUNTIME_MODULE_NAME
|
|
843
881
|
});
|
|
844
|
-
const accessibilityObject = buildPropertiesFromAttributes(
|
|
882
|
+
const accessibilityObject = buildPropertiesFromAttributes(normalizedAttributes);
|
|
845
883
|
const accessibilityExpr = core.types.callExpression(core.types.identifier(normalizeIdentifier.name), [accessibilityObject]);
|
|
846
884
|
spreadAttributes.push(core.types.jsxSpreadAttribute(accessibilityExpr));
|
|
847
885
|
}
|
|
@@ -867,15 +905,31 @@ function processProps(path, file) {
|
|
|
867
905
|
const remainingAttributes = [];
|
|
868
906
|
for (const attribute of currentAttributes) {
|
|
869
907
|
if (styleAttribute && attribute === styleAttribute) continue;
|
|
870
|
-
if (
|
|
871
|
-
continue;
|
|
872
|
-
}
|
|
908
|
+
if (shouldNormalize && isNormalizedProperty(attribute)) continue;
|
|
873
909
|
remainingAttributes.push(attribute);
|
|
874
910
|
}
|
|
875
|
-
|
|
911
|
+
const accessibleAttribute = shouldNormalize ? void 0 : buildAccessibleDefault(path, file, platform);
|
|
912
|
+
path.node.attributes = [...spreadAttributes, selectableAttribute, ...remainingAttributes, accessibleAttribute].filter(
|
|
876
913
|
(attribute) => attribute !== void 0
|
|
877
914
|
);
|
|
878
915
|
}
|
|
916
|
+
function buildAccessibleDefault(path, file, platform) {
|
|
917
|
+
if (platform === "web") return void 0;
|
|
918
|
+
let value;
|
|
919
|
+
if (platform === "ios" || platform === "android") {
|
|
920
|
+
value = core.types.booleanLiteral(platform === "ios");
|
|
921
|
+
} else {
|
|
922
|
+
const accessibleIdentifier = addFileImportHint({
|
|
923
|
+
file,
|
|
924
|
+
nameHint: "getDefaultTextAccessible",
|
|
925
|
+
path,
|
|
926
|
+
importName: "getDefaultTextAccessible",
|
|
927
|
+
moduleName: RUNTIME_MODULE_NAME
|
|
928
|
+
});
|
|
929
|
+
value = core.types.callExpression(core.types.identifier(accessibleIdentifier.name), []);
|
|
930
|
+
}
|
|
931
|
+
return core.types.jsxAttribute(core.types.jsxIdentifier("accessible"), core.types.jsxExpressionContainer(value));
|
|
932
|
+
}
|
|
879
933
|
|
|
880
934
|
const LOG_PREFIX = "[react-native-boost]";
|
|
881
935
|
const ANSI_RESET = "\x1B[0m";
|
|
@@ -974,7 +1028,9 @@ function formatPathLocation(payloadPath) {
|
|
|
974
1028
|
}
|
|
975
1029
|
|
|
976
1030
|
const viewBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
977
|
-
//
|
|
1031
|
+
// The `View` wrapper translates these into native props (e.g. `aria-*` → `accessibility*`,
|
|
1032
|
+
// `tabIndex` → `focusable`). The native host does not understand them, so passing them through
|
|
1033
|
+
// would silently drop them. TODO: process these at runtime instead of bailing.
|
|
978
1034
|
"accessible",
|
|
979
1035
|
"accessibilityLabel",
|
|
980
1036
|
"accessibilityState",
|
|
@@ -982,37 +1038,29 @@ const viewBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
|
982
1038
|
"aria-checked",
|
|
983
1039
|
"aria-disabled",
|
|
984
1040
|
"aria-expanded",
|
|
1041
|
+
"aria-hidden",
|
|
985
1042
|
"aria-label",
|
|
1043
|
+
"aria-labelledby",
|
|
1044
|
+
"aria-live",
|
|
986
1045
|
"aria-selected",
|
|
1046
|
+
"aria-valuemax",
|
|
1047
|
+
"aria-valuemin",
|
|
1048
|
+
"aria-valuenow",
|
|
1049
|
+
"aria-valuetext",
|
|
987
1050
|
"id",
|
|
988
1051
|
"nativeID",
|
|
989
|
-
"
|
|
990
|
-
// TODO: process style at runtime
|
|
1052
|
+
"tabIndex"
|
|
991
1053
|
]);
|
|
992
1054
|
const viewOptimizer = (path, logger, options) => {
|
|
993
1055
|
if (!isValidJSXComponent(path, "View")) return;
|
|
994
1056
|
if (!isReactNativeImport(path, "View")) return;
|
|
995
|
-
let ancestorClassification;
|
|
996
|
-
const getAncestorClassification = () => {
|
|
997
|
-
if (!ancestorClassification) {
|
|
998
|
-
ancestorClassification = getViewAncestorClassification(path);
|
|
999
|
-
}
|
|
1000
|
-
return ancestorClassification;
|
|
1001
|
-
};
|
|
1002
1057
|
const forced = isForcedLine(path);
|
|
1003
1058
|
const overridableChecks = [
|
|
1004
1059
|
{
|
|
1005
1060
|
reason: "contains blacklisted props",
|
|
1006
1061
|
shouldBail: () => hasBlacklistedProperty(path, viewBlacklistedProperties)
|
|
1007
1062
|
},
|
|
1008
|
-
|
|
1009
|
-
reason: "has Text ancestor",
|
|
1010
|
-
shouldBail: () => getAncestorClassification() === "text"
|
|
1011
|
-
},
|
|
1012
|
-
{
|
|
1013
|
-
reason: "has unresolved ancestor and dangerous optimization is disabled",
|
|
1014
|
-
shouldBail: () => getAncestorClassification() === "unknown" && (options == null ? void 0 : options.dangerouslyOptimizeViewWithUnknownAncestors) !== true
|
|
1015
|
-
}
|
|
1063
|
+
...ancestorBailoutChecks(path, (options == null ? void 0 : options.dangerouslyOptimizeViewWithUnknownAncestors) === true)
|
|
1016
1064
|
];
|
|
1017
1065
|
if (forced) {
|
|
1018
1066
|
const overriddenReason = getFirstBailoutReason(overridableChecks);
|
|
@@ -1047,6 +1095,7 @@ const viewOptimizer = (path, logger, options) => {
|
|
|
1047
1095
|
|
|
1048
1096
|
var index = helperPluginUtils.declare((api) => {
|
|
1049
1097
|
api.assertVersion(7);
|
|
1098
|
+
const platform = api.caller((caller) => caller == null ? void 0 : caller.platform);
|
|
1050
1099
|
return {
|
|
1051
1100
|
name: "react-native-boost",
|
|
1052
1101
|
visitor: {
|
|
@@ -1056,7 +1105,7 @@ var index = helperPluginUtils.declare((api) => {
|
|
|
1056
1105
|
const options = (_a = pluginState.opts) != null ? _a : {};
|
|
1057
1106
|
const logger = getOrCreateLogger(pluginState, options);
|
|
1058
1107
|
if (isIgnoredFile(path, (_b = options.ignores) != null ? _b : [])) return;
|
|
1059
|
-
if (((_c = options.optimizations) == null ? void 0 : _c.text) !== false) textOptimizer(path, logger);
|
|
1108
|
+
if (((_c = options.optimizations) == null ? void 0 : _c.text) !== false) textOptimizer(path, logger, options, platform);
|
|
1060
1109
|
if (((_d = options.optimizations) == null ? void 0 : _d.view) !== false) viewOptimizer(path, logger, options);
|
|
1061
1110
|
}
|
|
1062
1111
|
}
|