react-native-unistyles 3.4.0 → 3.5.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.
Files changed (185) hide show
  1. package/README.md +23 -7
  2. package/android/build.gradle +12 -1
  3. package/android/src/main/cxx/NativeUnistylesModule.cpp +8 -4
  4. package/android/src/main/cxx/NativeUnistylesModule.h +5 -1
  5. package/components/native/ActivityIndicator/package.json +1 -1
  6. package/cxx/common/UnistylesConstants.h +1 -0
  7. package/cxx/converters/TransformOriginConverter.cpp +106 -0
  8. package/cxx/core/StyleSheetRegistry.cpp +9 -9
  9. package/cxx/core/StyleSheetRegistry.h +3 -3
  10. package/cxx/core/UnistyleWrapper.h +13 -1
  11. package/cxx/core/UnistylesRegistry.cpp +102 -18
  12. package/cxx/core/UnistylesRegistry.h +42 -5
  13. package/cxx/hybridObjects/HybridShadowRegistry.cpp +124 -4
  14. package/cxx/hybridObjects/HybridShadowRegistry.h +20 -0
  15. package/cxx/hybridObjects/HybridStyleSheet.cpp +20 -12
  16. package/cxx/hybridObjects/HybridUnistylesRuntime.cpp +11 -1
  17. package/cxx/hybridObjects/HybridUnistylesRuntime.h +4 -2
  18. package/cxx/parser/Parser.cpp +29 -1
  19. package/cxx/parser/Parser.h +1 -0
  20. package/cxx/shadowTree/NativeProps.h +46 -2
  21. package/cxx/shadowTree/ShadowTrafficController.h +4 -0
  22. package/cxx/shadowTree/ShadowTreeDiagnostics.cpp +339 -0
  23. package/cxx/shadowTree/ShadowTreeDiagnostics.h +18 -0
  24. package/cxx/shadowTree/ShadowTreeManager.cpp +7 -1
  25. package/cxx/shadowTree/ShadowTreeManager.h +1 -1
  26. package/diagnostics/package.json +6 -0
  27. package/ios/UnistylesModuleOnLoad.mm +9 -3
  28. package/lib/commonjs/components/native/ActivityIndicator.native.js +10 -0
  29. package/lib/commonjs/components/native/ActivityIndicator.native.js.map +1 -0
  30. package/lib/commonjs/components/native/NativeText.js +6 -0
  31. package/lib/commonjs/components/native/NativeText.js.map +1 -1
  32. package/lib/commonjs/components/native/NativeText.native.js +17 -1
  33. package/lib/commonjs/components/native/NativeText.native.js.map +1 -1
  34. package/lib/commonjs/components/native/NativeView.js +6 -0
  35. package/lib/commonjs/components/native/NativeView.js.map +1 -1
  36. package/lib/commonjs/components/native/NativeView.native.js +22 -3
  37. package/lib/commonjs/components/native/NativeView.native.js.map +1 -1
  38. package/lib/commonjs/components/native/Pressable.js +7 -3
  39. package/lib/commonjs/components/native/Pressable.js.map +1 -1
  40. package/lib/commonjs/components/native/TouchableHighlight.native.js +10 -0
  41. package/lib/commonjs/components/native/TouchableHighlight.native.js.map +1 -0
  42. package/lib/commonjs/core/createUnistylesActivityIndicator.js +41 -0
  43. package/lib/commonjs/core/createUnistylesActivityIndicator.js.map +1 -0
  44. package/lib/commonjs/core/createUnistylesElement.js +59 -14
  45. package/lib/commonjs/core/createUnistylesElement.js.map +1 -1
  46. package/lib/commonjs/core/createUnistylesTouchableHighlight.js +58 -0
  47. package/lib/commonjs/core/createUnistylesTouchableHighlight.js.map +1 -0
  48. package/lib/commonjs/core/index.js +14 -0
  49. package/lib/commonjs/core/index.js.map +1 -1
  50. package/lib/commonjs/diagnostics/index.js +13 -0
  51. package/lib/commonjs/diagnostics/index.js.map +1 -0
  52. package/lib/commonjs/diagnostics/types.js +2 -0
  53. package/lib/commonjs/diagnostics/types.js.map +1 -0
  54. package/lib/commonjs/diagnostics/verify.js +19 -0
  55. package/lib/commonjs/diagnostics/verify.js.map +1 -0
  56. package/lib/commonjs/diagnostics/verify.native.js +12 -0
  57. package/lib/commonjs/diagnostics/verify.native.js.map +1 -0
  58. package/lib/commonjs/index.js +38 -0
  59. package/lib/commonjs/index.js.map +1 -1
  60. package/lib/commonjs/mocks.js +41 -0
  61. package/lib/commonjs/mocks.js.map +1 -1
  62. package/lib/commonjs/reanimated/variant/useUpdateVariantColor.native.js +8 -0
  63. package/lib/commonjs/reanimated/variant/useUpdateVariantColor.native.js.map +1 -1
  64. package/lib/commonjs/specs/ShadowRegistry/index.js +97 -1
  65. package/lib/commonjs/specs/ShadowRegistry/index.js.map +1 -1
  66. package/lib/commonjs/specs/StyleSheet/index.js +5 -1
  67. package/lib/commonjs/specs/StyleSheet/index.js.map +1 -1
  68. package/lib/commonjs/specs/UnistylesRuntime/index.js.map +1 -1
  69. package/lib/commonjs/web/registry.js +3 -0
  70. package/lib/commonjs/web/registry.js.map +1 -1
  71. package/lib/module/components/native/ActivityIndicator.native.js +6 -0
  72. package/lib/module/components/native/ActivityIndicator.native.js.map +1 -0
  73. package/lib/module/components/native/NativeText.js +1 -1
  74. package/lib/module/components/native/NativeText.js.map +1 -1
  75. package/lib/module/components/native/NativeText.native.js +15 -1
  76. package/lib/module/components/native/NativeText.native.js.map +1 -1
  77. package/lib/module/components/native/NativeView.js +1 -0
  78. package/lib/module/components/native/NativeView.js.map +1 -1
  79. package/lib/module/components/native/NativeView.native.js +19 -2
  80. package/lib/module/components/native/NativeView.native.js.map +1 -1
  81. package/lib/module/components/native/Pressable.js +7 -3
  82. package/lib/module/components/native/Pressable.js.map +1 -1
  83. package/lib/module/components/native/TouchableHighlight.native.js +6 -0
  84. package/lib/module/components/native/TouchableHighlight.native.js.map +1 -0
  85. package/lib/module/core/createUnistylesActivityIndicator.js +35 -0
  86. package/lib/module/core/createUnistylesActivityIndicator.js.map +1 -0
  87. package/lib/module/core/createUnistylesElement.js +58 -13
  88. package/lib/module/core/createUnistylesElement.js.map +1 -1
  89. package/lib/module/core/createUnistylesTouchableHighlight.js +52 -0
  90. package/lib/module/core/createUnistylesTouchableHighlight.js.map +1 -0
  91. package/lib/module/core/index.js +2 -0
  92. package/lib/module/core/index.js.map +1 -1
  93. package/lib/module/diagnostics/index.js +5 -0
  94. package/lib/module/diagnostics/index.js.map +1 -0
  95. package/lib/module/diagnostics/types.js +2 -0
  96. package/lib/module/diagnostics/types.js.map +1 -0
  97. package/lib/module/diagnostics/verify.js +14 -0
  98. package/lib/module/diagnostics/verify.js.map +1 -0
  99. package/lib/module/diagnostics/verify.native.js +8 -0
  100. package/lib/module/diagnostics/verify.native.js.map +1 -0
  101. package/lib/module/index.js +2 -0
  102. package/lib/module/index.js.map +1 -1
  103. package/lib/module/mocks.js +41 -0
  104. package/lib/module/mocks.js.map +1 -1
  105. package/lib/module/reanimated/variant/useUpdateVariantColor.native.js +9 -1
  106. package/lib/module/reanimated/variant/useUpdateVariantColor.native.js.map +1 -1
  107. package/lib/module/specs/ShadowRegistry/index.js +97 -1
  108. package/lib/module/specs/ShadowRegistry/index.js.map +1 -1
  109. package/lib/module/specs/StyleSheet/index.js +6 -1
  110. package/lib/module/specs/StyleSheet/index.js.map +1 -1
  111. package/lib/module/specs/UnistylesRuntime/index.js.map +1 -1
  112. package/lib/module/web/registry.js +3 -0
  113. package/lib/module/web/registry.js.map +1 -1
  114. package/lib/typescript/src/components/native/ActivityIndicator.native.d.ts +2 -0
  115. package/lib/typescript/src/components/native/ActivityIndicator.native.d.ts.map +1 -0
  116. package/lib/typescript/src/components/native/NativeText.d.ts +1 -1
  117. package/lib/typescript/src/components/native/NativeText.d.ts.map +1 -1
  118. package/lib/typescript/src/components/native/NativeText.native.d.ts.map +1 -1
  119. package/lib/typescript/src/components/native/NativeView.d.ts +1 -0
  120. package/lib/typescript/src/components/native/NativeView.d.ts.map +1 -1
  121. package/lib/typescript/src/components/native/NativeView.native.d.ts +2 -2
  122. package/lib/typescript/src/components/native/NativeView.native.d.ts.map +1 -1
  123. package/lib/typescript/src/components/native/Pressable.d.ts.map +1 -1
  124. package/lib/typescript/src/components/native/TouchableHighlight.native.d.ts +2 -0
  125. package/lib/typescript/src/components/native/TouchableHighlight.native.d.ts.map +1 -0
  126. package/lib/typescript/src/core/createUnistylesActivityIndicator.d.ts +3 -0
  127. package/lib/typescript/src/core/createUnistylesActivityIndicator.d.ts.map +1 -0
  128. package/lib/typescript/src/core/createUnistylesElement.d.ts.map +1 -1
  129. package/lib/typescript/src/core/createUnistylesTouchableHighlight.d.ts +3 -0
  130. package/lib/typescript/src/core/createUnistylesTouchableHighlight.d.ts.map +1 -0
  131. package/lib/typescript/src/core/index.d.ts +2 -0
  132. package/lib/typescript/src/core/index.d.ts.map +1 -1
  133. package/lib/typescript/src/core/useUnistyles.d.ts +1 -1
  134. package/lib/typescript/src/diagnostics/index.d.ts +3 -0
  135. package/lib/typescript/src/diagnostics/index.d.ts.map +1 -0
  136. package/lib/typescript/src/diagnostics/types.d.ts +19 -0
  137. package/lib/typescript/src/diagnostics/types.d.ts.map +1 -0
  138. package/lib/typescript/src/diagnostics/verify.d.ts +3 -0
  139. package/lib/typescript/src/diagnostics/verify.d.ts.map +1 -0
  140. package/lib/typescript/src/diagnostics/verify.native.d.ts +3 -0
  141. package/lib/typescript/src/diagnostics/verify.native.d.ts.map +1 -0
  142. package/lib/typescript/src/index.d.ts +4 -1
  143. package/lib/typescript/src/index.d.ts.map +1 -1
  144. package/lib/typescript/src/reanimated/variant/useUpdateVariantColor.native.d.ts.map +1 -1
  145. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts +5 -1
  146. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts.map +1 -1
  147. package/lib/typescript/src/specs/ShadowRegistry/types.d.ts +3 -0
  148. package/lib/typescript/src/specs/ShadowRegistry/types.d.ts.map +1 -1
  149. package/lib/typescript/src/specs/StyleSheet/index.d.ts +4 -1
  150. package/lib/typescript/src/specs/StyleSheet/index.d.ts.map +1 -1
  151. package/lib/typescript/src/specs/UnistylesRuntime/index.d.ts +2 -2
  152. package/lib/typescript/src/specs/UnistylesRuntime/index.d.ts.map +1 -1
  153. package/lib/typescript/src/types/breakpoints.d.ts +1 -1
  154. package/lib/typescript/src/types/breakpoints.d.ts.map +1 -1
  155. package/lib/typescript/src/types/stylesheet.d.ts +3 -3
  156. package/lib/typescript/src/types/stylesheet.d.ts.map +1 -1
  157. package/lib/typescript/src/web/registry.d.ts.map +1 -1
  158. package/package.json +9 -1
  159. package/plugin/index.d.ts +4 -4
  160. package/plugin/index.js +21 -5
  161. package/src/components/native/ActivityIndicator.native.tsx +5 -0
  162. package/src/components/native/NativeText.native.tsx +18 -1
  163. package/src/components/native/NativeText.tsx +1 -1
  164. package/src/components/native/NativeView.native.tsx +22 -2
  165. package/src/components/native/NativeView.tsx +2 -0
  166. package/src/components/native/Pressable.tsx +8 -3
  167. package/src/components/native/TouchableHighlight.native.tsx +5 -0
  168. package/src/core/createUnistylesActivityIndicator.tsx +47 -0
  169. package/src/core/createUnistylesElement.tsx +45 -14
  170. package/src/core/createUnistylesTouchableHighlight.tsx +69 -0
  171. package/src/core/index.ts +2 -0
  172. package/src/diagnostics/index.ts +3 -0
  173. package/src/diagnostics/types.ts +25 -0
  174. package/src/diagnostics/verify.native.ts +7 -0
  175. package/src/diagnostics/verify.ts +13 -0
  176. package/src/index.ts +4 -1
  177. package/src/mocks.ts +41 -0
  178. package/src/reanimated/variant/useUpdateVariantColor.native.ts +7 -1
  179. package/src/specs/ShadowRegistry/index.ts +128 -1
  180. package/src/specs/ShadowRegistry/types.ts +3 -0
  181. package/src/specs/StyleSheet/index.ts +10 -2
  182. package/src/specs/UnistylesRuntime/index.ts +7 -2
  183. package/src/types/breakpoints.ts +5 -3
  184. package/src/types/stylesheet.ts +3 -3
  185. package/src/web/registry.ts +3 -0
@@ -6,6 +6,11 @@ using namespace facebook::react;
6
6
  jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
7
7
  helpers::assertThat(rt, count == 2, "Unistyles: Invalid babel transform 'ShadowRegistry link' expected 2 arguments.");
8
8
 
9
+ // runtime was replaced, registry belongs to the next one
10
+ if (!this->_unistylesRuntime->ownsRegistry()) {
11
+ return jsi::Value::undefined();
12
+ }
13
+
9
14
  auto shadowNodeWrapper = getShadowNodeFromRef(rt, args[0]);
10
15
 
11
16
  std::vector<core::Unistyle::Shared> unistyleWrappers = core::unistyleFromValue(rt, args[1]);
@@ -97,28 +102,69 @@ jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisVa
97
102
  }
98
103
 
99
104
  std::optional<folly::dynamic> initialScopedUpdate;
105
+ bool shouldCommit = false;
100
106
 
101
- if (scopedTheme.has_value() || wasSuspended) {
102
- initialScopedUpdate = parser.parseStylesToShadowTreeStyles(rt, unistylesData);
107
+ if (scopedTheme.has_value()) {
108
+ initialScopedUpdate = parser.parseStylesToShadowTreeUpdates(rt, unistylesData);
109
+ shouldCommit = shadow::hasNativeProps(&shadowNodeWrapper->getFamily());
110
+ } else if (shadow::hasNativeProps(&shadowNodeWrapper->getFamily())) {
111
+ auto update = parser.parseStylesToShadowTreeUpdates(rt, unistylesData);
112
+
113
+ if (shadow::hasOutdatedNativeProps(&shadowNodeWrapper->getFamily(), update)) {
114
+ initialScopedUpdate = std::move(update);
115
+ shouldCommit = true;
116
+ }
103
117
  }
104
118
 
105
119
  registry.linkShadowNodeWithUnistyle(
106
120
  rt,
121
+ this->_unistylesRuntime->generation,
107
122
  shadowNodeWrapper->getFamilyShared(),
108
123
  unistylesData,
109
124
  std::move(initialScopedUpdate)
110
125
  );
111
126
 
112
- if (wasSuspended) {
113
- shadow::ShadowTreeManager::updateShadowTree(rt);
127
+ if (shouldCommit) {
128
+ this->scheduleShadowTreeUpdate(rt);
114
129
  }
115
130
 
116
131
  return jsi::Value::undefined();
117
132
  }
118
133
 
134
+ void HybridShadowRegistry::scheduleShadowTreeUpdate(jsi::Runtime& rt) {
135
+ if (this->_isShadowTreeUpdateScheduled->exchange(true)) {
136
+ return;
137
+ }
138
+
139
+ auto isShadowTreeUpdateScheduled = this->_isShadowTreeUpdateScheduled;
140
+ auto unistylesRuntime = this->_unistylesRuntime;
141
+
142
+ rt.queueMicrotask(jsi::Function::createFromHostFunction(
143
+ rt,
144
+ jsi::PropNameID::forAscii(rt, "unistylesUpdateShadowTree"),
145
+ 0,
146
+ [isShadowTreeUpdateScheduled, unistylesRuntime](jsi::Runtime& rt, const jsi::Value&, const jsi::Value*, size_t) {
147
+ isShadowTreeUpdateScheduled->store(false);
148
+
149
+ // pending updates belong to the runtime that replaced this one
150
+ if (!unistylesRuntime->ownsRegistry()) {
151
+ return jsi::Value::undefined();
152
+ }
153
+
154
+ shadow::ShadowTreeManager::updateShadowTree(rt);
155
+
156
+ return jsi::Value::undefined();
157
+ }
158
+ ));
159
+ }
160
+
119
161
  jsi::Value HybridShadowRegistry::unlink(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
120
162
  helpers::assertThat(rt, count == 1, "Unistyles: Invalid babel transform 'ShadowRegistry unlink' expected 1 argument.");
121
163
 
164
+ if (!this->_unistylesRuntime->ownsRegistry()) {
165
+ return jsi::Value::undefined();
166
+ }
167
+
122
168
  auto shadowNodeWrapper = getShadowNodeFromRef(rt, args[0]);
123
169
 
124
170
  auto& registry = core::UnistylesRegistry::get();
@@ -131,6 +177,10 @@ jsi::Value HybridShadowRegistry::unlink(jsi::Runtime &rt, const jsi::Value &this
131
177
  jsi::Value HybridShadowRegistry::suspend(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
132
178
  helpers::assertThat(rt, count == 1, "Unistyles: Invalid babel transform 'ShadowRegistry suspend' expected 1 argument.");
133
179
 
180
+ if (!this->_unistylesRuntime->ownsRegistry()) {
181
+ return jsi::Value::undefined();
182
+ }
183
+
134
184
  auto shadowNodeWrapper = getShadowNodeFromRef(rt, args[0]);
135
185
  auto& registry = core::UnistylesRegistry::get();
136
186
 
@@ -140,6 +190,10 @@ jsi::Value HybridShadowRegistry::suspend(jsi::Runtime &rt, const jsi::Value &thi
140
190
  }
141
191
 
142
192
  jsi::Value HybridShadowRegistry::flush(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
193
+ if (!this->_unistylesRuntime->ownsRegistry()) {
194
+ return jsi::Value::undefined();
195
+ }
196
+
143
197
  shadow::ShadowTreeManager::updateShadowTree(rt);
144
198
 
145
199
  return jsi::Value::undefined();
@@ -148,6 +202,10 @@ jsi::Value HybridShadowRegistry::flush(jsi::Runtime &rt, const jsi::Value &thisV
148
202
  jsi::Value HybridShadowRegistry::setScopedTheme(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
149
203
  helpers::assertThat(rt, count == 1, "Unistyles: setScopedTheme expected 1 argument.");
150
204
 
205
+ if (!this->_unistylesRuntime->ownsRegistry()) {
206
+ return jsi::Value::undefined();
207
+ }
208
+
151
209
  auto& registry = core::UnistylesRegistry::get();
152
210
 
153
211
  if (args[0].isUndefined()) {
@@ -170,6 +228,68 @@ jsi::Value HybridShadowRegistry::getScopedTheme(jsi::Runtime &rt, const jsi::Val
170
228
  : jsi::Value::undefined();
171
229
  }
172
230
 
231
+ jsi::Value HybridShadowRegistry::verify(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
232
+ return shadow::ShadowTreeDiagnostics::verify(rt, this->_unistylesRuntime);
233
+ }
234
+
235
+ jsi::Value HybridShadowRegistry::takeCommittedTags(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
236
+ auto tags = core::UnistylesRegistry::get().takeCommittedTags();
237
+ auto result = jsi::Array(rt, tags.size());
238
+
239
+ for (size_t i = 0; i < tags.size(); i++) {
240
+ result.setValueAtIndex(rt, i, jsi::Value(static_cast<double>(tags[i])));
241
+ }
242
+
243
+ return result;
244
+ }
245
+
246
+ jsi::Value HybridShadowRegistry::refreshReactNodes(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
247
+ helpers::assertThat(rt, count == 1 && args[0].isObject(), "Unistyles: refreshReactNodes expected to be called with an array of shadow nodes.");
248
+
249
+ auto nodes = args[0].asObject(rt).asArray(rt);
250
+ std::unordered_map<SurfaceId, std::shared_ptr<const RootShadowNode>> roots;
251
+
252
+ UIManagerBinding::getBinding(rt)->getUIManager().getShadowTreeRegistry().enumerate([&roots](const ShadowTree& shadowTree, bool&) {
253
+ roots.emplace(shadowTree.getSurfaceId(), shadowTree.getCurrentRevision().rootShadowNode);
254
+ });
255
+
256
+ for (size_t i = 0; i < nodes.size(rt); i++) {
257
+ auto node = nodes.getValueAtIndex(rt, i);
258
+
259
+ if (!node.isObject() || !node.asObject(rt).hasNativeState<ShadowNodeWrapper>(rt)) {
260
+ continue;
261
+ }
262
+
263
+ auto wrapper = node.asObject(rt).getNativeState<ShadowNodeWrapper>(rt);
264
+
265
+ // never committed, React cloned it in a render that isn't committed yet (eg. a transition that yielded)
266
+ // it holds what React rendered since, pointing it at the committed node would drop that
267
+ if (!wrapper->shadowNode->getHasBeenPromoted()) {
268
+ continue;
269
+ }
270
+
271
+ const auto& family = wrapper->shadowNode->getFamily();
272
+ auto rootIt = roots.find(family.getSurfaceId());
273
+
274
+ if (rootIt == roots.end()) {
275
+ continue;
276
+ }
277
+
278
+ auto ancestors = family.getAncestors(*rootIt->second);
279
+
280
+ // frozen nodes are not in the committed tree, nativeProps_DEPRECATED covers them
281
+ if (ancestors.empty()) {
282
+ continue;
283
+ }
284
+
285
+ const auto& [parent, index] = ancestors.back();
286
+
287
+ wrapper->shadowNode = parent.get().getChildren().at(index);
288
+ }
289
+
290
+ return jsi::Value::undefined();
291
+ }
292
+
173
293
  std::shared_ptr<const core::ShadowNode> HybridShadowRegistry::getShadowNodeFromRef(jsi::Runtime& rt, const jsi::Value& maybeRef) {
174
294
  return Bridging<std::shared_ptr<const ShadowNode>>::fromJs(rt, maybeRef);
175
295
  }
@@ -6,7 +6,9 @@
6
6
  #include "UnistylesState.h"
7
7
  #include "UnistylesRegistry.h"
8
8
  #include "ShadowTreeManager.h"
9
+ #include "ShadowTreeDiagnostics.h"
9
10
  #include <cxxreact/ReactNativeVersion.h>
11
+ #include <atomic>
10
12
 
11
13
  namespace margelo::nitro::unistyles {
12
14
 
@@ -38,6 +40,18 @@ struct HybridShadowRegistry: public HybridUnistylesShadowRegistrySpec {
38
40
  const jsi::Value& thisValue,
39
41
  const jsi::Value* args,
40
42
  size_t count);
43
+ jsi::Value verify(jsi::Runtime& rt,
44
+ const jsi::Value& thisValue,
45
+ const jsi::Value* args,
46
+ size_t count);
47
+ jsi::Value takeCommittedTags(jsi::Runtime& rt,
48
+ const jsi::Value& thisValue,
49
+ const jsi::Value* args,
50
+ size_t count);
51
+ jsi::Value refreshReactNodes(jsi::Runtime& rt,
52
+ const jsi::Value& thisValue,
53
+ const jsi::Value* args,
54
+ size_t count);
41
55
 
42
56
  void loadHybridMethods() override {
43
57
  HybridUnistylesShadowRegistrySpec::loadHybridMethods();
@@ -49,13 +63,19 @@ struct HybridShadowRegistry: public HybridUnistylesShadowRegistrySpec {
49
63
  prototype.registerRawHybridMethod("flush", 0, &HybridShadowRegistry::flush);
50
64
  prototype.registerRawHybridMethod("setScopedTheme", 1, &HybridShadowRegistry::setScopedTheme);
51
65
  prototype.registerRawHybridMethod("getScopedTheme", 0, &HybridShadowRegistry::getScopedTheme);
66
+ prototype.registerRawHybridMethod("verify", 0, &HybridShadowRegistry::verify);
67
+ prototype.registerRawHybridMethod("takeCommittedTags", 0, &HybridShadowRegistry::takeCommittedTags);
68
+ prototype.registerRawHybridMethod("refreshReactNodes", 1, &HybridShadowRegistry::refreshReactNodes);
52
69
  });
53
70
  };
54
71
 
55
72
  std::shared_ptr<const core::ShadowNode> getShadowNodeFromRef(jsi::Runtime& rt, const jsi::Value& maybeRef);
56
73
 
57
74
  private:
75
+ void scheduleShadowTreeUpdate(jsi::Runtime& rt);
76
+
58
77
  std::shared_ptr<HybridUnistylesRuntime> _unistylesRuntime;
78
+ std::shared_ptr<std::atomic<bool>> _isShadowTreeUpdateScheduled = std::make_shared<std::atomic<bool>>(false);
59
79
  };
60
80
 
61
81
  }
@@ -16,12 +16,13 @@ double HybridStyleSheet::getHairlineWidth() {
16
16
  jsi::Value HybridStyleSheet::create(jsi::Runtime& rt, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) {
17
17
  helpers::assertThat(rt, count == 1, "StyleSheet.create expected to be called with one argument.");
18
18
  helpers::assertThat(rt, arguments[0].isObject(), "StyleSheet.create expected to be called with object or function.");
19
+ helpers::assertThat(rt, this->_unistylesRuntime->ownsRegistry(), helpers::RUNTIME_REPLACED_ERROR);
19
20
 
20
21
  auto thisStyleSheet = thisVal.asObject(rt);
21
22
  auto& registry = core::UnistylesRegistry::get();
22
23
 
23
24
  jsi::Object rawStyleSheet = arguments[0].asObject(rt);
24
- auto registeredStyleSheet = registry.addStyleSheetFromValue(rt, std::move(rawStyleSheet));
25
+ auto registeredStyleSheet = registry.addStyleSheetFromValue(rt, this->_unistylesRuntime->generation, std::move(rawStyleSheet));
25
26
 
26
27
  auto parser = parser::Parser(this->_unistylesRuntime);
27
28
 
@@ -34,6 +35,7 @@ jsi::Value HybridStyleSheet::create(jsi::Runtime& rt, const jsi::Value &thisVal,
34
35
  jsi::Value HybridStyleSheet::configure(jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) {
35
36
  helpers::assertThat(rt, count == 1, "StyleSheet.configure expected to be called with one argument.");
36
37
  helpers::assertThat(rt, arguments[0].isObject(), "StyleSheet.configure expected to be called with object.");
38
+ helpers::assertThat(rt, this->_unistylesRuntime->ownsRegistry(), helpers::RUNTIME_REPLACED_ERROR);
37
39
 
38
40
  auto config = arguments[0].asObject(rt);
39
41
 
@@ -73,6 +75,8 @@ jsi::Value HybridStyleSheet::init(jsi::Runtime &rt, const jsi::Value &thisVal, c
73
75
  return jsi::Value::undefined();
74
76
  }
75
77
 
78
+ helpers::assertThat(rt, this->_unistylesRuntime->ownsRegistry(), helpers::RUNTIME_REPLACED_ERROR);
79
+
76
80
  // create new state
77
81
  auto& registry = core::UnistylesRegistry::get();
78
82
 
@@ -165,7 +169,7 @@ void HybridStyleSheet::parseThemes(jsi::Runtime &rt, jsi::Object themes) {
165
169
  helpers::enumerateJSIObject(rt, themes, [&](const std::string& propertyName, jsi::Value& propertyValue){
166
170
  helpers::assertThat(rt, propertyValue.isObject(), "StyleSheet.configure's registered theme '" + propertyName + "' must be an object.");
167
171
 
168
- registry.registerTheme(rt, propertyName, propertyValue);
172
+ registry.registerTheme(rt, this->_unistylesRuntime->generation, propertyName, propertyValue);
169
173
  });
170
174
  }
171
175
 
@@ -267,7 +271,8 @@ void HybridStyleSheet::onPlatformDependenciesChange(std::vector<UnistyleDependen
267
271
  this->_unistylesRuntime->runOnJSThread([weakSelf, dependencies](jsi::Runtime& rt) {
268
272
  auto self = std::dynamic_pointer_cast<HybridStyleSheet>(weakSelf.lock());
269
273
 
270
- if (!self) {
274
+ // runtime was replaced in the meantime, registry belongs to the next one
275
+ if (!self || !self->_unistylesRuntime->ownsRegistry()) {
271
276
  return;
272
277
  }
273
278
 
@@ -288,7 +293,7 @@ void HybridStyleSheet::onPlatformNativeDependenciesChange(std::vector<UnistyleDe
288
293
  this->_unistylesRuntime->runOnJSThread([weakSelf, dependencies, miniRuntime](jsi::Runtime& rt){
289
294
  auto self = std::dynamic_pointer_cast<HybridStyleSheet>(weakSelf.lock());
290
295
 
291
- if (!self) {
296
+ if (!self || !self->_unistylesRuntime->ownsRegistry()) {
292
297
  return;
293
298
  }
294
299
 
@@ -329,7 +334,7 @@ void HybridStyleSheet::onImeChange(UnistylesNativeMiniRuntime miniRuntime) {
329
334
  this->_unistylesRuntime->runOnJSThread([weakSelf, miniRuntime](jsi::Runtime& rt){
330
335
  auto self = std::dynamic_pointer_cast<HybridStyleSheet>(weakSelf.lock());
331
336
 
332
- if (!self) {
337
+ if (!self || !self->_unistylesRuntime->ownsRegistry()) {
333
338
  return;
334
339
  }
335
340
 
@@ -348,22 +353,25 @@ void HybridStyleSheet::applyDependencyChanges(jsi::Runtime& rt, std::vector<Unis
348
353
  // but their rawValue must still be refreshed so rerenders read fresh closures
349
354
  auto dependentStyleSheets = registry.getStyleSheetsToRefresh(dependencies);
350
355
 
351
- if (dependencyMap.empty() && dependentStyleSheets.empty()) {
352
- return;
353
- }
354
-
355
356
  // rebuild rawValue BEFORE notifying listeners so JS rerenders read fresh closures
356
- parser.rebuildUnistylesInDependencyMap(rt, dependencyMap, dependentStyleSheets, maybeMiniRuntime);
357
+ if (!dependencyMap.empty() || !dependentStyleSheets.empty()) {
358
+ parser.rebuildUnistylesInDependencyMap(rt, dependencyMap, dependentStyleSheets, maybeMiniRuntime);
359
+ }
357
360
 
358
361
  if (!dependencyMap.empty()) {
359
362
  parser.rebuildShadowLeafUpdates(rt, dependencyMap);
360
363
  }
361
364
 
362
- this->notifyJSListeners(dependencies);
365
+ std::vector<Tag> committedTags;
363
366
 
364
367
  if (!dependencyMap.empty()) {
365
- shadow::ShadowTreeManager::updateShadowTree(rt);
368
+ committedTags = shadow::ShadowTreeManager::updateShadowTree(rt);
366
369
  }
370
+
371
+ // after the commit, so listeners can point React at the committed nodes
372
+ registry.setCommittedTags(std::move(committedTags));
373
+ // also when no StyleSheet depends on the change, hooks (useUnistyles, withUnistyles, Display, Hide) may
374
+ this->notifyJSListeners(dependencies);
367
375
  }
368
376
 
369
377
  void HybridStyleSheet::notifyJSListeners(std::vector<UnistyleDependency>& dependencies) {
@@ -4,9 +4,18 @@
4
4
  using namespace margelo::nitro::unistyles;
5
5
 
6
6
  core::UnistylesState& HybridUnistylesRuntime::getState() {
7
+ // state belongs to the runtime that replaced this one
8
+ if (!this->ownsRegistry()) {
9
+ throw std::runtime_error(helpers::RUNTIME_REPLACED_ERROR);
10
+ }
11
+
7
12
  return core::UnistylesRegistry::get().getState();
8
13
  }
9
14
 
15
+ bool HybridUnistylesRuntime::ownsRegistry() const {
16
+ return core::UnistylesRegistry::get().isCurrent(this->generation);
17
+ }
18
+
10
19
  ColorScheme HybridUnistylesRuntime::getColorScheme() {
11
20
  auto colorScheme = this->_nativePlatform->getColorScheme();
12
21
 
@@ -166,13 +175,14 @@ jsi::Value HybridUnistylesRuntime::updateTheme(jsi::Runtime &rt, const jsi::Valu
166
175
  helpers::assertThat(rt, count == 2, "UnistylesRuntime.updateTheme expected to be called with 2 arguments.");
167
176
  helpers::assertThat(rt, args[0].isString(), "UnistylesRuntime.updateTheme expected first argument to be a string.");
168
177
  helpers::assertThat(rt, args[1].isObject(), "UnistylesRuntime.updateTheme expected first argument to be a function.");
178
+ helpers::assertThat(rt, this->ownsRegistry(), helpers::RUNTIME_REPLACED_ERROR);
169
179
 
170
180
  auto& registry = core::UnistylesRegistry::get();
171
181
  auto themeName = args[0].asString(rt).utf8(rt);
172
182
 
173
183
  helpers::assertThat(rt, args[1].asObject(rt).isFunction(rt), "UnistylesRuntime.updateTheme expected second argument to be a function.");
174
184
 
175
- registry.updateTheme(rt, themeName, args[1].asObject(rt).asFunction(rt));
185
+ registry.updateTheme(rt, this->generation, themeName, args[1].asObject(rt).asFunction(rt));
176
186
 
177
187
  this->_onDependenciesChange({UnistyleDependency::THEME});
178
188
 
@@ -13,8 +13,8 @@
13
13
  namespace margelo::nitro::unistyles {
14
14
 
15
15
  struct HybridUnistylesRuntime: public HybridUnistylesRuntimeSpec {
16
- HybridUnistylesRuntime(std::shared_ptr<HybridNativePlatformSpec> nativePlatform, std::function<void(std::function<void(jsi::Runtime&)>&&)> runOnJSThread)
17
- : HybridObject(TAG), _nativePlatform{nativePlatform}, runOnJSThread(std::move(runOnJSThread)) {}
16
+ HybridUnistylesRuntime(std::shared_ptr<HybridNativePlatformSpec> nativePlatform, std::function<void(std::function<void(jsi::Runtime&)>&&)> runOnJSThread, uint64_t generation)
17
+ : HybridObject(TAG), _nativePlatform{nativePlatform}, runOnJSThread(std::move(runOnJSThread)), generation{generation} {}
18
18
 
19
19
  jsi::Value getTheme(jsi::Runtime& rt,
20
20
  const jsi::Value& thisValue,
@@ -70,11 +70,13 @@ struct HybridUnistylesRuntime: public HybridUnistylesRuntimeSpec {
70
70
  std::unordered_map<std::string, double> getBreakpoints() override;
71
71
 
72
72
  core::UnistylesState& getState();
73
+ bool ownsRegistry() const;
73
74
  UnistylesCxxMiniRuntime buildMiniRuntimeFromNativeRuntime(UnistylesNativeMiniRuntime& nativeMiniRuntime);
74
75
  jsi::Value getMiniRuntimeAsValue(jsi::Runtime& rt, std::optional<UnistylesNativeMiniRuntime> maybeMiniRuntime);
75
76
  void includeDependenciesForColorSchemeChange(std::vector<UnistyleDependency>& deps);
76
77
  void calculateNewThemeAndDependencies(std::vector<UnistyleDependency>& deps);
77
78
  std::function<void(std::function<void(jsi::Runtime&)>&&)> runOnJSThread;
79
+ const uint64_t generation;
78
80
 
79
81
  private:
80
82
  std::shared_ptr<HybridNavigationBar> _navigationBar;
@@ -439,7 +439,7 @@ void parser::Parser::rebuildShadowLeafUpdates(jsi::Runtime& rt, core::Dependency
439
439
 
440
440
  for (const auto& [shadowNode, unistyles] : dependencyMap) {
441
441
  // Parse string colors (e.g., "#000000") to int representation
442
- auto rawProps = this->parseStylesToShadowTreeStyles(rt, unistyles);
442
+ auto rawProps = this->parseStylesToShadowTreeUpdates(rt, unistyles);
443
443
 
444
444
  updates.emplace(shadowNode, std::move(rawProps));
445
445
  }
@@ -1268,6 +1268,34 @@ folly::dynamic parser::Parser::parseStylesToShadowTreeStyles(jsi::Runtime& rt, c
1268
1268
  return jsi::dynamicFromValue(rt, jsi::Value(rt, convertedStyles));
1269
1269
  }
1270
1270
 
1271
+ // shadow tree updates skip the props an inline style sets last, React (or an animation) owns them
1272
+ // writing them would also leave them in nativeProps_DEPRECATED, which React Native applies to every later clone,
1273
+ // so a value captured at link time would undo what React rendered since
1274
+ folly::dynamic parser::Parser::parseStylesToShadowTreeUpdates(jsi::Runtime& rt, const std::vector<std::shared_ptr<UnistyleData>>& unistyles) {
1275
+ auto props = this->parseStylesToShadowTreeStyles(rt, unistyles);
1276
+ std::unordered_map<std::string, bool> isOwnedByInlineStyle;
1277
+
1278
+ for (const auto& unistyleData : unistyles) {
1279
+ if (!unistyleData->parsedStyle.has_value()) {
1280
+ continue;
1281
+ }
1282
+
1283
+ bool isInlineStyle = unistyleData->unistyle->styleKey == helpers::EXOTIC_STYLE_KEY;
1284
+
1285
+ helpers::enumerateJSIObject(rt, unistyleData->parsedStyle.value(), [&isOwnedByInlineStyle, isInlineStyle](const std::string& propName, jsi::Value&) {
1286
+ isOwnedByInlineStyle[propName] = isInlineStyle;
1287
+ });
1288
+ }
1289
+
1290
+ for (const auto& [propName, isInline] : isOwnedByInlineStyle) {
1291
+ if (isInline) {
1292
+ props.erase(propName);
1293
+ }
1294
+ }
1295
+
1296
+ return props;
1297
+ }
1298
+
1271
1299
 
1272
1300
  // check is styleKey contains color
1273
1301
  bool parser::Parser::isColor(const std::string& propertyName) {
@@ -28,6 +28,7 @@ struct Parser {
28
28
  void rebuildUnistylesInDependencyMap(jsi::Runtime& rt, core::DependencyMap& dependencyMap, std::vector<std::shared_ptr<core::StyleSheet>>& styleSheets, std::optional<UnistylesNativeMiniRuntime> maybeMiniRuntime);
29
29
  void rebuildShadowLeafUpdates(jsi::Runtime& rt, core::DependencyMap& dependencyMap);
30
30
  folly::dynamic parseStylesToShadowTreeStyles(jsi::Runtime& rt, const std::vector<std::shared_ptr<UnistyleData>>& unistyles);
31
+ folly::dynamic parseStylesToShadowTreeUpdates(jsi::Runtime& rt, const std::vector<std::shared_ptr<UnistyleData>>& unistyles);
31
32
  void rebuildUnistyle(jsi::Runtime& rt, Unistyle::Shared unistyle, const Variants& variants, std::optional<std::vector<folly::dynamic>>);
32
33
  void rebuildUnistyleWithScopedTheme(jsi::Runtime& rt, jsi::Value& jsScopedTheme, std::shared_ptr<core::UnistyleData> unistyleData);
33
34
  jsi::Value getParsedStyleSheetForScopedTheme(jsi::Runtime& rt, core::Unistyle::Shared unistyle, std::string& scopedTheme);
@@ -3,8 +3,11 @@
3
3
  #include <folly/dynamic.h>
4
4
  #include <react/renderer/core/ShadowNodeFamily.h>
5
5
  #include <cxxreact/ReactNativeVersion.h>
6
+ #include <algorithm>
7
+ #include <cstdint>
6
8
  #include <memory>
7
9
  #include <mutex>
10
+ #include <string>
8
11
 
9
12
  // React Native reads ShadowNodeFamily::nativeProps_DEPRECATED from any thread
10
13
  // (eg. ShadowNode::clone during main thread layout) and since 0.86.1 guards every access with nativePropsMutex
@@ -33,12 +36,53 @@ inline void mergeNativeProps(const ShadowNodeFamily* family, const folly::dynami
33
36
  family->nativeProps_DEPRECATED = std::make_unique<folly::dynamic>(props);
34
37
  }
35
38
 
36
- inline void resetNativeProps(const ShadowNodeFamily* family) {
39
+ inline bool hasNativeProps(const ShadowNodeFamily* family) {
37
40
  #if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
38
41
  std::lock_guard<std::mutex> lock(family->nativePropsMutex);
39
42
  #endif
40
43
 
41
- family->nativeProps_DEPRECATED.reset();
44
+ return family->nativeProps_DEPRECATED && family->nativeProps_DEPRECATED->isObject();
45
+ }
46
+
47
+ inline bool isSamePropValue(const std::string& propName, const folly::dynamic& value, const folly::dynamic& otherValue) {
48
+ if (value == otherValue) {
49
+ return true;
50
+ }
51
+
52
+ if (!value.isNumber() || !otherValue.isNumber()) {
53
+ return false;
54
+ }
55
+
56
+ std::string lowercasePropName = propName;
57
+ std::transform(lowercasePropName.begin(), lowercasePropName.end(), lowercasePropName.begin(), ::tolower);
58
+
59
+ if (lowercasePropName.find("color") == std::string::npos) {
60
+ return false;
61
+ }
62
+
63
+ return static_cast<uint32_t>(static_cast<int64_t>(value.asDouble())) == static_cast<uint32_t>(static_cast<int64_t>(otherValue.asDouble()));
64
+ }
65
+
66
+ inline bool hasOutdatedNativeProps(const ShadowNodeFamily* family, const folly::dynamic& props) {
67
+ #if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
68
+ std::lock_guard<std::mutex> lock(family->nativePropsMutex);
69
+ #endif
70
+
71
+ if (!family->nativeProps_DEPRECATED || !family->nativeProps_DEPRECATED->isObject() || !props.isObject()) {
72
+ return false;
73
+ }
74
+
75
+ auto& nativeProps = *family->nativeProps_DEPRECATED;
76
+
77
+ for (const auto& [propName, propValue] : props.items()) {
78
+ auto it = nativeProps.find(propName);
79
+
80
+ if (it != nativeProps.items().end() && !isSamePropValue(propName.asString(), it->second, propValue)) {
81
+ return true;
82
+ }
83
+ }
84
+
85
+ return false;
42
86
  }
43
87
 
44
88
  }
@@ -36,6 +36,10 @@ struct ShadowTrafficController {
36
36
  return family;
37
37
  }
38
38
 
39
+ inline size_t getUpdatesCount() const {
40
+ return _unistylesUpdates.size();
41
+ }
42
+
39
43
  template <typename F>
40
44
  inline auto withLock(F&& func) {
41
45
  std::lock_guard<std::mutex> lock(_mutex);