react-native-windows 0.0.0-canary.480 → 0.0.0-canary.481

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.
@@ -100,7 +100,8 @@ namespace Microsoft.ReactNative
100
100
 
101
101
  DOC_STRING(
102
102
  "The name of the JavaScript bundle file to load. This should be a relative path from @.BundleRootPath. "
103
- "The `.bundle` extension will be appended to the end, when looking for the bundle file.")
103
+ "The `.bundle` extension will be appended to the end, when looking for the bundle file.\n"
104
+ "If using an embedded RCDATA resource, this identifies the resource ID that stores the bundle. See @.BundleRootPath.")
104
105
  DOC_DEFAULT("index.windows")
105
106
  String JavaScriptBundleFile { get; set; };
106
107
 
@@ -176,7 +177,16 @@ namespace Microsoft.ReactNative
176
177
  "If this is not provided, the value of @.JavaScriptBundleFile is used.")
177
178
  String DebugBundlePath { get; set; };
178
179
 
179
- DOC_STRING("Base path used for the location of the bundle.")
180
+ DOC_STRING(
181
+ "Base path used for the location of the bundle. \n"
182
+ "This can be an `ms-appx://` or `ms-appdata://` URI (if the app is UWP or packaged using MSIX), "
183
+ "a filesystem path, or a URI pointing at an embedded resource.\n"
184
+ "Examples:\n\n"
185
+ "- `ms-appx:///Bundle` - locates the bundle in the MSIX package. See [URI schemes](https://docs.microsoft.com/windows/uwp/app-resources/uri-schemes) for other UWP/MSIX valid URI formats."
186
+ "- `C:\\foo\\bar` - locates the bundle in the local filesystem. Note [UWP app file access permissions](https://docs.microsoft.com/windows/uwp/files/file-access-permissions)."
187
+ "- `resource://moduleName` - locates the bundle as an embedded RCDATA resource in moduleName. Specify the resource ID in @.JavaScriptBundleFile."
188
+ "- `resource://` - locates the bundle as an embedded RCDATA resource in the running process's module. Specify the resource ID in @.JavaScriptBundleFile."
189
+ )
180
190
  DOC_DEFAULT("ms-appx:///Bundle/")
181
191
  String BundleRootPath { get; set; };
182
192
 
@@ -15,6 +15,45 @@
15
15
 
16
16
  namespace Microsoft::ReactNative {
17
17
 
18
+ std::string GetBundleFromEmbeddedResource(winrt::hstring str) {
19
+ winrt::Windows::Foundation::Uri uri(str);
20
+ auto moduleName = uri.Host();
21
+ auto path = uri.Path();
22
+ // skip past the leading / slash
23
+ auto resourceName = path.c_str() + 1;
24
+
25
+ auto hmodule = GetModuleHandle(moduleName != L"" ? moduleName.c_str() : nullptr);
26
+ if (!hmodule) {
27
+ throw std::invalid_argument(fmt::format("Couldn't find module {}", winrt::to_string(moduleName)));
28
+ }
29
+
30
+ auto resource = FindResourceW(hmodule, resourceName, RT_RCDATA);
31
+ if (!resource) {
32
+ throw std::invalid_argument(fmt::format(
33
+ "Couldn't find resource {} in module {}", winrt::to_string(resourceName), winrt::to_string(moduleName)));
34
+ }
35
+
36
+ auto hglobal = LoadResource(hmodule, resource);
37
+ if (!hglobal) {
38
+ throw std::invalid_argument(fmt::format(
39
+ "Couldn't load resource {} in module {}", winrt::to_string(resourceName), winrt::to_string(moduleName)));
40
+ }
41
+
42
+ auto start = static_cast<char *>(LockResource(hglobal));
43
+ if (!start) {
44
+ throw std::invalid_argument(fmt::format(
45
+ "Couldn't lock resource {} in module {}", winrt::to_string(resourceName), winrt::to_string(moduleName)));
46
+ }
47
+
48
+ auto size = SizeofResource(hmodule, resource);
49
+ if (!size) {
50
+ throw std::invalid_argument(fmt::format(
51
+ "Couldn't get size of resource {} in module {}", winrt::to_string(resourceName), winrt::to_string(moduleName)));
52
+ }
53
+
54
+ return std::string(start, start + size);
55
+ }
56
+
18
57
  std::future<std::string> LocalBundleReader::LoadBundleAsync(const std::string &bundleUri) {
19
58
  winrt::hstring str(Microsoft::Common::Unicode::Utf8ToUtf16(bundleUri));
20
59
 
@@ -26,6 +65,8 @@ std::future<std::string> LocalBundleReader::LoadBundleAsync(const std::string &b
26
65
  if (bundleUri._Starts_with("ms-app")) {
27
66
  winrt::Windows::Foundation::Uri uri(str);
28
67
  file = co_await winrt::Windows::Storage::StorageFile::GetFileFromApplicationUriAsync(uri);
68
+ } else if (bundleUri._Starts_with("resource://")) {
69
+ co_return GetBundleFromEmbeddedResource(str);
29
70
  } else {
30
71
  file = co_await winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(str);
31
72
  }
@@ -10,7 +10,7 @@
10
10
  -->
11
11
  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
12
12
  <PropertyGroup>
13
- <ReactNativeWindowsVersion>0.0.0-canary.480</ReactNativeWindowsVersion>
13
+ <ReactNativeWindowsVersion>0.0.0-canary.481</ReactNativeWindowsVersion>
14
14
  <ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
15
15
  <ReactNativeWindowsMinor>0</ReactNativeWindowsMinor>
16
16
  <ReactNativeWindowsPatch>0</ReactNativeWindowsPatch>
@@ -509,7 +509,15 @@ void InstanceImpl::loadBundleInternal(std::string &&jsBundleRelativePath, bool s
509
509
  std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / jsBundleRelativePath).string();
510
510
  auto bundleString = FileMappingBigString::fromPath(bundlePath);
511
511
  #else
512
- std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / (jsBundleRelativePath + ".bundle")).string();
512
+ std::string bundlePath;
513
+ if (m_devSettings->bundleRootPath._Starts_with("resource://")) {
514
+ auto uri = winrt::Windows::Foundation::Uri(
515
+ winrt::to_hstring(m_devSettings->bundleRootPath), winrt::to_hstring(jsBundleRelativePath));
516
+ bundlePath = winrt::to_string(uri.ToString());
517
+ } else {
518
+ bundlePath = (fs::path(m_devSettings->bundleRootPath) / (jsBundleRelativePath + ".bundle")).string();
519
+ }
520
+
513
521
  auto bundleString = std::make_unique<::Microsoft::ReactNative::StorageFileBigString>(bundlePath);
514
522
  #endif
515
523
  m_innerInstance->loadScriptFromString(std::move(bundleString), std::move(jsBundleRelativePath), synchronously);
@@ -517,11 +525,9 @@ void InstanceImpl::loadBundleInternal(std::string &&jsBundleRelativePath, bool s
517
525
  } catch (const std::exception &e) {
518
526
  m_devSettings->errorCallback(e.what());
519
527
  } catch (const winrt::hresult_error &hrerr) {
520
- std::stringstream ss;
521
- ss << "[" << std::hex << std::showbase << std::setw(8) << static_cast<uint32_t>(hrerr.code()) << "] "
522
- << winrt::to_string(hrerr.message());
528
+ auto error = fmt::format("[0x{:0>8x}] {}", static_cast<uint32_t>(hrerr.code()), winrt::to_string(hrerr.message()));
523
529
 
524
- m_devSettings->errorCallback(std::move(ss.str()));
530
+ m_devSettings->errorCallback(std::move(error));
525
531
  }
526
532
  }
527
533
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-windows",
3
- "version": "0.0.0-canary.480",
3
+ "version": "0.0.0-canary.481",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -74,7 +74,7 @@ typedef int LogSeverity;
74
74
  inline void FlushLogFiles(LogSeverity min_severity) {}
75
75
 
76
76
  #define google GlogStub
77
- static const int INFO = 1;
77
+ static const int GLOG_INFO = 1;
78
78
 
79
79
  #endif
80
80
 
@@ -1,64 +0,0 @@
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
- // No header guards since it is legitimately possible to include this file more
9
- // than once with and without REACT_NATIVE_DEBUG.
10
-
11
- // react_native_assert allows us to opt-in to specific asserts on Android and
12
- // test before moving on. When all issues have been found, maybe we can use
13
- // `UNDEBUG` flag to disable NDEBUG in debug builds on Android.
14
-
15
- #include "flags.h"
16
-
17
- #undef react_native_assert
18
-
19
- #ifndef REACT_NATIVE_DEBUG
20
-
21
- #define react_native_assert(e) ((void)0)
22
-
23
- #else // REACT_NATIVE_DEBUG
24
-
25
- #ifdef __ANDROID__
26
-
27
- #include <android/log.h>
28
-
29
- #ifdef __cplusplus
30
- extern "C" {
31
- #endif // __cplusplus
32
- void react_native_assert_fail(
33
- const char *func,
34
- const char *file,
35
- int line,
36
- const char *expr);
37
- #ifdef __cplusplus
38
- }
39
- #endif // __cpusplus
40
-
41
- #define react_native_assert(e) \
42
- ((e) ? (void)0 : react_native_assert_fail(__func__, __FILE__, __LINE__, #e))
43
-
44
- #else // __ANDROID__
45
-
46
- #include <glog/logging.h>
47
- #include <cassert>
48
-
49
- // For all platforms, but iOS+Xcode especially: flush logs because some might be
50
- // lost on iOS if an assert is hit right after this. If you are trying to debug
51
- // something actively and have added lots of LOG statements to track down an
52
- // issue, there is race between flushing the final logs and stopping execution
53
- // when the assert hits. Thus, if we know an assert will fail, we force flushing
54
- // to happen right before the assert.
55
- #define react_native_assert(cond) \
56
- if (!(cond)) { \
57
- LOG(ERROR) << "react_native_assert failure: " << #cond; \
58
- google::FlushLogFiles(google::INFO); \
59
- assert(cond); \
60
- }
61
-
62
- #endif // platforms besides __ANDROID__
63
-
64
- #endif // REACT_NATIVE_DEBUG
@@ -1,310 +0,0 @@
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 "StubViewTree.h"
9
-
10
- #include <glog/logging.h>
11
- #include <react/debug/react_native_assert.h>
12
-
13
- #ifdef STUB_VIEW_TREE_VERBOSE
14
- #define STUB_VIEW_LOG(code) code
15
- #else
16
- #define STUB_VIEW_LOG(code)
17
- #endif
18
-
19
- namespace facebook {
20
- namespace react {
21
-
22
- StubViewTree::StubViewTree(ShadowView const &shadowView) {
23
- auto view = std::make_shared<StubView>();
24
- view->update(shadowView);
25
- rootTag = shadowView.tag;
26
- registry[shadowView.tag] = view;
27
- }
28
-
29
- StubView const &StubViewTree::getRootStubView() const {
30
- return *registry.at(rootTag);
31
- }
32
-
33
- StubView const &StubViewTree::getStubView(Tag tag) const {
34
- return *registry.at(tag);
35
- }
36
-
37
- size_t StubViewTree::size() const {
38
- return registry.size();
39
- }
40
-
41
- void StubViewTree::mutate(ShadowViewMutationList const &mutations) {
42
- STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Mutating Begin"; });
43
- for (auto const &mutation : mutations) {
44
- switch (mutation.type) {
45
- case ShadowViewMutation::Create: {
46
- react_native_assert(mutation.parentShadowView == ShadowView{});
47
- react_native_assert(mutation.oldChildShadowView == ShadowView{});
48
- react_native_assert(mutation.newChildShadowView.props);
49
- auto stubView = std::make_shared<StubView>();
50
- stubView->update(mutation.newChildShadowView);
51
- auto tag = mutation.newChildShadowView.tag;
52
- STUB_VIEW_LOG({
53
- LOG(ERROR) << "StubView: Create [" << tag << "] ##"
54
- << std::hash<ShadowView>{}((ShadowView)*stubView);
55
- });
56
- react_native_assert(registry.find(tag) == registry.end());
57
- registry[tag] = stubView;
58
- break;
59
- }
60
-
61
- case ShadowViewMutation::Delete: {
62
- STUB_VIEW_LOG({
63
- LOG(ERROR) << "StubView: Delete [" << mutation.oldChildShadowView.tag
64
- << "] ##"
65
- << std::hash<ShadowView>{}(mutation.oldChildShadowView);
66
- });
67
- react_native_assert(mutation.parentShadowView == ShadowView{});
68
- react_native_assert(mutation.newChildShadowView == ShadowView{});
69
- auto tag = mutation.oldChildShadowView.tag;
70
- react_native_assert(registry.find(tag) != registry.end());
71
- auto stubView = registry[tag];
72
- if ((ShadowView)(*stubView) != mutation.oldChildShadowView) {
73
- LOG(ERROR)
74
- << "StubView: ASSERT FAILURE: DELETE mutation assertion failure: oldChildShadowView does not match stubView: ["
75
- << mutation.oldChildShadowView.tag << "] stub hash: ##"
76
- << std::hash<ShadowView>{}((ShadowView)*stubView)
77
- << " old mutation hash: ##"
78
- << std::hash<ShadowView>{}(mutation.oldChildShadowView);
79
- #ifdef RN_DEBUG_STRING_CONVERTIBLE
80
- LOG(ERROR) << "StubView: "
81
- << getDebugPropsDescription((ShadowView)*stubView, {});
82
- LOG(ERROR) << "OldChildShadowView: "
83
- << getDebugPropsDescription(
84
- mutation.oldChildShadowView, {});
85
- #endif
86
- }
87
- react_native_assert(
88
- (ShadowView)(*stubView) == mutation.oldChildShadowView);
89
- registry.erase(tag);
90
- break;
91
- }
92
-
93
- case ShadowViewMutation::Insert: {
94
- if (!mutation.mutatedViewIsVirtual()) {
95
- react_native_assert(mutation.oldChildShadowView == ShadowView{});
96
- auto parentTag = mutation.parentShadowView.tag;
97
- auto childTag = mutation.newChildShadowView.tag;
98
- if (registry.find(parentTag) == registry.end()) {
99
- LOG(ERROR)
100
- << "StubView: ASSERT FAILURE: INSERT mutation assertion failure: parentTag not found: ["
101
- << parentTag << "] inserting child: [" << childTag << "]";
102
- }
103
- if (registry.find(childTag) == registry.end()) {
104
- LOG(ERROR)
105
- << "StubView: ASSERT FAILURE: INSERT mutation assertion failure: childTag not found: ["
106
- << parentTag << "] inserting child: [" << childTag << "]";
107
- }
108
- react_native_assert(registry.find(parentTag) != registry.end());
109
- auto parentStubView = registry[parentTag];
110
- react_native_assert(registry.find(childTag) != registry.end());
111
- auto childStubView = registry[childTag];
112
- childStubView->update(mutation.newChildShadowView);
113
- STUB_VIEW_LOG({
114
- LOG(ERROR) << "StubView: Insert [" << childTag << "] into ["
115
- << parentTag << "] @" << mutation.index << "("
116
- << parentStubView->children.size() << " children)";
117
- });
118
- react_native_assert(childStubView->parentTag == NO_VIEW_TAG);
119
- react_native_assert(
120
- mutation.index >= 0 &&
121
- parentStubView->children.size() >=
122
- static_cast<size_t>(mutation.index));
123
- childStubView->parentTag = parentTag;
124
- parentStubView->children.insert(
125
- parentStubView->children.begin() + mutation.index, childStubView);
126
- } else {
127
- auto childTag = mutation.newChildShadowView.tag;
128
- react_native_assert(registry.find(childTag) != registry.end());
129
- auto childStubView = registry[childTag];
130
- childStubView->update(mutation.newChildShadowView);
131
- }
132
- break;
133
- }
134
-
135
- case ShadowViewMutation::Remove: {
136
- if (!mutation.mutatedViewIsVirtual()) {
137
- react_native_assert(mutation.newChildShadowView == ShadowView{});
138
- auto parentTag = mutation.parentShadowView.tag;
139
- auto childTag = mutation.oldChildShadowView.tag;
140
- if (registry.find(parentTag) == registry.end()) {
141
- LOG(ERROR)
142
- << "StubView: ASSERT FAILURE: REMOVE mutation assertion failure: parentTag not found: ["
143
- << parentTag << "] removing child: [" << childTag << "]";
144
- }
145
- react_native_assert(registry.find(parentTag) != registry.end());
146
- auto parentStubView = registry[parentTag];
147
- STUB_VIEW_LOG({
148
- LOG(ERROR) << "StubView: Remove [" << childTag << "] from ["
149
- << parentTag << "] @" << mutation.index << " with "
150
- << parentStubView->children.size() << " children";
151
- });
152
- react_native_assert(
153
- mutation.index >= 0 &&
154
- parentStubView->children.size() >
155
- static_cast<size_t>(mutation.index));
156
- react_native_assert(registry.find(childTag) != registry.end());
157
- auto childStubView = registry[childTag];
158
- if ((ShadowView)(*childStubView) != mutation.oldChildShadowView) {
159
- LOG(ERROR)
160
- << "StubView: ASSERT FAILURE: REMOVE mutation assertion failure: oldChildShadowView does not match oldStubView: ["
161
- << mutation.oldChildShadowView.tag << "] stub hash: ##"
162
- << std::hash<ShadowView>{}((ShadowView)*childStubView)
163
- << " old mutation hash: ##"
164
- << std::hash<ShadowView>{}(mutation.oldChildShadowView);
165
- #ifdef RN_DEBUG_STRING_CONVERTIBLE
166
- LOG(ERROR) << "ChildStubView: "
167
- << getDebugPropsDescription(
168
- (ShadowView)*childStubView, {});
169
- LOG(ERROR) << "OldChildShadowView: "
170
- << getDebugPropsDescription(
171
- mutation.oldChildShadowView, {});
172
- #endif
173
- }
174
- react_native_assert(
175
- (ShadowView)(*childStubView) == mutation.oldChildShadowView);
176
- react_native_assert(childStubView->parentTag == parentTag);
177
- STUB_VIEW_LOG({
178
- std::string strChildList = "";
179
- int i = 0;
180
- for (auto const &child : parentStubView->children) {
181
- strChildList.append(std::to_string(i));
182
- strChildList.append(":");
183
- strChildList.append(std::to_string(child->tag));
184
- strChildList.append(", ");
185
- i++;
186
- }
187
- LOG(ERROR) << "StubView: BEFORE REMOVE: Children of " << parentTag
188
- << ": " << strChildList;
189
- });
190
- react_native_assert(
191
- mutation.index >= 0 &&
192
- parentStubView->children.size() >
193
- static_cast<size_t>(mutation.index) &&
194
- parentStubView->children[mutation.index]->tag ==
195
- childStubView->tag);
196
- childStubView->parentTag = NO_VIEW_TAG;
197
- parentStubView->children.erase(
198
- parentStubView->children.begin() + mutation.index);
199
- }
200
- break;
201
- }
202
-
203
- case ShadowViewMutation::Update: {
204
- STUB_VIEW_LOG({
205
- LOG(ERROR) << "StubView: Update [" << mutation.newChildShadowView.tag
206
- << "] old hash: ##"
207
- << std::hash<ShadowView>{}(mutation.oldChildShadowView)
208
- << " new hash: ##"
209
- << std::hash<ShadowView>{}(mutation.newChildShadowView);
210
- });
211
- react_native_assert(mutation.oldChildShadowView.tag != 0);
212
- react_native_assert(mutation.newChildShadowView.tag != 0);
213
- react_native_assert(mutation.newChildShadowView.props);
214
- react_native_assert(
215
- mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag);
216
- react_native_assert(
217
- registry.find(mutation.newChildShadowView.tag) != registry.end());
218
- auto oldStubView = registry[mutation.newChildShadowView.tag];
219
- react_native_assert(oldStubView->tag != 0);
220
- if ((ShadowView)(*oldStubView) != mutation.oldChildShadowView) {
221
- LOG(ERROR)
222
- << "StubView: ASSERT FAILURE: UPDATE mutation assertion failure: oldChildShadowView does not match oldStubView: ["
223
- << mutation.oldChildShadowView.tag << "] old stub hash: ##"
224
- << std::hash<ShadowView>{}((ShadowView)*oldStubView)
225
- << " old mutation hash: ##"
226
- << std::hash<ShadowView>{}(mutation.oldChildShadowView);
227
- #ifdef RN_DEBUG_STRING_CONVERTIBLE
228
- LOG(ERROR) << "OldStubView: "
229
- << getDebugPropsDescription((ShadowView)*oldStubView, {});
230
- LOG(ERROR) << "OldChildShadowView: "
231
- << getDebugPropsDescription(
232
- mutation.oldChildShadowView, {});
233
- #endif
234
- }
235
- react_native_assert(
236
- (ShadowView)(*oldStubView) == mutation.oldChildShadowView);
237
- oldStubView->update(mutation.newChildShadowView);
238
-
239
- // Hash for stub view and the ShadowView should be identical - this
240
- // tests that StubView and ShadowView hash are equivalent.
241
- react_native_assert(
242
- std::hash<ShadowView>{}((ShadowView)*oldStubView) ==
243
- std::hash<ShadowView>{}(mutation.newChildShadowView));
244
-
245
- break;
246
- }
247
- }
248
- }
249
- STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Mutating End"; });
250
-
251
- // For iOS especially: flush logs because some might be lost on iOS if an
252
- // assert is hit right after this.
253
- google::FlushLogFiles(google::INFO);
254
- }
255
-
256
- bool operator==(StubViewTree const &lhs, StubViewTree const &rhs) {
257
- if (lhs.registry.size() != rhs.registry.size()) {
258
- STUB_VIEW_LOG({
259
- LOG(ERROR) << "Registry sizes are different. Sizes: LHS: "
260
- << lhs.registry.size() << " RHS: " << rhs.registry.size();
261
-
262
- [&](std::ostream &stream) -> std::ostream & {
263
- stream << "Tags in LHS: ";
264
- for (auto const &pair : lhs.registry) {
265
- auto &lhsStubView = *lhs.registry.at(pair.first);
266
- stream << "[" << lhsStubView.tag << "]##"
267
- << std::hash<ShadowView>{}((ShadowView)lhsStubView) << " ";
268
- }
269
- return stream;
270
- }(LOG(ERROR));
271
-
272
- [&](std::ostream &stream) -> std::ostream & {
273
- stream << "Tags in RHS: ";
274
- for (auto const &pair : rhs.registry) {
275
- auto &rhsStubView = *rhs.registry.at(pair.first);
276
- stream << "[" << rhsStubView.tag << "]##"
277
- << std::hash<ShadowView>{}((ShadowView)rhsStubView) << " ";
278
- }
279
- return stream;
280
- }(LOG(ERROR));
281
- });
282
-
283
- return false;
284
- }
285
-
286
- for (auto const &pair : lhs.registry) {
287
- auto &lhsStubView = *lhs.registry.at(pair.first);
288
- auto &rhsStubView = *rhs.registry.at(pair.first);
289
-
290
- if (lhsStubView != rhsStubView) {
291
- STUB_VIEW_LOG({
292
- LOG(ERROR) << "Registry entries are different. LHS: ["
293
- << lhsStubView.tag << "] ##"
294
- << std::hash<ShadowView>{}((ShadowView)lhsStubView)
295
- << " RHS: [" << rhsStubView.tag << "] ##"
296
- << std::hash<ShadowView>{}((ShadowView)rhsStubView);
297
- });
298
- return false;
299
- }
300
- }
301
-
302
- return true;
303
- }
304
-
305
- bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs) {
306
- return !(lhs == rhs);
307
- }
308
-
309
- } // namespace react
310
- } // namespace facebook