react-native 0.85.1 → 0.85.3
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/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/Utilities/HMRClient.js +28 -1
- package/React/Base/RCTVersion.m +1 -1
- package/React/CoreModules/RCTDevLoadingView.mm +17 -0
- package/React/CoreModules/RCTJscSafeUrl+Internal.h +23 -0
- package/React/CoreModules/RCTJscSafeUrl.mm +38 -0
- package/React/CoreModules/RCTRedBox+Internal.h +42 -0
- package/React/CoreModules/RCTRedBox.mm +30 -454
- package/React/CoreModules/RCTRedBox2AnsiParser+Internal.h +22 -0
- package/React/CoreModules/RCTRedBox2AnsiParser.mm +55 -0
- package/React/CoreModules/RCTRedBox2Controller+Internal.h +34 -0
- package/React/CoreModules/RCTRedBox2Controller.mm +764 -0
- package/React/CoreModules/RCTRedBox2ErrorParser+Internal.h +46 -0
- package/React/CoreModules/RCTRedBox2ErrorParser.mm +57 -0
- package/React/CoreModules/RCTRedBoxController+Internal.h +31 -0
- package/React/CoreModules/RCTRedBoxController.mm +447 -0
- package/React/CoreModules/RCTRedBoxHMRClient+Internal.h +26 -0
- package/React/CoreModules/RCTRedBoxHMRClient.mm +125 -0
- package/React/CoreModules/React-CoreModules.podspec +1 -0
- package/React/DevSupport/RCTFrameTimingsObserver.h +24 -0
- package/React/DevSupport/RCTFrameTimingsObserver.mm +298 -0
- package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +40 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorFlags.kt +4 -0
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingSequence.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingsObserver.kt +127 -26
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +31 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +51 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +56 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt +39 -0
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +50 -10
- package/ReactAndroid/src/main/jni/CMakeLists.txt +7 -0
- package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.cpp +22 -0
- package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.h +2 -0
- package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +71 -1
- package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +16 -1
- package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp +14 -0
- package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h +18 -4
- package/ReactCommon/React-Fabric.podspec +6 -0
- package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
- package/ReactCommon/jsinspector-modern/HostAgent.cpp +36 -0
- package/ReactCommon/jsinspector-modern/HostTarget.cpp +7 -1
- package/ReactCommon/jsinspector-modern/HostTarget.h +25 -0
- package/ReactCommon/jsinspector-modern/HostTargetTracing.cpp +1 -1
- package/ReactCommon/jsinspector-modern/HostTargetTracing.h +4 -4
- package/ReactCommon/jsinspector-modern/InspectorFlags.cpp +12 -0
- package/ReactCommon/jsinspector-modern/InspectorFlags.h +12 -0
- package/ReactCommon/jsinspector-modern/NetworkIOAgent.cpp +1 -1
- package/ReactCommon/jsinspector-modern/RuntimeAgent.cpp +19 -0
- package/ReactCommon/jsinspector-modern/RuntimeAgent.h +7 -0
- package/ReactCommon/jsinspector-modern/RuntimeTarget.cpp +33 -0
- package/ReactCommon/jsinspector-modern/RuntimeTarget.h +6 -0
- package/ReactCommon/jsinspector-modern/tests/HostTargetTest.cpp +12 -0
- package/ReactCommon/jsinspector-modern/tests/InspectorMocks.h +3 -2
- package/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp +1 -0
- package/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp +1 -1
- package/ReactCommon/jsinspector-modern/tests/TracingTest.cpp +1 -1
- package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.cpp +10 -0
- package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.h +3 -1
- package/ReactCommon/jsinspector-modern/tracing/CMakeLists.txt +1 -0
- package/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h +7 -3
- package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.cpp +52 -29
- package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.h +6 -6
- package/ReactCommon/jsinspector-modern/tracing/PerformanceTracerSection.h +113 -0
- package/ReactCommon/jsinspector-modern/tracing/React-jsinspectortracing.podspec +1 -0
- package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.cpp +12 -5
- package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h +3 -1
- package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.cpp +42 -0
- package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.h +7 -0
- package/ReactCommon/react/debug/CMakeLists.txt +2 -1
- package/ReactCommon/react/debug/React-debug.podspec +7 -1
- package/ReactCommon/react/debug/redbox/AnsiParser.cpp +139 -0
- package/ReactCommon/react/debug/redbox/AnsiParser.h +35 -0
- package/ReactCommon/react/debug/redbox/JscSafeUrl.cpp +179 -0
- package/ReactCommon/react/debug/redbox/JscSafeUrl.h +27 -0
- package/ReactCommon/react/debug/redbox/RedBoxErrorParser.cpp +171 -0
- package/ReactCommon/react/debug/redbox/RedBoxErrorParser.h +40 -0
- package/ReactCommon/react/debug/redbox/tests/AnsiParserTest.cpp +97 -0
- package/ReactCommon/react/debug/redbox/tests/JscSafeUrlTest.cpp +173 -0
- package/ReactCommon/react/debug/redbox/tests/RedBoxErrorParserTest.cpp +107 -0
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +21 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +26 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +135 -45
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +12 -2
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +21 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +46 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +6 -1
- package/ReactCommon/react/nativemodule/defaults/CMakeLists.txt +1 -0
- package/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp +7 -0
- package/ReactCommon/react/nativemodule/defaults/React-defaultsnativemodule.podspec +1 -0
- package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +26 -1
- package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +11 -1
- package/ReactCommon/react/nativemodule/mutationobserver/NativeMutationObserver.h +4 -0
- package/ReactCommon/react/nativemodule/mutationobserver/React-mutationobservernativemodule.podspec +66 -0
- package/ReactCommon/react/performance/timeline/PerformanceObserver.cpp +18 -6
- package/ReactCommon/react/performance/timeline/PerformanceObserver.h +2 -0
- package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +4 -1
- package/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +58 -25
- package/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +9 -0
- package/ReactCommon/react/renderer/animationbackend/AnimationChoreographer.h +5 -0
- package/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +1 -0
- package/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +115 -0
- package/ReactCommon/{jsinspector-modern → react/utils}/Base64.h +2 -2
- package/package.json +11 -11
- package/scripts/cocoapods/utils.rb +1 -0
- package/scripts/react_native_pods.rb +1 -0
- package/scripts/replace-rncore-version.js +72 -15
- package/src/private/featureflags/ReactNativeFeatureFlags.js +26 -1
- package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +6 -1
- package/src/private/setup/setUpDefaultReactNativeEnvironment.js +6 -0
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
#include "Timing.h"
|
|
10
10
|
#include "TracingCategory.h"
|
|
11
11
|
|
|
12
|
+
#include <react/utils/Base64.h>
|
|
13
|
+
|
|
12
14
|
namespace facebook::react::jsinspector_modern::tracing {
|
|
13
15
|
|
|
14
16
|
/* static */ TraceEvent TraceEventGenerator::createSetLayerTreeIdEvent(
|
|
@@ -70,14 +72,19 @@ TraceEventGenerator::createFrameTimingsEvents(
|
|
|
70
72
|
/* static */ TraceEvent TraceEventGenerator::createScreenshotEvent(
|
|
71
73
|
FrameSequenceId frameSequenceId,
|
|
72
74
|
int sourceId,
|
|
73
|
-
std::
|
|
75
|
+
std::vector<uint8_t>&& snapshot,
|
|
74
76
|
HighResTimeStamp expectedDisplayTime,
|
|
75
77
|
ProcessId processId,
|
|
76
78
|
ThreadId threadId) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
// Convert binary data to string for Base64 encoding
|
|
80
|
+
std::string snapshotBytes(snapshot.begin(), snapshot.end());
|
|
81
|
+
std::string base64Snapshot = base64Encode(snapshotBytes);
|
|
82
|
+
|
|
83
|
+
folly::dynamic args =
|
|
84
|
+
folly::dynamic::object("snapshot", std::move(base64Snapshot))(
|
|
85
|
+
"source_id", sourceId)("frame_sequence", frameSequenceId)(
|
|
86
|
+
"expected_display_time",
|
|
87
|
+
highResTimeStampToTracingClockTimeStamp(expectedDisplayTime));
|
|
81
88
|
|
|
82
89
|
return TraceEvent{
|
|
83
90
|
.name = "Screenshot",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
#include <jsinspector-modern/tracing/FrameTimingSequence.h>
|
|
13
13
|
#include <react/timing/primitives.h>
|
|
14
14
|
|
|
15
|
+
#include <cstdint>
|
|
15
16
|
#include <utility>
|
|
17
|
+
#include <vector>
|
|
16
18
|
|
|
17
19
|
namespace facebook::react::jsinspector_modern::tracing {
|
|
18
20
|
|
|
@@ -49,7 +51,7 @@ class TraceEventGenerator {
|
|
|
49
51
|
static TraceEvent createScreenshotEvent(
|
|
50
52
|
FrameSequenceId frameSequenceId,
|
|
51
53
|
int sourceId,
|
|
52
|
-
std::
|
|
54
|
+
std::vector<uint8_t> &&snapshot,
|
|
53
55
|
HighResTimeStamp expectedDisplayTime,
|
|
54
56
|
ProcessId processId,
|
|
55
57
|
ThreadId threadId);
|
|
@@ -112,4 +112,46 @@ TraceEventSerializer::serializeProfileChunkCPUProfileNodeCallFrame(
|
|
|
112
112
|
return dynamicCallFrame;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
/* static */ size_t TraceEventSerializer::estimateJsonSize(
|
|
116
|
+
const folly::dynamic& value) {
|
|
117
|
+
switch (value.type()) {
|
|
118
|
+
case folly::dynamic::Type::NULLT:
|
|
119
|
+
return 4; // null
|
|
120
|
+
case folly::dynamic::Type::BOOL:
|
|
121
|
+
return 5; // false
|
|
122
|
+
case folly::dynamic::Type::INT64:
|
|
123
|
+
case folly::dynamic::Type::DOUBLE:
|
|
124
|
+
return 20; // conservative max for numeric values
|
|
125
|
+
case folly::dynamic::Type::STRING:
|
|
126
|
+
return value.stringPiece().size() + 2; // quotes
|
|
127
|
+
case folly::dynamic::Type::ARRAY: {
|
|
128
|
+
size_t size = 2; // []
|
|
129
|
+
bool first = true;
|
|
130
|
+
for (const auto& element : value) {
|
|
131
|
+
if (!first) {
|
|
132
|
+
size += 1; // comma
|
|
133
|
+
}
|
|
134
|
+
first = false;
|
|
135
|
+
size += estimateJsonSize(element);
|
|
136
|
+
}
|
|
137
|
+
return size;
|
|
138
|
+
}
|
|
139
|
+
case folly::dynamic::Type::OBJECT: {
|
|
140
|
+
size_t size = 2; // {}
|
|
141
|
+
bool first = true;
|
|
142
|
+
for (const auto& [key, val] : value.items()) {
|
|
143
|
+
if (!first) {
|
|
144
|
+
size += 1; // comma
|
|
145
|
+
}
|
|
146
|
+
first = false;
|
|
147
|
+
// key size + quotes + colon
|
|
148
|
+
size += key.stringPiece().size() + 3;
|
|
149
|
+
size += estimateJsonSize(val);
|
|
150
|
+
}
|
|
151
|
+
return size;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
115
157
|
} // namespace facebook::react::jsinspector_modern::tracing
|
|
@@ -84,6 +84,13 @@ class TraceEventSerializer {
|
|
|
84
84
|
*/
|
|
85
85
|
static folly::dynamic serializeProfileChunkCPUProfileNodeCallFrame(
|
|
86
86
|
TraceEventProfileChunk::CPUProfile::Node::CallFrame &&callFrame);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Estimates the JSON-serialized byte size of a folly::dynamic value.
|
|
90
|
+
* This is a rough but conservative estimate to avoid the cost of
|
|
91
|
+
* double-serialization (once to measure, once to emit).
|
|
92
|
+
*/
|
|
93
|
+
static size_t estimateJsonSize(const folly::dynamic &value);
|
|
87
94
|
};
|
|
88
95
|
|
|
89
96
|
} // namespace facebook::react::jsinspector_modern::tracing
|
|
@@ -9,7 +9,8 @@ set(CMAKE_VERBOSE_MAKEFILE on)
|
|
|
9
9
|
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
|
|
10
10
|
|
|
11
11
|
file(GLOB react_debug_SRC CONFIGURE_DEPENDS *.cpp)
|
|
12
|
-
|
|
12
|
+
file(GLOB react_debug_redbox_SRC CONFIGURE_DEPENDS redbox/*.cpp)
|
|
13
|
+
add_library(react_debug OBJECT ${react_debug_SRC} ${react_debug_redbox_SRC})
|
|
13
14
|
|
|
14
15
|
target_include_directories(react_debug PUBLIC ${REACT_COMMON_DIR})
|
|
15
16
|
|
|
@@ -25,10 +25,16 @@ Pod::Spec.new do |s|
|
|
|
25
25
|
s.author = "Meta Platforms, Inc. and its affiliates"
|
|
26
26
|
s.platforms = min_supported_versions
|
|
27
27
|
s.source = source
|
|
28
|
-
s.source_files = podspec_sources("
|
|
28
|
+
s.source_files = podspec_sources("*.{cpp,h}", "*.h")
|
|
29
29
|
s.header_dir = "react/debug"
|
|
30
30
|
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
|
|
31
31
|
"DEFINES_MODULE" => "YES" }
|
|
32
32
|
|
|
33
33
|
resolve_use_frameworks(s, header_mappings_dir: "../..", module_name: "React_debug")
|
|
34
|
+
|
|
35
|
+
s.subspec "redbox" do |ss|
|
|
36
|
+
ss.source_files = podspec_sources("redbox/*.{cpp,h}", "redbox/*.h")
|
|
37
|
+
ss.exclude_files = "redbox/tests/**/*.{cpp,h}"
|
|
38
|
+
ss.header_dir = "react/debug/redbox"
|
|
39
|
+
end
|
|
34
40
|
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "AnsiParser.h"
|
|
9
|
+
|
|
10
|
+
#include <optional>
|
|
11
|
+
|
|
12
|
+
#include <regex> // NOLINT(facebook-hte-BadInclude-regex)
|
|
13
|
+
|
|
14
|
+
// @lint-ignore-every CLANGTIDY facebook-hte-StdRegexIsAwful
|
|
15
|
+
namespace facebook::react::unstable_redbox {
|
|
16
|
+
|
|
17
|
+
namespace {
|
|
18
|
+
|
|
19
|
+
// Afterglow theme colors (matching AnsiHighlight.js)
|
|
20
|
+
std::optional<AnsiColor> ansiColor(int code) {
|
|
21
|
+
switch (code) {
|
|
22
|
+
case 30:
|
|
23
|
+
return AnsiColor{.r = 27, .g = 27, .b = 27}; // black
|
|
24
|
+
case 31:
|
|
25
|
+
return AnsiColor{.r = 187, .g = 86, .b = 83}; // red
|
|
26
|
+
case 32:
|
|
27
|
+
return AnsiColor{.r = 144, .g = 157, .b = 98}; // green
|
|
28
|
+
case 33:
|
|
29
|
+
return AnsiColor{.r = 234, .g = 193, .b = 121}; // yellow
|
|
30
|
+
case 34:
|
|
31
|
+
return AnsiColor{.r = 125, .g = 169, .b = 199}; // blue
|
|
32
|
+
case 35:
|
|
33
|
+
return AnsiColor{.r = 176, .g = 101, .b = 151}; // magenta
|
|
34
|
+
case 36:
|
|
35
|
+
return AnsiColor{.r = 140, .g = 220, .b = 216}; // cyan
|
|
36
|
+
case 37:
|
|
37
|
+
return std::nullopt; // white = default
|
|
38
|
+
case 90:
|
|
39
|
+
return AnsiColor{.r = 98, .g = 98, .b = 98}; // bright black
|
|
40
|
+
case 91:
|
|
41
|
+
return AnsiColor{.r = 187, .g = 86, .b = 83}; // bright red
|
|
42
|
+
case 92:
|
|
43
|
+
return AnsiColor{.r = 144, .g = 157, .b = 98}; // bright green
|
|
44
|
+
case 93:
|
|
45
|
+
return AnsiColor{.r = 234, .g = 193, .b = 121}; // bright yellow
|
|
46
|
+
case 94:
|
|
47
|
+
return AnsiColor{.r = 125, .g = 169, .b = 199}; // bright blue
|
|
48
|
+
case 95:
|
|
49
|
+
return AnsiColor{.r = 176, .g = 101, .b = 151}; // bright magenta
|
|
50
|
+
case 96:
|
|
51
|
+
return AnsiColor{.r = 140, .g = 220, .b = 216}; // bright cyan
|
|
52
|
+
case 97:
|
|
53
|
+
return AnsiColor{.r = 247, .g = 247, .b = 247}; // bright white
|
|
54
|
+
default:
|
|
55
|
+
return std::nullopt;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const std::regex& ansiRegex() {
|
|
60
|
+
static const std::regex re(R"(\x1b\[([0-9;]*)m)");
|
|
61
|
+
return re;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
int parseSgrCode(const std::string& params, size_t& pos) {
|
|
65
|
+
size_t next = params.find(';', pos);
|
|
66
|
+
if (next == std::string::npos) {
|
|
67
|
+
next = params.size();
|
|
68
|
+
}
|
|
69
|
+
int code = 0;
|
|
70
|
+
for (size_t i = pos; i < next; ++i) {
|
|
71
|
+
code = code * 10 + (params[i] - '0');
|
|
72
|
+
}
|
|
73
|
+
pos = next + 1;
|
|
74
|
+
return code;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
} // namespace
|
|
78
|
+
|
|
79
|
+
std::vector<AnsiSpan> parseAnsi(const std::string& text) {
|
|
80
|
+
std::vector<AnsiSpan> spans;
|
|
81
|
+
std::optional<AnsiColor> currentFg;
|
|
82
|
+
std::optional<AnsiColor> currentBg;
|
|
83
|
+
auto it = std::sregex_iterator(text.begin(), text.end(), ansiRegex());
|
|
84
|
+
auto end = std::sregex_iterator();
|
|
85
|
+
size_t lastEnd = 0;
|
|
86
|
+
|
|
87
|
+
for (; it != end; ++it) {
|
|
88
|
+
const auto& match = *it;
|
|
89
|
+
auto matchStart = static_cast<size_t>(match.position());
|
|
90
|
+
|
|
91
|
+
if (matchStart > lastEnd) {
|
|
92
|
+
spans.push_back(
|
|
93
|
+
AnsiSpan{
|
|
94
|
+
.text = text.substr(lastEnd, matchStart - lastEnd),
|
|
95
|
+
.foregroundColor = currentFg,
|
|
96
|
+
.backgroundColor = currentBg});
|
|
97
|
+
}
|
|
98
|
+
lastEnd = matchStart + match.length();
|
|
99
|
+
|
|
100
|
+
std::string params = match[1].str();
|
|
101
|
+
// ESC[m (no params) is equivalent to ESC[0m (reset all attributes)
|
|
102
|
+
if (params.empty()) {
|
|
103
|
+
currentFg = std::nullopt;
|
|
104
|
+
currentBg = std::nullopt;
|
|
105
|
+
}
|
|
106
|
+
size_t pos = 0;
|
|
107
|
+
while (pos < params.size()) {
|
|
108
|
+
int code = parseSgrCode(params, pos);
|
|
109
|
+
if (code == 0) {
|
|
110
|
+
currentFg = std::nullopt;
|
|
111
|
+
currentBg = std::nullopt;
|
|
112
|
+
} else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) {
|
|
113
|
+
currentFg = ansiColor(code);
|
|
114
|
+
} else if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) {
|
|
115
|
+
currentBg = ansiColor(code - 10);
|
|
116
|
+
} else if (code == 39) {
|
|
117
|
+
currentFg = std::nullopt;
|
|
118
|
+
} else if (code == 49) {
|
|
119
|
+
currentBg = std::nullopt;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (lastEnd < text.size()) {
|
|
125
|
+
spans.push_back(
|
|
126
|
+
AnsiSpan{
|
|
127
|
+
.text = text.substr(lastEnd),
|
|
128
|
+
.foregroundColor = currentFg,
|
|
129
|
+
.backgroundColor = currentBg});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return spans;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
std::string stripAnsi(const std::string& text) {
|
|
136
|
+
return std::regex_replace(text, ansiRegex(), "");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
} // namespace facebook::react::unstable_redbox
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <optional>
|
|
11
|
+
#include <string>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
namespace facebook::react::unstable_redbox {
|
|
15
|
+
|
|
16
|
+
struct AnsiColor {
|
|
17
|
+
uint8_t r, g, b;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
struct AnsiSpan {
|
|
21
|
+
std::string text;
|
|
22
|
+
std::optional<AnsiColor> foregroundColor;
|
|
23
|
+
std::optional<AnsiColor> backgroundColor;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parse ANSI escape sequences in text and produce a list of styled spans.
|
|
28
|
+
* Uses the Afterglow color theme (matching LogBox's AnsiHighlight.js).
|
|
29
|
+
*/
|
|
30
|
+
std::vector<AnsiSpan> parseAnsi(const std::string &text);
|
|
31
|
+
|
|
32
|
+
/** Strip all ANSI escape sequences from text. */
|
|
33
|
+
std::string stripAnsi(const std::string &text);
|
|
34
|
+
|
|
35
|
+
} // namespace facebook::react::unstable_redbox
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "JscSafeUrl.h"
|
|
9
|
+
|
|
10
|
+
#include <cassert>
|
|
11
|
+
#include <regex> // NOLINT(facebook-hte-BadInclude-regex)
|
|
12
|
+
#include <stdexcept>
|
|
13
|
+
#include <string_view>
|
|
14
|
+
|
|
15
|
+
// @lint-ignore-every CLANGTIDY facebook-hte-StdRegexIsAwful
|
|
16
|
+
|
|
17
|
+
namespace facebook::react::unstable_redbox {
|
|
18
|
+
|
|
19
|
+
namespace {
|
|
20
|
+
|
|
21
|
+
// We use regex-based URL parsing as defined in RFC 3986 because it's easier to
|
|
22
|
+
// determine whether the input is a complete URI, a path-absolute or a
|
|
23
|
+
// path-rootless (as defined in the spec), and be as faithful to the input as
|
|
24
|
+
// possible. This will match any string, and does not imply validity.
|
|
25
|
+
//
|
|
26
|
+
// https://www.rfc-editor.org/rfc/rfc3986#appendix-B
|
|
27
|
+
const std::regex& uriRegex() {
|
|
28
|
+
static const std::regex re(
|
|
29
|
+
R"(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)");
|
|
30
|
+
return re;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
struct ParsedUri {
|
|
34
|
+
std::string_view schemeAndAuthority;
|
|
35
|
+
std::string_view path;
|
|
36
|
+
bool hasQueryPart = false;
|
|
37
|
+
std::string_view queryWithoutQuestionMark;
|
|
38
|
+
std::string_view fragmentWithHash;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
ParsedUri rfc3986Parse(std::string_view url) {
|
|
42
|
+
std::cmatch match;
|
|
43
|
+
if (!std::regex_match(
|
|
44
|
+
url.data(), url.data() + url.size(), match, uriRegex())) {
|
|
45
|
+
throw std::runtime_error("Unexpected error - failed to regex-match URL");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// match[1] = scheme with colon (e.g. "http:")
|
|
49
|
+
// match[3] = authority with slashes (e.g. "//host")
|
|
50
|
+
// match[5] = path
|
|
51
|
+
// match[6] = query with question mark (e.g. "?key=val")
|
|
52
|
+
// match[7] = query without question mark
|
|
53
|
+
// match[8] = fragment with hash (e.g. "#frag")
|
|
54
|
+
|
|
55
|
+
auto viewOf = [&](int group) -> std::string_view {
|
|
56
|
+
if (!match[group].matched) {
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
return {match[group].first, static_cast<size_t>(match[group].length())};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// schemeAndAuthority = (match[1] || "") + (match[3] || "")
|
|
63
|
+
// These are contiguous when both present, but may be individually absent.
|
|
64
|
+
std::string_view schemeAndAuthority;
|
|
65
|
+
if (match[1].matched && match[3].matched) {
|
|
66
|
+
assert(match[1].second == match[3].first);
|
|
67
|
+
schemeAndAuthority = {
|
|
68
|
+
match[1].first, static_cast<size_t>(match[3].second - match[1].first)};
|
|
69
|
+
} else if (match[1].matched) {
|
|
70
|
+
schemeAndAuthority = viewOf(1);
|
|
71
|
+
} else if (match[3].matched) {
|
|
72
|
+
schemeAndAuthority = viewOf(3);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return ParsedUri{
|
|
76
|
+
.schemeAndAuthority = schemeAndAuthority,
|
|
77
|
+
.path = viewOf(5),
|
|
78
|
+
.hasQueryPart = match[6].matched,
|
|
79
|
+
.queryWithoutQuestionMark = viewOf(7),
|
|
80
|
+
.fragmentWithHash = viewOf(8),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
} // namespace
|
|
85
|
+
|
|
86
|
+
bool isJscSafeUrl(std::string_view url) {
|
|
87
|
+
return !rfc3986Parse(url).hasQueryPart;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
std::string toNormalUrl(std::string url) {
|
|
91
|
+
auto parsed = rfc3986Parse(url);
|
|
92
|
+
auto markerPos = parsed.path.find("//&");
|
|
93
|
+
if (markerPos == std::string_view::npos) {
|
|
94
|
+
return url;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// path before //&, then ?, then path after //&
|
|
98
|
+
std::string_view pathBefore = parsed.path.substr(0, markerPos);
|
|
99
|
+
std::string_view pathAfter = parsed.path.substr(markerPos + 3);
|
|
100
|
+
|
|
101
|
+
// We don't expect JSC urls to also have query strings, but interpret
|
|
102
|
+
// liberally and append them.
|
|
103
|
+
bool hasExistingQuery = !parsed.queryWithoutQuestionMark.empty();
|
|
104
|
+
|
|
105
|
+
// Likewise, JSC URLs will usually have their fragments stripped, but
|
|
106
|
+
// preserve if we find one.
|
|
107
|
+
size_t totalSize = parsed.schemeAndAuthority.size() + pathBefore.size() +
|
|
108
|
+
1 /* ? */ + pathAfter.size() +
|
|
109
|
+
(hasExistingQuery ? 1 + parsed.queryWithoutQuestionMark.size() : 0) +
|
|
110
|
+
parsed.fragmentWithHash.size();
|
|
111
|
+
|
|
112
|
+
std::string result;
|
|
113
|
+
result.reserve(totalSize);
|
|
114
|
+
result += parsed.schemeAndAuthority;
|
|
115
|
+
result += pathBefore;
|
|
116
|
+
result += '?';
|
|
117
|
+
result += pathAfter;
|
|
118
|
+
if (hasExistingQuery) {
|
|
119
|
+
result += '&';
|
|
120
|
+
result += parsed.queryWithoutQuestionMark;
|
|
121
|
+
}
|
|
122
|
+
result += parsed.fragmentWithHash;
|
|
123
|
+
assert(result.size() == totalSize);
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
std::string toJscSafeUrl(std::string url) {
|
|
128
|
+
if (!rfc3986Parse(url).hasQueryPart) {
|
|
129
|
+
return url;
|
|
130
|
+
}
|
|
131
|
+
url = toNormalUrl(std::move(url));
|
|
132
|
+
auto parsed = rfc3986Parse(url);
|
|
133
|
+
if (!parsed.queryWithoutQuestionMark.empty() &&
|
|
134
|
+
(parsed.path.empty() || parsed.path == "/")) {
|
|
135
|
+
throw std::invalid_argument(
|
|
136
|
+
"The given URL \"" + url +
|
|
137
|
+
"\" has an empty path and cannot be converted to a JSC-safe format.");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Query strings may contain '?' (e.g. in key or value names) - these
|
|
141
|
+
// must be percent-encoded to form a valid path, and not be stripped.
|
|
142
|
+
// Count them first so we can preallocate exactly.
|
|
143
|
+
bool hasQuery = !parsed.queryWithoutQuestionMark.empty();
|
|
144
|
+
size_t questionMarks = 0;
|
|
145
|
+
if (hasQuery) {
|
|
146
|
+
for (char c : parsed.queryWithoutQuestionMark) {
|
|
147
|
+
if (c == '?') {
|
|
148
|
+
questionMarks++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Each '?' becomes "%3F" (+2 bytes), plus "//&" delimiter (+3 bytes)
|
|
154
|
+
size_t totalSize = parsed.schemeAndAuthority.size() + parsed.path.size() +
|
|
155
|
+
(hasQuery ? 3 + parsed.queryWithoutQuestionMark.size() + questionMarks * 2
|
|
156
|
+
: 0) +
|
|
157
|
+
// We expect JSC to strip this - we don't handle fragments for now.
|
|
158
|
+
parsed.fragmentWithHash.size();
|
|
159
|
+
|
|
160
|
+
std::string result;
|
|
161
|
+
result.reserve(totalSize);
|
|
162
|
+
result += parsed.schemeAndAuthority;
|
|
163
|
+
result += parsed.path;
|
|
164
|
+
if (hasQuery) {
|
|
165
|
+
result += "//&";
|
|
166
|
+
for (char c : parsed.queryWithoutQuestionMark) {
|
|
167
|
+
if (c == '?') {
|
|
168
|
+
result += "%3F";
|
|
169
|
+
} else {
|
|
170
|
+
result += c;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
result += parsed.fragmentWithHash;
|
|
175
|
+
assert(result.size() == totalSize);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
} // namespace facebook::react::unstable_redbox
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <string_view>
|
|
12
|
+
|
|
13
|
+
namespace facebook::react::unstable_redbox {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* These functions are for handling of query-string free URLs, necessitated
|
|
17
|
+
* by query string stripping of URLs in JavaScriptCore stack traces
|
|
18
|
+
* introduced in iOS 16.4. This is a direct port of https://www.npmjs.com/package/jsc-safe-url.
|
|
19
|
+
*
|
|
20
|
+
* See https://github.com/facebook/react-native/issues/36794 for context.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
bool isJscSafeUrl(std::string_view url);
|
|
24
|
+
std::string toNormalUrl(std::string url);
|
|
25
|
+
std::string toJscSafeUrl(std::string url);
|
|
26
|
+
|
|
27
|
+
} // namespace facebook::react::unstable_redbox
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "RedBoxErrorParser.h"
|
|
9
|
+
|
|
10
|
+
#include <regex> // NOLINT(facebook-hte-BadInclude-regex)
|
|
11
|
+
#include <unordered_set>
|
|
12
|
+
|
|
13
|
+
// @lint-ignore-every CLANGTIDY facebook-hte-StdRegexIsAwful
|
|
14
|
+
namespace facebook::react::unstable_redbox {
|
|
15
|
+
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
const std::regex& metroErrorRegex() {
|
|
19
|
+
static const std::regex re(
|
|
20
|
+
R"(^(?:InternalError Metro has encountered an error:) (.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+))");
|
|
21
|
+
return re;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const std::regex& babelTransformErrorRegex() {
|
|
25
|
+
static const std::regex re(
|
|
26
|
+
R"(^(?:TransformError )?(?:SyntaxError: |ReferenceError: )(.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+))");
|
|
27
|
+
return re;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const std::regex& bundleLoadErrorRegex() {
|
|
31
|
+
static const std::regex re(R"(^(\w+) in (\S+): (.+) \((\d+):(\d+)\))");
|
|
32
|
+
return re;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const std::regex& babelCodeFrameErrorRegex() {
|
|
36
|
+
static const std::regex re(
|
|
37
|
+
R"(^(?:TransformError )?(?:.*):? (?:.*?)(\/.*): ([\s\S]+?)\n([ >]{2}[\d\s]+ \|[\s\S]+|\x1b[\s\S]+))");
|
|
38
|
+
return re;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
bool startsWithTransformError(const std::string& msg) {
|
|
42
|
+
return msg.rfind("TransformError ", 0) == 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const std::unordered_set<std::string>& knownBundleLoadErrorTypes() {
|
|
46
|
+
static const std::unordered_set<std::string> types{
|
|
47
|
+
"SyntaxError", "ReferenceError", "TypeError", "UnableToResolveError"};
|
|
48
|
+
return types;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
} // namespace
|
|
52
|
+
|
|
53
|
+
ParsedError parseErrorMessage(
|
|
54
|
+
const std::string& message,
|
|
55
|
+
const std::string& name,
|
|
56
|
+
const std::string& componentStack,
|
|
57
|
+
bool isFatal) {
|
|
58
|
+
std::smatch match;
|
|
59
|
+
|
|
60
|
+
if (message.empty()) {
|
|
61
|
+
return ParsedError{
|
|
62
|
+
.title = isFatal ? "Uncaught Error" : "Error",
|
|
63
|
+
.message = "",
|
|
64
|
+
.codeFrame = std::nullopt,
|
|
65
|
+
.isCompileError = false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 1. Metro internal error
|
|
70
|
+
if (std::regex_search(message, match, metroErrorRegex())) {
|
|
71
|
+
return ParsedError{
|
|
72
|
+
.title = match[1].str().empty() ? "Metro Error" : match[1].str(),
|
|
73
|
+
.message = match[2].str(),
|
|
74
|
+
.codeFrame =
|
|
75
|
+
CodeFrame{
|
|
76
|
+
.content = match[5].str(),
|
|
77
|
+
.fileName = "",
|
|
78
|
+
.row = std::stoi(match[3].str()),
|
|
79
|
+
.column = std::stoi(match[4].str()),
|
|
80
|
+
},
|
|
81
|
+
.isCompileError = true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 2. Babel transform error
|
|
86
|
+
if (std::regex_search(message, match, babelTransformErrorRegex())) {
|
|
87
|
+
return ParsedError{
|
|
88
|
+
.title = "Syntax Error",
|
|
89
|
+
.message = match[2].str(),
|
|
90
|
+
.codeFrame =
|
|
91
|
+
CodeFrame{
|
|
92
|
+
.content = match[5].str(),
|
|
93
|
+
.fileName = match[1].str(),
|
|
94
|
+
.row = std::stoi(match[3].str()),
|
|
95
|
+
.column = std::stoi(match[4].str()),
|
|
96
|
+
},
|
|
97
|
+
.isCompileError = true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 3. Bundle loading error: "ErrorType in /path: message (line:col)"
|
|
102
|
+
if (std::regex_search(message, match, bundleLoadErrorRegex())) {
|
|
103
|
+
const auto& errorType = match[1].str();
|
|
104
|
+
if (knownBundleLoadErrorTypes().count(errorType) > 0) {
|
|
105
|
+
std::string title = errorType == "UnableToResolveError"
|
|
106
|
+
? "Module Not Found"
|
|
107
|
+
: "Syntax Error";
|
|
108
|
+
std::optional<std::string> codeFrameContent;
|
|
109
|
+
auto newlinePos = message.find('\n');
|
|
110
|
+
if (newlinePos != std::string::npos) {
|
|
111
|
+
codeFrameContent = message.substr(newlinePos + 1);
|
|
112
|
+
}
|
|
113
|
+
return ParsedError{
|
|
114
|
+
.title = title,
|
|
115
|
+
.message = match[3].str(),
|
|
116
|
+
.codeFrame =
|
|
117
|
+
CodeFrame{
|
|
118
|
+
.content = codeFrameContent.value_or(""),
|
|
119
|
+
.fileName = match[2].str(),
|
|
120
|
+
.row = std::stoi(match[4].str()),
|
|
121
|
+
.column = std::stoi(match[5].str()),
|
|
122
|
+
},
|
|
123
|
+
.isCompileError = true,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 4. Babel code frame error
|
|
129
|
+
if (std::regex_search(message, match, babelCodeFrameErrorRegex())) {
|
|
130
|
+
return ParsedError{
|
|
131
|
+
.title = "Syntax Error",
|
|
132
|
+
.message = match[2].str(),
|
|
133
|
+
.codeFrame =
|
|
134
|
+
CodeFrame{
|
|
135
|
+
.content = match[3].str(),
|
|
136
|
+
.fileName = match[1].str(),
|
|
137
|
+
},
|
|
138
|
+
.isCompileError = true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 5. Generic transform error (no code frame)
|
|
143
|
+
if (startsWithTransformError(message)) {
|
|
144
|
+
return ParsedError{
|
|
145
|
+
.title = "Syntax Error",
|
|
146
|
+
.message = message,
|
|
147
|
+
.codeFrame = std::nullopt,
|
|
148
|
+
.isCompileError = true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 6. Determine title from context (matching LogBoxInspectorHeader title map)
|
|
153
|
+
std::string title;
|
|
154
|
+
if (!name.empty()) {
|
|
155
|
+
title = name;
|
|
156
|
+
} else if (!componentStack.empty()) {
|
|
157
|
+
title = "Render Error";
|
|
158
|
+
} else if (isFatal) {
|
|
159
|
+
title = "Uncaught Error";
|
|
160
|
+
} else {
|
|
161
|
+
title = "Error";
|
|
162
|
+
}
|
|
163
|
+
return ParsedError{
|
|
164
|
+
.title = title,
|
|
165
|
+
.message = message,
|
|
166
|
+
.codeFrame = std::nullopt,
|
|
167
|
+
.isCompileError = false,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
} // namespace facebook::react::unstable_redbox
|