react-native-boost 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -4
- package/dist/plugin/esm/index.mjs +66 -40
- package/dist/plugin/esm/index.mjs.map +1 -1
- package/dist/plugin/index.js +66 -40
- package/dist/plugin/index.js.map +1 -1
- package/package.json +2 -1
- package/src/plugin/optimizers/text/index.ts +25 -19
- package/src/plugin/optimizers/view/index.ts +25 -20
- package/src/plugin/types/index.ts +1 -0
- package/src/plugin/utils/common/validation.ts +20 -23
- package/src/plugin/utils/logger.ts +16 -2
package/README.md
CHANGED
|
@@ -20,10 +20,19 @@ 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
|
-
<img src="./apps/docs/docs/
|
|
23
|
+
<img src="./apps/docs/content/docs/information/img/benchmark-ios.png" width="500" />
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
|
-
More benchmarks are available in the [docs](https://react-native-boost.oss.kuatsu.de/docs/
|
|
26
|
+
More benchmarks are available in the [docs](https://react-native-boost.oss.kuatsu.de/docs/information/benchmarks).
|
|
27
|
+
|
|
28
|
+
## Compatibility
|
|
29
|
+
|
|
30
|
+
| `react-native-boost` | React Native |
|
|
31
|
+
| -------------------- | ---------------- |
|
|
32
|
+
| `0.x` | All versions[^1] |
|
|
33
|
+
| `1.x` | `>=0.83` |
|
|
34
|
+
|
|
35
|
+
[^1]: Starting from React Native `0.80`, `react-native-boost@0` prints import deprecation warnings.
|
|
27
36
|
|
|
28
37
|
## Installation
|
|
29
38
|
|
|
@@ -59,11 +68,11 @@ yarn start --clear
|
|
|
59
68
|
|
|
60
69
|
That's it! No imports in your code, rebuilding, or anything else is required.
|
|
61
70
|
|
|
62
|
-
Optionally, you can configure the Babel plugin with a few options described in the [documentation](https://react-native-boost.oss.kuatsu.de/docs/
|
|
71
|
+
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
72
|
|
|
64
73
|
## How it works
|
|
65
74
|
|
|
66
|
-
A technical rundown of how the plugin works can be found in the [docs](https://react-native-boost.oss.kuatsu.de/docs/
|
|
75
|
+
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
76
|
|
|
68
77
|
## Contributing
|
|
69
78
|
|
|
@@ -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;
|
|
@@ -736,16 +742,10 @@ const textBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
|
736
742
|
]);
|
|
737
743
|
const textOptimizer = (path, logger) => {
|
|
738
744
|
if (!isValidJSXComponent(path, "Text")) return;
|
|
745
|
+
if (!isReactNativeImport(path, "Text")) return;
|
|
739
746
|
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
|
-
},
|
|
747
|
+
const forced = isForcedLine(path);
|
|
748
|
+
const overridableChecks = [
|
|
749
749
|
{
|
|
750
750
|
reason: "contains blacklisted props",
|
|
751
751
|
shouldBail: () => hasBlacklistedProperty(path, textBlacklistedProperties)
|
|
@@ -758,14 +758,24 @@ const textOptimizer = (path, logger) => {
|
|
|
758
758
|
reason: "contains non-string children",
|
|
759
759
|
shouldBail: () => hasInvalidChildren(path, parent)
|
|
760
760
|
}
|
|
761
|
-
]
|
|
762
|
-
if (
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
path,
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
761
|
+
];
|
|
762
|
+
if (forced) {
|
|
763
|
+
const overriddenReason = getFirstBailoutReason(overridableChecks);
|
|
764
|
+
if (overriddenReason) {
|
|
765
|
+
logger.forced({ component: "Text", path, reason: overriddenReason });
|
|
766
|
+
}
|
|
767
|
+
} else {
|
|
768
|
+
const skipReason = getFirstBailoutReason([
|
|
769
|
+
{
|
|
770
|
+
reason: "line is marked with @boost-ignore",
|
|
771
|
+
shouldBail: () => isIgnoredLine(path)
|
|
772
|
+
},
|
|
773
|
+
...overridableChecks
|
|
774
|
+
]);
|
|
775
|
+
if (skipReason) {
|
|
776
|
+
logger.skipped({ component: "Text", path, reason: skipReason });
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
769
779
|
}
|
|
770
780
|
const hub = path.hub;
|
|
771
781
|
const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
|
|
@@ -870,11 +880,14 @@ const ANSI_RESET = "\x1B[0m";
|
|
|
870
880
|
const ANSI_GREEN = "\x1B[32m";
|
|
871
881
|
const ANSI_YELLOW = "\x1B[33m";
|
|
872
882
|
const ANSI_MAGENTA = "\x1B[35m";
|
|
883
|
+
const ANSI_RED = "\x1B[31m";
|
|
873
884
|
const noopLogger = {
|
|
874
885
|
optimized() {
|
|
875
886
|
},
|
|
876
887
|
skipped() {
|
|
877
888
|
},
|
|
889
|
+
forced() {
|
|
890
|
+
},
|
|
878
891
|
warning() {
|
|
879
892
|
}
|
|
880
893
|
};
|
|
@@ -888,6 +901,12 @@ const createLogger = ({ verbose, silent }) => {
|
|
|
888
901
|
if (!verbose) return;
|
|
889
902
|
writeLog("skipped", `Skipped ${payload.component} in ${formatPathLocation(payload.path)} (${payload.reason})`);
|
|
890
903
|
},
|
|
904
|
+
forced(payload) {
|
|
905
|
+
writeLog(
|
|
906
|
+
"forced",
|
|
907
|
+
`Force-optimized ${payload.component} in ${formatPathLocation(payload.path)} (skipped bailout: ${payload.reason})`
|
|
908
|
+
);
|
|
909
|
+
},
|
|
891
910
|
warning(payload) {
|
|
892
911
|
const context = formatWarningContext(payload);
|
|
893
912
|
const message = context.length > 0 ? `${context}: ${payload.message}` : payload.message;
|
|
@@ -916,6 +935,9 @@ function formatLevel(level) {
|
|
|
916
935
|
if (level === "skipped") {
|
|
917
936
|
return colorize("[skipped]", ANSI_YELLOW);
|
|
918
937
|
}
|
|
938
|
+
if (level === "forced") {
|
|
939
|
+
return colorize("[forced]", ANSI_RED);
|
|
940
|
+
}
|
|
919
941
|
return colorize("[warning]", ANSI_MAGENTA);
|
|
920
942
|
}
|
|
921
943
|
function colorize(value, colorCode) {
|
|
@@ -967,6 +989,7 @@ const viewBlacklistedProperties = /* @__PURE__ */ new Set([
|
|
|
967
989
|
]);
|
|
968
990
|
const viewOptimizer = (path, logger, options) => {
|
|
969
991
|
if (!isValidJSXComponent(path, "View")) return;
|
|
992
|
+
if (!isReactNativeImport(path, "View")) return;
|
|
970
993
|
let ancestorClassification;
|
|
971
994
|
const getAncestorClassification = () => {
|
|
972
995
|
if (!ancestorClassification) {
|
|
@@ -974,15 +997,8 @@ const viewOptimizer = (path, logger, options) => {
|
|
|
974
997
|
}
|
|
975
998
|
return ancestorClassification;
|
|
976
999
|
};
|
|
977
|
-
const
|
|
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
|
-
},
|
|
1000
|
+
const forced = isForcedLine(path);
|
|
1001
|
+
const overridableChecks = [
|
|
986
1002
|
{
|
|
987
1003
|
reason: "contains blacklisted props",
|
|
988
1004
|
shouldBail: () => hasBlacklistedProperty(path, viewBlacklistedProperties)
|
|
@@ -995,14 +1011,24 @@ const viewOptimizer = (path, logger, options) => {
|
|
|
995
1011
|
reason: "has unresolved ancestor and dangerous optimization is disabled",
|
|
996
1012
|
shouldBail: () => getAncestorClassification() === "unknown" && (options == null ? void 0 : options.dangerouslyOptimizeViewWithUnknownAncestors) !== true
|
|
997
1013
|
}
|
|
998
|
-
]
|
|
999
|
-
if (
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
path,
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1014
|
+
];
|
|
1015
|
+
if (forced) {
|
|
1016
|
+
const overriddenReason = getFirstBailoutReason(overridableChecks);
|
|
1017
|
+
if (overriddenReason) {
|
|
1018
|
+
logger.forced({ component: "View", path, reason: overriddenReason });
|
|
1019
|
+
}
|
|
1020
|
+
} else {
|
|
1021
|
+
const skipReason = getFirstBailoutReason([
|
|
1022
|
+
{
|
|
1023
|
+
reason: "line is marked with @boost-ignore",
|
|
1024
|
+
shouldBail: () => isIgnoredLine(path)
|
|
1025
|
+
},
|
|
1026
|
+
...overridableChecks
|
|
1027
|
+
]);
|
|
1028
|
+
if (skipReason) {
|
|
1029
|
+
logger.skipped({ component: "View", path, reason: skipReason });
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1006
1032
|
}
|
|
1007
1033
|
const hub = path.hub;
|
|
1008
1034
|
const file = typeof hub === "object" && hub !== null && "file" in hub ? hub.file : void 0;
|