react-native-unistyles 3.6.0 → 3.7.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 (142) hide show
  1. package/README.md +4 -9
  2. package/android/build.gradle +42 -0
  3. package/android/src/legacy/java/com/unistyles/NativePlatform+extraWindows.kt +16 -0
  4. package/android/src/main/cxx/NativeUnistylesModule.cpp +5 -11
  5. package/android/src/main/cxx/NativeUnistylesModule.h +0 -4
  6. package/android/src/main/java/com/unistyles/NativePlatform+android.kt +82 -39
  7. package/android/src/main/java/com/unistyles/NativePlatform+insets.kt +73 -0
  8. package/android/src/main/java/com/unistyles/UnistylesModule.kt +2 -13
  9. package/android/src/rn86/java/com/unistyles/NativePlatform+extraWindows.kt +28 -0
  10. package/cxx/common/PerformanceTracing.h +85 -0
  11. package/cxx/converters/BackgroundLayerConverter.cpp +523 -0
  12. package/cxx/converters/BackgroundLayerConverter.h +14 -0
  13. package/cxx/converters/FontVariationSettingsConverter.cpp +104 -0
  14. package/cxx/converters/FontVariationSettingsConverter.h +17 -0
  15. package/cxx/core/UnistyleWrapper.h +16 -1
  16. package/cxx/hybridObjects/HybridShadowRegistry.cpp +8 -0
  17. package/cxx/hybridObjects/HybridStyleSheet.cpp +11 -0
  18. package/cxx/hybridObjects/HybridStyleSheet.h +1 -0
  19. package/cxx/hybridObjects/HybridUnistylesRuntime.cpp +10 -30
  20. package/cxx/hybridObjects/HybridUnistylesRuntime.h +2 -2
  21. package/cxx/parser/Parser.cpp +75 -10
  22. package/cxx/shadowTree/ShadowTreeDiagnostics.cpp +74 -0
  23. package/cxx/shadowTree/ShadowTreeManager.cpp +3 -0
  24. package/lib/commonjs/components/NamedTheme.js +4 -4
  25. package/lib/commonjs/components/NamedTheme.js.map +1 -1
  26. package/lib/commonjs/components/ScopedThemeContext.js +3 -1
  27. package/lib/commonjs/components/ScopedThemeContext.js.map +1 -1
  28. package/lib/commonjs/core/createUnistylesImageBackground.js +6 -2
  29. package/lib/commonjs/core/createUnistylesImageBackground.js.map +1 -1
  30. package/lib/commonjs/core/withUnistyles/withUnistyles.js +10 -7
  31. package/lib/commonjs/core/withUnistyles/withUnistyles.js.map +1 -1
  32. package/lib/commonjs/core/withUnistyles/withUnistyles.native.js +22 -3
  33. package/lib/commonjs/core/withUnistyles/withUnistyles.native.js.map +1 -1
  34. package/lib/commonjs/mocks.js +27 -3
  35. package/lib/commonjs/mocks.js.map +1 -1
  36. package/lib/commonjs/specs/ShadowRegistry/index.js +13 -3
  37. package/lib/commonjs/specs/ShadowRegistry/index.js.map +1 -1
  38. package/lib/commonjs/specs/ShadowRegistry/tracing.js +26 -0
  39. package/lib/commonjs/specs/ShadowRegistry/tracing.js.map +1 -0
  40. package/lib/commonjs/utils.js +52 -1
  41. package/lib/commonjs/utils.js.map +1 -1
  42. package/lib/commonjs/web/convert/index.js +19 -1
  43. package/lib/commonjs/web/convert/index.js.map +1 -1
  44. package/lib/commonjs/web/convert/module.d.ts +3 -0
  45. package/lib/commonjs/web/convert/object/backgroundLayers.js +30 -0
  46. package/lib/commonjs/web/convert/object/backgroundLayers.js.map +1 -0
  47. package/lib/commonjs/web/convert/object/fontVariationSettings.js +12 -0
  48. package/lib/commonjs/web/convert/object/fontVariationSettings.js.map +1 -0
  49. package/lib/commonjs/web/convert/object/index.js +22 -0
  50. package/lib/commonjs/web/convert/object/index.js.map +1 -1
  51. package/lib/commonjs/web/convert/utils.js +7 -1
  52. package/lib/commonjs/web/convert/utils.js.map +1 -1
  53. package/lib/commonjs/web/registry.js +6 -4
  54. package/lib/commonjs/web/registry.js.map +1 -1
  55. package/lib/commonjs/web/runtime.js +9 -6
  56. package/lib/commonjs/web/runtime.js.map +1 -1
  57. package/lib/commonjs/web/utils/unistyle.js +1 -3
  58. package/lib/commonjs/web/utils/unistyle.js.map +1 -1
  59. package/lib/module/components/NamedTheme.js +4 -4
  60. package/lib/module/components/NamedTheme.js.map +1 -1
  61. package/lib/module/components/ScopedThemeContext.js +3 -1
  62. package/lib/module/components/ScopedThemeContext.js.map +1 -1
  63. package/lib/module/core/createUnistylesImageBackground.js +6 -2
  64. package/lib/module/core/createUnistylesImageBackground.js.map +1 -1
  65. package/lib/module/core/withUnistyles/withUnistyles.js +9 -6
  66. package/lib/module/core/withUnistyles/withUnistyles.js.map +1 -1
  67. package/lib/module/core/withUnistyles/withUnistyles.native.js +24 -5
  68. package/lib/module/core/withUnistyles/withUnistyles.native.js.map +1 -1
  69. package/lib/module/mocks.js +27 -3
  70. package/lib/module/mocks.js.map +1 -1
  71. package/lib/module/specs/ShadowRegistry/index.js +13 -3
  72. package/lib/module/specs/ShadowRegistry/index.js.map +1 -1
  73. package/lib/module/specs/ShadowRegistry/tracing.js +21 -0
  74. package/lib/module/specs/ShadowRegistry/tracing.js.map +1 -0
  75. package/lib/module/utils.js +50 -0
  76. package/lib/module/utils.js.map +1 -1
  77. package/lib/module/web/convert/index.js +22 -3
  78. package/lib/module/web/convert/index.js.map +1 -1
  79. package/lib/module/web/convert/module.d.ts +3 -0
  80. package/lib/module/web/convert/object/backgroundLayers.js +25 -0
  81. package/lib/module/web/convert/object/backgroundLayers.js.map +1 -0
  82. package/lib/module/web/convert/object/fontVariationSettings.js +7 -0
  83. package/lib/module/web/convert/object/fontVariationSettings.js.map +1 -0
  84. package/lib/module/web/convert/object/index.js +2 -0
  85. package/lib/module/web/convert/object/index.js.map +1 -1
  86. package/lib/module/web/convert/utils.js +4 -0
  87. package/lib/module/web/convert/utils.js.map +1 -1
  88. package/lib/module/web/registry.js +6 -4
  89. package/lib/module/web/registry.js.map +1 -1
  90. package/lib/module/web/runtime.js +9 -6
  91. package/lib/module/web/runtime.js.map +1 -1
  92. package/lib/module/web/utils/unistyle.js +2 -4
  93. package/lib/module/web/utils/unistyle.js.map +1 -1
  94. package/lib/typescript/src/components/ScopedThemeContext.d.ts +2 -1
  95. package/lib/typescript/src/components/ScopedThemeContext.d.ts.map +1 -1
  96. package/lib/typescript/src/core/createUnistylesImageBackground.d.ts.map +1 -1
  97. package/lib/typescript/src/core/withUnistyles/withUnistyles.d.ts +6 -3
  98. package/lib/typescript/src/core/withUnistyles/withUnistyles.d.ts.map +1 -1
  99. package/lib/typescript/src/core/withUnistyles/withUnistyles.native.d.ts +7 -4
  100. package/lib/typescript/src/core/withUnistyles/withUnistyles.native.d.ts.map +1 -1
  101. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts.map +1 -1
  102. package/lib/typescript/src/specs/ShadowRegistry/tracing.d.ts +2 -0
  103. package/lib/typescript/src/specs/ShadowRegistry/tracing.d.ts.map +1 -0
  104. package/lib/typescript/src/utils.d.ts +1 -0
  105. package/lib/typescript/src/utils.d.ts.map +1 -1
  106. package/lib/typescript/src/web/convert/index.d.ts.map +1 -1
  107. package/lib/typescript/src/web/convert/object/backgroundLayers.d.ts +4 -0
  108. package/lib/typescript/src/web/convert/object/backgroundLayers.d.ts.map +1 -0
  109. package/lib/typescript/src/web/convert/object/fontVariationSettings.d.ts +4 -0
  110. package/lib/typescript/src/web/convert/object/fontVariationSettings.d.ts.map +1 -0
  111. package/lib/typescript/src/web/convert/object/index.d.ts +2 -0
  112. package/lib/typescript/src/web/convert/object/index.d.ts.map +1 -1
  113. package/lib/typescript/src/web/convert/utils.d.ts +2 -0
  114. package/lib/typescript/src/web/convert/utils.d.ts.map +1 -1
  115. package/lib/typescript/src/web/registry.d.ts.map +1 -1
  116. package/lib/typescript/src/web/runtime.d.ts +1 -0
  117. package/lib/typescript/src/web/runtime.d.ts.map +1 -1
  118. package/lib/typescript/src/web/utils/unistyle.d.ts.map +1 -1
  119. package/metro/babel-transformer.js +79 -17
  120. package/package.json +26 -26
  121. package/plugin/index.js +79 -17
  122. package/src/components/NamedTheme.tsx +5 -5
  123. package/src/components/ScopedThemeContext.ts +6 -2
  124. package/src/core/createUnistylesImageBackground.tsx +6 -2
  125. package/src/core/withUnistyles/withUnistyles.native.tsx +28 -6
  126. package/src/core/withUnistyles/withUnistyles.tsx +6 -6
  127. package/src/mocks.ts +14 -5
  128. package/src/specs/ShadowRegistry/index.ts +14 -3
  129. package/src/specs/ShadowRegistry/tracing.ts +23 -0
  130. package/src/utils.ts +51 -0
  131. package/src/web/convert/index.ts +39 -3
  132. package/src/web/convert/object/backgroundLayers.ts +33 -0
  133. package/src/web/convert/object/fontVariationSettings.ts +7 -0
  134. package/src/web/convert/object/index.ts +2 -0
  135. package/src/web/convert/utils.ts +11 -0
  136. package/src/web/registry.ts +7 -8
  137. package/src/web/runtime.ts +7 -7
  138. package/src/web/utils/unistyle.ts +2 -4
  139. package/lib/commonjs/web/convert/module.d.js +0 -2
  140. package/lib/commonjs/web/convert/module.d.js.map +0 -1
  141. package/lib/module/web/convert/module.d.js +0 -2
  142. package/lib/module/web/convert/module.d.js.map +0 -1
@@ -0,0 +1,523 @@
1
+ #include "BackgroundLayerConverter.h"
2
+
3
+ #include <algorithm>
4
+ #include <cctype>
5
+ #include <cmath>
6
+ #include <cstdlib>
7
+ #include <limits>
8
+ #include <optional>
9
+ #include <vector>
10
+
11
+ // React Native processes background size, position and repeat strings in JS
12
+ // (processBackgroundSize.js, processBackgroundPosition.js and
13
+ // processBackgroundRepeat.js). Its native props parser only reads the processed
14
+ // arrays, even with enableNativeCSSParsing, so shadow tree updates convert the
15
+ // strings here with the same output.
16
+ namespace margelo::nitro::unistyles::converters {
17
+
18
+ namespace {
19
+
20
+ bool isWhitespace(char character) {
21
+ return std::isspace(static_cast<unsigned char>(character)) != 0;
22
+ }
23
+
24
+ bool isDigit(const std::string &value, size_t index) {
25
+ return index < value.size() &&
26
+ std::isdigit(static_cast<unsigned char>(value[index])) != 0;
27
+ }
28
+
29
+ std::string toLower(std::string value) {
30
+ std::transform(value.begin(), value.end(), value.begin(),
31
+ [](unsigned char c) { return std::tolower(c); });
32
+
33
+ return value;
34
+ }
35
+
36
+ bool endsWith(const std::string &value, const std::string &suffix) {
37
+ return value.size() >= suffix.size() &&
38
+ value.compare(value.size() - suffix.size(), suffix.size(), suffix) ==
39
+ 0;
40
+ }
41
+
42
+ std::string trim(const std::string &value) {
43
+ auto start = std::find_if_not(value.begin(), value.end(), isWhitespace);
44
+ auto end = std::find_if_not(value.rbegin(), value.rend(), isWhitespace).base();
45
+
46
+ return start < end ? std::string(start, end) : std::string();
47
+ }
48
+
49
+ // value.split(',').map(s => s.trim())
50
+ std::vector<std::string> splitLayers(const std::string &value) {
51
+ std::vector<std::string> layers;
52
+ size_t start = 0;
53
+
54
+ while (true) {
55
+ auto end = value.find(',', start);
56
+
57
+ layers.push_back(trim(value.substr(start, end - start)));
58
+
59
+ if (end == std::string::npos) {
60
+ return layers;
61
+ }
62
+
63
+ start = end + 1;
64
+ }
65
+ }
66
+
67
+ // value.split(/\s+/).filter(p => p.length > 0)
68
+ std::vector<std::string> splitParts(const std::string &value) {
69
+ std::vector<std::string> parts;
70
+ std::string part;
71
+
72
+ for (auto character : value) {
73
+ if (!isWhitespace(character)) {
74
+ part.push_back(character);
75
+
76
+ continue;
77
+ }
78
+
79
+ if (!part.empty()) {
80
+ parts.push_back(part);
81
+ part.clear();
82
+ }
83
+ }
84
+
85
+ if (!part.empty()) {
86
+ parts.push_back(part);
87
+ }
88
+
89
+ return parts;
90
+ }
91
+
92
+ // JS parseFloat: the longest decimal prefix after leading whitespace, NaN
93
+ // without one
94
+ double parseFloat(const std::string &value) {
95
+ size_t index = 0;
96
+
97
+ while (index < value.size() && isWhitespace(value[index])) {
98
+ index++;
99
+ }
100
+
101
+ auto start = index;
102
+
103
+ if (index < value.size() && (value[index] == '+' || value[index] == '-')) {
104
+ index++;
105
+ }
106
+
107
+ if (value.compare(index, 8, "Infinity") == 0) {
108
+ return value[start] == '-' ? -std::numeric_limits<double>::infinity()
109
+ : std::numeric_limits<double>::infinity();
110
+ }
111
+
112
+ size_t digits = 0;
113
+
114
+ while (isDigit(value, index)) {
115
+ index++;
116
+ digits++;
117
+ }
118
+
119
+ if (index < value.size() && value[index] == '.') {
120
+ index++;
121
+
122
+ while (isDigit(value, index)) {
123
+ index++;
124
+ digits++;
125
+ }
126
+ }
127
+
128
+ if (digits == 0) {
129
+ return std::numeric_limits<double>::quiet_NaN();
130
+ }
131
+
132
+ if (index < value.size() && (value[index] == 'e' || value[index] == 'E')) {
133
+ auto exponentIndex = index + 1;
134
+
135
+ if (exponentIndex < value.size() &&
136
+ (value[exponentIndex] == '+' || value[exponentIndex] == '-')) {
137
+ exponentIndex++;
138
+ }
139
+
140
+ auto exponentStart = exponentIndex;
141
+
142
+ while (isDigit(value, exponentIndex)) {
143
+ exponentIndex++;
144
+ }
145
+
146
+ if (exponentIndex > exponentStart) {
147
+ index = exponentIndex;
148
+ }
149
+ }
150
+
151
+ return std::strtod(value.substr(start, index - start).c_str(), nullptr);
152
+ }
153
+
154
+ // [ <length-percentage [0,∞]> | auto ]
155
+ std::optional<folly::dynamic> toBackgroundSizeValue(const std::string &size) {
156
+ if (endsWith(size, "px")) {
157
+ auto number = parseFloat(size);
158
+
159
+ if (!std::isnan(number) && number >= 0) {
160
+ return folly::dynamic(number);
161
+ }
162
+ }
163
+
164
+ if (endsWith(size, "%") && parseFloat(size) >= 0) {
165
+ return folly::dynamic(size);
166
+ }
167
+
168
+ if (size == "auto") {
169
+ return folly::dynamic(size);
170
+ }
171
+
172
+ return std::nullopt;
173
+ }
174
+
175
+ // https://www.w3.org/TR/css-backgrounds-3/#typedef-bg-size
176
+ folly::dynamic parseBackgroundSize(const std::string &value) {
177
+ folly::dynamic result = folly::dynamic::array();
178
+
179
+ for (const auto &size : splitLayers(value)) {
180
+ if (size.empty()) {
181
+ return folly::dynamic::array();
182
+ }
183
+
184
+ auto parts = splitParts(size);
185
+
186
+ if (parts.size() == 2) {
187
+ auto x = toBackgroundSizeValue(toLower(parts[0]));
188
+ auto y = toBackgroundSizeValue(toLower(parts[1]));
189
+
190
+ if (!x.has_value() || !y.has_value()) {
191
+ return folly::dynamic::array();
192
+ }
193
+
194
+ result.push_back(folly::dynamic::object("x", x.value())("y", y.value()));
195
+ } else if (parts.size() == 1) {
196
+ auto x = toBackgroundSizeValue(toLower(parts[0]));
197
+
198
+ if (!x.has_value()) {
199
+ return folly::dynamic::array();
200
+ }
201
+
202
+ result.push_back(folly::dynamic::object("x", x.value())("y", "auto"));
203
+ }
204
+ }
205
+
206
+ return result;
207
+ }
208
+
209
+ bool isValidPosition(const std::string &position) {
210
+ return endsWith(position, "px") || endsWith(position, "%") ||
211
+ position == "0";
212
+ }
213
+
214
+ std::optional<folly::dynamic>
215
+ getPositionFromCSSValue(const std::string &position) {
216
+ if (endsWith(position, "px")) {
217
+ return folly::dynamic(parseFloat(position));
218
+ }
219
+
220
+ if (endsWith(position, "%")) {
221
+ return folly::dynamic(position);
222
+ }
223
+
224
+ // CSS length allows 0 as a valid value
225
+ if (position == "0") {
226
+ return folly::dynamic(0);
227
+ }
228
+
229
+ return std::nullopt;
230
+ }
231
+
232
+ // https://www.w3.org/TR/css-backgrounds-3/#typedef-bg-position
233
+ folly::dynamic parseBackgroundPosition(const std::string &value) {
234
+ folly::dynamic result = folly::dynamic::array();
235
+
236
+ for (const auto &position : splitLayers(value)) {
237
+ std::optional<folly::dynamic> top;
238
+ std::optional<folly::dynamic> left;
239
+ std::optional<folly::dynamic> right;
240
+ std::optional<folly::dynamic> bottom;
241
+ auto parts = splitParts(position);
242
+
243
+ // 1. Single value syntax [ left | center | right | top | bottom |
244
+ // <length-percentage> ]
245
+ if (parts.size() == 1) {
246
+ auto token1 = toLower(parts[0]);
247
+
248
+ if (token1 == "left") {
249
+ left = "0%";
250
+ top = "50%";
251
+ } else if (token1 == "center") {
252
+ left = "50%";
253
+ top = "50%";
254
+ } else if (token1 == "right") {
255
+ left = "100%";
256
+ top = "50%";
257
+ } else if (token1 == "top") {
258
+ left = "50%";
259
+ top = "0%";
260
+ } else if (token1 == "bottom") {
261
+ left = "50%";
262
+ top = "100%";
263
+ } else if (isValidPosition(token1)) {
264
+ auto positionValue = getPositionFromCSSValue(token1);
265
+
266
+ if (!positionValue.has_value()) {
267
+ return folly::dynamic::array();
268
+ }
269
+
270
+ left = positionValue;
271
+ top = "50%";
272
+ }
273
+ }
274
+
275
+ // 2. Two value syntax [ left | center | right | <length-percentage> ] [
276
+ // top | center | bottom | <length-percentage> ]
277
+ if (parts.size() == 2) {
278
+ auto token1 = toLower(parts[0]);
279
+
280
+ if (token1 == "left") {
281
+ left = "0%";
282
+ } else if (token1 == "center") {
283
+ left = "50%";
284
+ } else if (token1 == "right") {
285
+ left = "100%";
286
+ } else if (token1 == "top") {
287
+ top = "0%";
288
+ } else if (token1 == "bottom") {
289
+ top = "100%";
290
+ } else if (isValidPosition(token1)) {
291
+ auto positionValue = getPositionFromCSSValue(token1);
292
+
293
+ if (!positionValue.has_value()) {
294
+ return folly::dynamic::array();
295
+ }
296
+
297
+ left = positionValue;
298
+ }
299
+
300
+ auto token2 = toLower(parts[1]);
301
+
302
+ if (token2 == "top") {
303
+ top = "0%";
304
+ } else if (token2 == "center") {
305
+ top = "50%";
306
+ } else if (token2 == "bottom") {
307
+ top = "100%";
308
+ } else if (token2 == "left") {
309
+ left = "0%";
310
+ } else if (token2 == "right") {
311
+ left = "100%";
312
+ } else if (isValidPosition(token2)) {
313
+ auto positionValue = getPositionFromCSSValue(token2);
314
+
315
+ if (!positionValue.has_value()) {
316
+ return folly::dynamic::array();
317
+ }
318
+
319
+ top = positionValue;
320
+ }
321
+ }
322
+
323
+ // 3. Three value syntax [ center | [ left | right ] <length-percentage>? ]
324
+ // && [ center | [ top | bottom ] <length-percentage>? ]
325
+ if (parts.size() == 3) {
326
+ auto token1 = toLower(parts[0]);
327
+ auto token2 = toLower(parts[1]);
328
+ auto token3 = toLower(parts[2]);
329
+
330
+ if (token1 == "center") {
331
+ // e.g. center top 40%
332
+ left = "50%";
333
+ auto positionValue = getPositionFromCSSValue(token3);
334
+
335
+ if (!positionValue.has_value()) {
336
+ return folly::dynamic::array();
337
+ }
338
+
339
+ if (token2 == "top") {
340
+ top = positionValue;
341
+ } else if (token2 == "bottom") {
342
+ bottom = positionValue;
343
+ } else {
344
+ return folly::dynamic::array();
345
+ }
346
+ } else if (token3 == "center") {
347
+ // e.g. left 40% center
348
+ top = "50%";
349
+ auto positionValue = getPositionFromCSSValue(token2);
350
+
351
+ if (!positionValue.has_value()) {
352
+ return folly::dynamic::array();
353
+ }
354
+
355
+ if (token1 == "left") {
356
+ left = positionValue;
357
+ } else if (token1 == "right") {
358
+ right = positionValue;
359
+ } else {
360
+ return folly::dynamic::array();
361
+ }
362
+ } else {
363
+ // e.g. left 40% top, left top 10%
364
+ std::vector<std::string> tokens = {token1, token2, token3};
365
+
366
+ for (size_t i = 0; i < tokens.size(); i++) {
367
+ const auto &token = tokens[i];
368
+
369
+ if (isValidPosition(token)) {
370
+ auto positionValue = getPositionFromCSSValue(token);
371
+
372
+ if (!positionValue.has_value()) {
373
+ return folly::dynamic::array();
374
+ }
375
+
376
+ if (i == 0) {
377
+ continue;
378
+ }
379
+
380
+ const auto &previousToken = tokens[i - 1];
381
+
382
+ if (previousToken == "left") {
383
+ left = positionValue;
384
+ } else if (previousToken == "right") {
385
+ right = positionValue;
386
+ } else if (previousToken == "top") {
387
+ top = positionValue;
388
+ } else if (previousToken == "bottom") {
389
+ bottom = positionValue;
390
+ }
391
+ } else if (token == "left") {
392
+ left = "0%";
393
+ } else if (token == "right") {
394
+ right = "0%";
395
+ } else if (token == "top") {
396
+ top = "0%";
397
+ } else if (token == "bottom") {
398
+ bottom = "0%";
399
+ } else {
400
+ return folly::dynamic::array();
401
+ }
402
+ }
403
+ }
404
+ }
405
+
406
+ // 4. Four value syntax [ center | [ left | right ] <length-percentage>? ]
407
+ // && [ center | [ top | bottom ] <length-percentage>? ]
408
+ if (parts.size() == 4) {
409
+ auto keyword1 = toLower(parts[0]);
410
+ auto value1 = getPositionFromCSSValue(toLower(parts[1]));
411
+ auto keyword2 = toLower(parts[2]);
412
+ auto value2 = getPositionFromCSSValue(toLower(parts[3]));
413
+
414
+ if (!value1.has_value() || !value2.has_value()) {
415
+ return folly::dynamic::array();
416
+ }
417
+
418
+ if (keyword1 == "left") {
419
+ left = value1;
420
+ } else if (keyword1 == "right") {
421
+ right = value1;
422
+ }
423
+
424
+ if (keyword2 == "top") {
425
+ top = value2;
426
+ } else if (keyword2 == "bottom") {
427
+ bottom = value2;
428
+ }
429
+ }
430
+
431
+ if (top.has_value() && left.has_value()) {
432
+ result.push_back(
433
+ folly::dynamic::object("top", top.value())("left", left.value()));
434
+ } else if (bottom.has_value() && right.has_value()) {
435
+ result.push_back(folly::dynamic::object("bottom", bottom.value())(
436
+ "right", right.value()));
437
+ } else if (top.has_value() && right.has_value()) {
438
+ result.push_back(
439
+ folly::dynamic::object("top", top.value())("right", right.value()));
440
+ } else if (bottom.has_value() && left.has_value()) {
441
+ result.push_back(folly::dynamic::object("bottom", bottom.value())(
442
+ "left", left.value()));
443
+ } else {
444
+ return folly::dynamic::array();
445
+ }
446
+ }
447
+
448
+ return result;
449
+ }
450
+
451
+ bool isBackgroundRepeatKeyword(const std::string &value) {
452
+ return value == "repeat" || value == "space" || value == "round" ||
453
+ value == "no-repeat";
454
+ }
455
+
456
+ // https://www.w3.org/TR/css-backgrounds-3/#typedef-repeat-style
457
+ folly::dynamic parseBackgroundRepeat(const std::string &value) {
458
+ folly::dynamic result = folly::dynamic::array();
459
+
460
+ for (const auto &repeat : splitLayers(value)) {
461
+ if (repeat.empty()) {
462
+ return folly::dynamic::array();
463
+ }
464
+
465
+ auto parts = splitParts(repeat);
466
+
467
+ if (parts.size() == 1) {
468
+ auto token1 = toLower(parts[0]);
469
+
470
+ if (token1 == "repeat-x") {
471
+ result.push_back(folly::dynamic::object("x", "repeat")("y", "no-repeat"));
472
+ } else if (token1 == "repeat-y") {
473
+ result.push_back(folly::dynamic::object("x", "no-repeat")("y", "repeat"));
474
+ } else if (isBackgroundRepeatKeyword(token1)) {
475
+ result.push_back(folly::dynamic::object("x", token1)("y", token1));
476
+ } else {
477
+ return folly::dynamic::array();
478
+ }
479
+ } else if (parts.size() == 2) {
480
+ auto token1 = toLower(parts[0]);
481
+ auto token2 = toLower(parts[1]);
482
+
483
+ if (!isBackgroundRepeatKeyword(token1) ||
484
+ !isBackgroundRepeatKeyword(token2)) {
485
+ return folly::dynamic::array();
486
+ }
487
+
488
+ result.push_back(folly::dynamic::object("x", token1)("y", token2));
489
+ }
490
+ }
491
+
492
+ return result;
493
+ }
494
+
495
+ } // namespace
496
+
497
+ bool isBackgroundLayerPropName(const std::string &propertyName) {
498
+ return propertyName == "backgroundSize" ||
499
+ propertyName == "experimental_backgroundSize" ||
500
+ propertyName == "backgroundPosition" ||
501
+ propertyName == "experimental_backgroundPosition" ||
502
+ propertyName == "backgroundRepeat" ||
503
+ propertyName == "experimental_backgroundRepeat";
504
+ }
505
+
506
+ folly::dynamic parseBackgroundLayerString(const std::string &propertyName,
507
+ const std::string &value) {
508
+ std::string singleLineValue = value;
509
+
510
+ std::replace(singleLineValue.begin(), singleLineValue.end(), '\n', ' ');
511
+
512
+ if (endsWith(propertyName, "ackgroundSize")) {
513
+ return parseBackgroundSize(singleLineValue);
514
+ }
515
+
516
+ if (endsWith(propertyName, "ackgroundPosition")) {
517
+ return parseBackgroundPosition(singleLineValue);
518
+ }
519
+
520
+ return parseBackgroundRepeat(singleLineValue);
521
+ }
522
+
523
+ } // namespace margelo::nitro::unistyles::converters
@@ -0,0 +1,14 @@
1
+ #pragma once
2
+
3
+ #include <folly/dynamic.h>
4
+ #include <string>
5
+
6
+ namespace margelo::nitro::unistyles::converters {
7
+
8
+ // backgroundSize, backgroundPosition and backgroundRepeat (and their
9
+ // experimental_ aliases)
10
+ bool isBackgroundLayerPropName(const std::string &propertyName);
11
+ folly::dynamic parseBackgroundLayerString(const std::string &propertyName,
12
+ const std::string &value);
13
+
14
+ } // namespace margelo::nitro::unistyles::converters
@@ -0,0 +1,104 @@
1
+ #include "FontVariationSettingsConverter.h"
2
+
3
+ #include <algorithm>
4
+ #include <cmath>
5
+ #include <utility>
6
+ #include <vector>
7
+
8
+ // React Native converts the object form to a string in JS
9
+ // (processFontVariationSettings.js), and its native props only read strings,
10
+ // so shadow tree updates convert it here with the same output.
11
+ namespace margelo::nitro::unistyles::converters {
12
+
13
+ using namespace facebook;
14
+
15
+ namespace {
16
+
17
+ std::optional<std::string> quoteAxis(const std::string &axis) {
18
+ auto isPrintable = [](unsigned char character) {
19
+ return character >= 0x20 && character <= 0x7e;
20
+ };
21
+
22
+ if (axis.size() != 4 || !std::all_of(axis.begin(), axis.end(), isPrintable)) {
23
+ return std::nullopt;
24
+ }
25
+
26
+ if (axis.find('\'') == std::string::npos) {
27
+ return "'" + axis + "'";
28
+ }
29
+
30
+ if (axis.find('"') == std::string::npos) {
31
+ return "\"" + axis + "\"";
32
+ }
33
+
34
+ return std::nullopt;
35
+ }
36
+
37
+ } // namespace
38
+
39
+ bool isFontVariationSettingsPropName(const std::string &propertyName) {
40
+ return propertyName == "fontVariationSettings";
41
+ }
42
+
43
+ bool isFontVariationSettingsObject(jsi::Runtime &rt, jsi::Object &value) {
44
+ if (value.isArray(rt) || value.isFunction(rt)) {
45
+ return false;
46
+ }
47
+
48
+ auto propertyNames = value.getPropertyNames(rt);
49
+ auto length = propertyNames.size(rt);
50
+
51
+ for (size_t i = 0; i < length; i++) {
52
+ auto propertyName = propertyNames.getValueAtIndex(rt, i).asString(rt);
53
+
54
+ if (!value.getProperty(rt, jsi::PropNameID::forString(rt, propertyName))
55
+ .isNumber()) {
56
+ return false;
57
+ }
58
+ }
59
+
60
+ return true;
61
+ }
62
+
63
+ std::optional<std::string> fontVariationSettingsToString(jsi::Runtime &rt,
64
+ jsi::Object &value) {
65
+ std::vector<std::pair<std::string, double>> axes;
66
+ auto propertyNames = value.getPropertyNames(rt);
67
+ auto length = propertyNames.size(rt);
68
+
69
+ for (size_t i = 0; i < length; i++) {
70
+ auto propertyName = propertyNames.getValueAtIndex(rt, i).asString(rt);
71
+ auto axisValue =
72
+ value.getProperty(rt, jsi::PropNameID::forString(rt, propertyName));
73
+
74
+ if (!axisValue.isNumber() || !std::isfinite(axisValue.asNumber())) {
75
+ return std::nullopt;
76
+ }
77
+
78
+ axes.emplace_back(propertyName.utf8(rt), axisValue.asNumber());
79
+ }
80
+
81
+ std::sort(axes.begin(), axes.end());
82
+
83
+ std::string result;
84
+
85
+ for (const auto &[axis, axisValue] : axes) {
86
+ auto quotedAxis = quoteAxis(axis);
87
+
88
+ if (!quotedAxis.has_value()) {
89
+ return std::nullopt;
90
+ }
91
+
92
+ if (!result.empty()) {
93
+ result += ", ";
94
+ }
95
+
96
+ // String(value), so numbers are formatted like in JS
97
+ result += quotedAxis.value() + " " +
98
+ jsi::Value(axisValue).toString(rt).utf8(rt);
99
+ }
100
+
101
+ return result;
102
+ }
103
+
104
+ } // namespace margelo::nitro::unistyles::converters
@@ -0,0 +1,17 @@
1
+ #pragma once
2
+
3
+ #include <jsi/jsi.h>
4
+ #include <optional>
5
+ #include <string>
6
+
7
+ namespace margelo::nitro::unistyles::converters {
8
+
9
+ bool isFontVariationSettingsPropName(const std::string &propertyName);
10
+ // { wght: 700, slnt: -10 }, as opposed to a breakpoints object
11
+ bool isFontVariationSettingsObject(facebook::jsi::Runtime &rt,
12
+ facebook::jsi::Object &value);
13
+ std::optional<std::string>
14
+ fontVariationSettingsToString(facebook::jsi::Runtime &rt,
15
+ facebook::jsi::Object &value);
16
+
17
+ } // namespace margelo::nitro::unistyles::converters
@@ -44,7 +44,7 @@ inline static std::vector<std::string> getUnistylesHashKeys(jsi::Runtime& rt, js
44
44
  auto propertyName = propertyNames.getValueAtIndex(rt, i).getString(rt);
45
45
  std::string key = propertyName.utf8(rt);
46
46
 
47
- if (key.compare(0, prefix.length(), prefix) == 0) {
47
+ if (key.compare(0, prefix.length(), prefix) == 0 && object.getProperty(rt, propertyName).isObject()) {
48
48
  matchingKeys.push_back(key);
49
49
  }
50
50
  }
@@ -192,6 +192,21 @@ You likely accessed a style while the runtime was being torn down (eg. during a
192
192
  }
193
193
  }
194
194
 
195
+ if (unistyle->styleKey != helpers::EXOTIC_STYLE_KEY) {
196
+ std::vector<folly::dynamic> arguments = parsedArguments.has_value()
197
+ ? parsedArguments.value()
198
+ : std::vector<folly::dynamic>{};
199
+
200
+ auto unistyleData = std::make_shared<UnistyleData>(unistyle, variants, arguments, std::nullopt);
201
+
202
+ parser.rebuildUnistyleWithVariants(rt, unistyleData);
203
+
204
+ if (unistyleData->parsedStyle.has_value()) {
205
+ return jsi::Value(rt, unistyleData->parsedStyle.value()).asObject(rt);
206
+ }
207
+ }
208
+
209
+ // exotic (unbound) styles have no unistyle data to rebuild into
195
210
  parser.rebuildUnistyle(rt, unistyle, variants, parsedArguments);
196
211
 
197
212
  return jsi::Value(rt, unistyle->parsedStyle.value()).asObject(rt);