Skip to content

Commit 0e0cd39

Browse files
Add devsupport target to ReactCxxPlatform (#51902)
Summary: Pull Request resolved: #51902 changelog: [internal] Reviewed By: andrewdacenko Differential Revision: D76240769 fbshipit-source-id: 83d13c0446f86caf66addf82e1a06c5dd81388be
1 parent daff0c9 commit 0e0cd39

18 files changed

Lines changed: 1160 additions & 0 deletions
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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 "DevLoadingViewModule.h"
9+
10+
namespace facebook::react {
11+
12+
const int32_t DEFAULT_TEXT_COLOR = 0xFFFFFFFF;
13+
const int32_t DEFAULT_BACKGROUND_COLOR = 0xFF2584E8;
14+
15+
DevLoadingViewModule::DevLoadingViewModule(
16+
std::shared_ptr<CallInvoker> jsInvoker,
17+
std::weak_ptr<IDevUIDelegate> devUIDelegate)
18+
: NativeDevLoadingViewCxxSpec(jsInvoker),
19+
devUIDelegate_(std::move(devUIDelegate)) {}
20+
21+
DevLoadingViewModule::~DevLoadingViewModule() {
22+
if (auto devUIDelegate = devUIDelegate_.lock()) {
23+
devUIDelegate->hideLoadingView();
24+
}
25+
}
26+
27+
void DevLoadingViewModule::showMessage(
28+
jsi::Runtime& /*rt*/,
29+
const std::string& message,
30+
std::optional<int32_t> textColor,
31+
std::optional<int32_t> backgroundColor) {
32+
if (auto devUIDelegate = devUIDelegate_.lock()) {
33+
devUIDelegate->showLoadingView(
34+
message,
35+
SharedColor{textColor.value_or(DEFAULT_TEXT_COLOR)},
36+
SharedColor{backgroundColor.value_or(DEFAULT_BACKGROUND_COLOR)});
37+
}
38+
}
39+
40+
void DevLoadingViewModule::hide(jsi::Runtime& /*rt*/) {
41+
if (auto devUIDelegate = devUIDelegate_.lock()) {
42+
devUIDelegate->hideLoadingView();
43+
}
44+
}
45+
46+
} // namespace facebook::react
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 "IDevUIDelegate.h"
11+
12+
#include <FBReactNativeSpec/FBReactNativeSpecJSI.h>
13+
#include <jsi/jsi.h>
14+
#include <optional>
15+
16+
namespace facebook::react {
17+
18+
class DevLoadingViewModule
19+
: public NativeDevLoadingViewCxxSpec<DevLoadingViewModule> {
20+
public:
21+
DevLoadingViewModule(
22+
std::shared_ptr<CallInvoker> jsInvoker,
23+
std::weak_ptr<IDevUIDelegate> devUIDelegate);
24+
25+
~DevLoadingViewModule() override;
26+
27+
void showMessage(
28+
jsi::Runtime& rt,
29+
const std::string& message,
30+
std::optional<int32_t> textColor,
31+
std::optional<int32_t> backgroundColor);
32+
33+
void hide(jsi::Runtime& rt);
34+
35+
private:
36+
std::weak_ptr<IDevUIDelegate> devUIDelegate_;
37+
};
38+
39+
} // namespace facebook::react
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
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 "DevServerHelper.h"
9+
10+
#include <fmt/format.h>
11+
#include <glog/logging.h>
12+
#include <jsinspector-modern/InspectorFlags.h>
13+
#include <openssl/sha.h>
14+
#include <react/devsupport/inspector/Inspector.h>
15+
#include <react/devsupport/inspector/InspectorPackagerConnectionDelegate.h>
16+
#include <iomanip>
17+
#include <regex>
18+
19+
namespace {
20+
21+
constexpr std::string_view DEFAULT_PLATFORM = "android";
22+
23+
std::string SHA256(const std::string& input) {
24+
std::array<unsigned char, SHA256_DIGEST_LENGTH> hash{};
25+
SHA256_CTX sha256;
26+
SHA256_Init(&sha256);
27+
SHA256_Update(&sha256, input.c_str(), input.size());
28+
SHA256_Final(hash.data(), &sha256);
29+
std::stringstream ss;
30+
for (unsigned char i : hash) {
31+
ss << std::hex << std::setw(2) << std::setfill('0') << (int)i;
32+
}
33+
return ss.str();
34+
}
35+
36+
std::string urlEscape(const std::string& str) {
37+
std::regex pattern("[^a-zA-Z0-9._~-]");
38+
return std::regex_replace(str, pattern, "");
39+
}
40+
41+
} // namespace
42+
43+
namespace facebook::react {
44+
45+
DevServerHelper::DevServerHelper(
46+
std::string appId,
47+
std::string deviceName,
48+
const HttpClientFactory& httpClientFactory,
49+
JavaScriptModuleCallback javaScriptModuleCallback) noexcept
50+
: appId_(std::move(appId)),
51+
deviceName_(std::move(deviceName)),
52+
httpClient_(httpClientFactory()),
53+
javaScriptModuleCallback_(std::move(javaScriptModuleCallback)) {
54+
deviceId_ = SHA256(fmt::format("{}-{}", deviceName_, appId_));
55+
}
56+
57+
std::future<std::string> DevServerHelper::downloadBundleResourceSync(
58+
const std::string& jsBundleUrl,
59+
DownloadProgressCallback&& downloadProgressCallback) {
60+
auto promise = std::make_shared<std::promise<std::string>>();
61+
http::NetworkCallbacks callbacks{
62+
.onBody =
63+
[jsBundleUrl, promise, downloadProgressCallback](
64+
std::unique_ptr<folly::IOBuf> ioBuf) {
65+
std::string responseStr = ioBuf->moveToFbString().toStdString();
66+
promise->set_value(responseStr);
67+
},
68+
.onResponseComplete =
69+
[jsBundleUrl, promise, downloadProgressCallback](
70+
const std::string& error, bool timeoutError) {
71+
if (!error.empty() || timeoutError) {
72+
if (downloadProgressCallback) {
73+
downloadProgressCallback(DownloadProgressStatus::FAILED);
74+
}
75+
std::string errorMessage =
76+
"Failed to download JS bundle from Url: " + jsBundleUrl +
77+
". Error: " + (error.empty() ? "Timeout" : error);
78+
LOG(WARNING) << errorMessage;
79+
try {
80+
throw std::runtime_error(errorMessage);
81+
} catch (...) {
82+
try {
83+
promise->set_exception(std::current_exception());
84+
} catch (...) {
85+
}
86+
}
87+
} else if (downloadProgressCallback) {
88+
downloadProgressCallback(DownloadProgressStatus::FINISHED);
89+
}
90+
}};
91+
httpClient_->sendRequest(std::move(callbacks), "GET", jsBundleUrl);
92+
if (downloadProgressCallback) {
93+
downloadProgressCallback(DownloadProgressStatus::STARTED);
94+
}
95+
return promise->get_future();
96+
}
97+
98+
std::string DevServerHelper::getInspectorUrl() const {
99+
bool isProfilingBuild =
100+
jsinspector_modern::InspectorFlags::getInstance().getIsProfilingBuild();
101+
102+
return fmt::format(
103+
"ws://{}:{}/inspector/device?name={}&app={}&device={}&profiling={}",
104+
DEFAULT_DEV_SERVER_HOST,
105+
DEFAULT_DEV_SERVER_PORT,
106+
urlEscape(deviceName_),
107+
appId_,
108+
deviceId_,
109+
isProfilingBuild);
110+
}
111+
112+
std::string DevServerHelper::getBundleUrl() const {
113+
if (sourcePath_.empty()) {
114+
return "";
115+
}
116+
117+
bool dev = true;
118+
bool lazy = dev;
119+
bool minify = false;
120+
bool splitBundle = false;
121+
bool modulesOnly = splitBundle;
122+
bool runModule = !splitBundle;
123+
return fmt::format(
124+
"http://{}:{}/{}.bundle?platform={}&dev={}&lazy={}&minify={}&app={}&modulesOnly={}&runModule={}&inlineSourceMap=false&excludeSource=true&sourcePaths=url-server",
125+
DEFAULT_DEV_SERVER_HOST,
126+
DEFAULT_DEV_SERVER_PORT,
127+
sourcePath_,
128+
DEFAULT_PLATFORM,
129+
dev,
130+
lazy,
131+
minify,
132+
appId_,
133+
modulesOnly,
134+
runModule);
135+
};
136+
137+
std::string DevServerHelper::getPackagerConnectionUrl() const {
138+
return fmt::format(
139+
"ws://{}:{}/message", DEFAULT_DEV_SERVER_HOST, DEFAULT_DEV_SERVER_PORT);
140+
}
141+
142+
void DevServerHelper::openDebugger() const {
143+
auto requestUrl = fmt::format(
144+
"http://{}:{}/open-debugger?device={}",
145+
DEFAULT_DEV_SERVER_HOST,
146+
DEFAULT_DEV_SERVER_PORT,
147+
deviceId_);
148+
httpClient_->sendRequest({}, "POST", requestUrl);
149+
}
150+
151+
void DevServerHelper::setupHMRClient() const {
152+
folly::dynamic params = folly::dynamic::array(
153+
DEFAULT_PLATFORM,
154+
sourcePath_,
155+
DEFAULT_DEV_SERVER_HOST,
156+
DEFAULT_DEV_SERVER_PORT,
157+
true /*enable*/);
158+
javaScriptModuleCallback_("HMRClient", "setup", std::move(params));
159+
}
160+
161+
} // namespace facebook::react
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 <folly/dynamic.h>
11+
#include <react/http/IHttpClient.h>
12+
#include <react/nativemodule/JavaScriptModule.h>
13+
#include <future>
14+
#include <memory>
15+
#include <string>
16+
17+
namespace facebook::react {
18+
19+
namespace {
20+
21+
constexpr std::string_view DEFAULT_DEV_SERVER_HOST = "localhost";
22+
constexpr uint32_t DEFAULT_DEV_SERVER_PORT = 8081;
23+
24+
} // namespace
25+
26+
class DevServerHelper {
27+
public:
28+
enum class DownloadProgressStatus : short { STARTED, FAILED, FINISHED };
29+
using DownloadProgressCallback = std::function<void(DownloadProgressStatus)>;
30+
31+
DevServerHelper(
32+
std::string appId,
33+
std::string deviceName,
34+
const HttpClientFactory& httpClientFactory,
35+
JavaScriptModuleCallback javaScriptModuleCallback) noexcept;
36+
~DevServerHelper() noexcept = default;
37+
38+
std::future<std::string> downloadBundleResourceSync(
39+
const std::string& jsBundleUrl,
40+
DownloadProgressCallback&& downloadProgressCallback = nullptr);
41+
42+
std::string getInspectorUrl() const;
43+
44+
std::string getBundleUrl() const;
45+
46+
std::string getPackagerConnectionUrl() const;
47+
48+
void openDebugger() const;
49+
50+
void setSourcePath(const std::string& sourcePath) {
51+
sourcePath_ = sourcePath;
52+
}
53+
54+
void setupHMRClient() const;
55+
56+
private:
57+
std::string appId_;
58+
std::string deviceName_;
59+
std::unique_ptr<IHttpClient> httpClient_;
60+
JavaScriptModuleCallback javaScriptModuleCallback_;
61+
std::string deviceId_;
62+
std::string sourcePath_;
63+
};
64+
65+
} // namespace facebook::react
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 "DevSettingsModule.h"
9+
10+
#include <glog/logging.h>
11+
12+
namespace facebook::react {
13+
14+
void DevSettingsModule::reload(jsi::Runtime& /*rt*/) {
15+
LOG(INFO) << "DevSettingsModule::reload";
16+
if (liveReloadCallback_) {
17+
liveReloadCallback_();
18+
}
19+
}
20+
21+
void DevSettingsModule::reloadWithReason(
22+
jsi::Runtime& /*rt*/,
23+
const std::string& reason) {
24+
LOG(INFO) << "DevSettingsModule::reloadWithReason: " << reason;
25+
if (liveReloadCallback_) {
26+
liveReloadCallback_();
27+
}
28+
}
29+
30+
void DevSettingsModule::onFastRefresh(jsi::Runtime& /*rt*/) {
31+
LOG(INFO) << "DevSettingsModule::onFastRefresh";
32+
}
33+
34+
void DevSettingsModule::setHotLoadingEnabled(
35+
jsi::Runtime& /*rt*/,
36+
bool isHotLoadingEnabled) {
37+
LOG(INFO) << "DevSettingsModule::setHotLoadingEnabled: "
38+
<< (int)isHotLoadingEnabled;
39+
}
40+
41+
void DevSettingsModule::setIsDebuggingRemotely(
42+
jsi::Runtime& /*rt*/,
43+
bool /*isDebuggingRemotelyEnabled*/) {}
44+
45+
void DevSettingsModule::setProfilingEnabled(
46+
jsi::Runtime& /*rt*/,
47+
bool /*isProfilingEnabled*/) {}
48+
49+
void DevSettingsModule::toggleElementInspector(jsi::Runtime& rt) {}
50+
51+
void DevSettingsModule::addMenuItem(
52+
jsi::Runtime& /*rt*/,
53+
const std::string& /*title*/) {}
54+
55+
void DevSettingsModule::setIsShakeToShowDevMenuEnabled(
56+
jsi::Runtime& /*rt*/,
57+
bool /*enabled*/) {}
58+
59+
void DevSettingsModule::openDebugger(jsi::Runtime& /*rt*/) {
60+
if (auto devServerHelper = devServerHelper_.lock()) {
61+
devServerHelper->openDebugger();
62+
}
63+
}
64+
65+
void DevSettingsModule::addListener(
66+
jsi::Runtime& /*rt*/,
67+
const std::string& /*eventName*/) {
68+
// noop
69+
}
70+
71+
void DevSettingsModule::removeListeners(
72+
jsi::Runtime& /*rt*/,
73+
double /*count*/) {
74+
// noop
75+
}
76+
77+
} // namespace facebook::react

0 commit comments

Comments
 (0)