@lucent-lang/lucent 0.0.4 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -0
- package/app.plugin.js +46 -10
- package/dist/{bench-CR3E7e2e.js → bench-eEez4jhQ.js} +10 -6
- package/dist/{build-CAIrRSYA.js → build-CpUjeHFz.js} +1 -1
- package/dist/{build-check-QozuNJm5.js → build-check-BA1ZEp9s.js} +28 -13
- package/dist/{check-1SCmYlqA.js → check-BZx3FgI6.js} +1 -1
- package/dist/cli.js +62 -13
- package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
- package/dist/compiler.js +4 -4
- package/dist/{dashboard-B-NzjjaN.js → dashboard-CC-HXhcd.js} +2 -2
- package/dist/{dev-BYWMP7oC.js → dev-DI5yL8zx.js} +62 -23
- package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
- package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
- package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
- package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
- package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
- package/dist/pipeline-BM_VM2X_.js +700 -0
- package/dist/project-BKNoTEEp.js +342 -0
- package/dist/sdk-coverage-iiv3JqMp.js +113 -0
- package/dist/sdk-diff-ByJ1Gw-F.js +109 -0
- package/dist/sdk-lock-BQn7IyRs.js +38 -0
- package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-Bv2pNega.js} +2 -2
- package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-CPGJm_Ne.js} +2 -2
- package/dist/{sdk-show-CkrdlqlP.js → sdk-show-BUkLqRnA.js} +5 -4
- package/dist/sdk-usage-0ZFMAYg1.js +95 -0
- package/dist/src-ChnmWLwn.js +40851 -0
- package/dist/trace-Bi4hkmPx.js +139 -0
- package/lib/core.cjs +368 -1
- package/lib/sdk/android.d.ts +54 -1
- package/lib/sdk/compose.d.ts +94 -0
- package/lib/sdk/compose.schemas.json.gz +0 -0
- package/lib/sdk/core.d.ts +201 -0
- package/lib/sdk/ios.d.ts +75 -2
- package/lib/sdk/ui.d.ts +129 -0
- package/metro/transformer.cjs +11 -2
- package/package.json +4 -3
- package/runtime/cpp/lucent/abort.cpp +59 -11
- package/runtime/cpp/lucent/abort.h +22 -6
- package/runtime/cpp/lucent/array.h +12 -2
- package/runtime/cpp/lucent/async.h +101 -21
- package/runtime/cpp/lucent/bigint.cpp +898 -0
- package/runtime/cpp/lucent/bigint.h +302 -0
- package/runtime/cpp/lucent/buffer.cpp +228 -0
- package/runtime/cpp/lucent/buffer.h +328 -0
- package/runtime/cpp/lucent/builtins.cpp +4 -1
- package/runtime/cpp/lucent/bytes.h +21 -2
- package/runtime/cpp/lucent/callback.cpp +29 -0
- package/runtime/cpp/lucent/callback.h +230 -0
- package/runtime/cpp/lucent/compute.cpp +370 -0
- package/runtime/cpp/lucent/compute.h +455 -0
- package/runtime/cpp/lucent/conversions.h +111 -0
- package/runtime/cpp/lucent/core.h +16 -5
- package/runtime/cpp/lucent/equality.h +107 -28
- package/runtime/cpp/lucent/execution.cpp +416 -0
- package/runtime/cpp/lucent/execution.h +270 -0
- package/runtime/cpp/lucent/extension.cpp +97 -0
- package/runtime/cpp/lucent/extension.h +210 -0
- package/runtime/cpp/lucent/helpers.h +12 -5
- package/runtime/cpp/lucent/items.h +52 -0
- package/runtime/cpp/lucent/jserror.cpp +9 -0
- package/runtime/cpp/lucent/jserror.h +11 -0
- package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
- package/runtime/cpp/lucent/jsi/convert.h +128 -59
- package/runtime/cpp/lucent/jsi/host.cpp +213 -35
- package/runtime/cpp/lucent/jsi/host.h +116 -16
- package/runtime/cpp/lucent/json.h +19 -1
- package/runtime/cpp/lucent/lifecycle.cpp +260 -0
- package/runtime/cpp/lucent/lifecycle.h +124 -0
- package/runtime/cpp/lucent/lucent.h +7 -0
- package/runtime/cpp/lucent/map.h +41 -7
- package/runtime/cpp/lucent/native.cpp +35 -47
- package/runtime/cpp/lucent/native.h +116 -26
- package/runtime/cpp/lucent/number.cpp +38 -38
- package/runtime/cpp/lucent/number.h +33 -0
- package/runtime/cpp/lucent/operation.h +65 -0
- package/runtime/cpp/lucent/ops.h +14 -6
- package/runtime/cpp/lucent/platform/android.cpp +223 -10
- package/runtime/cpp/lucent/platform/android.h +227 -4
- package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
- package/runtime/cpp/lucent/platform/android_requests.h +140 -0
- package/runtime/cpp/lucent/platform/compose.h +256 -0
- package/runtime/cpp/lucent/platform/ios.h +325 -25
- package/runtime/cpp/lucent/platform/ios.mm +1 -14
- package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
- package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
- package/runtime/cpp/lucent/platform/swiftui.h +190 -0
- package/runtime/cpp/lucent/presentation.cpp +11 -0
- package/runtime/cpp/lucent/presentation.h +109 -0
- package/runtime/cpp/lucent/reactive.cpp +406 -0
- package/runtime/cpp/lucent/reactive.h +566 -0
- package/runtime/cpp/lucent/report.cpp +40 -0
- package/runtime/cpp/lucent/report.h +16 -0
- package/runtime/cpp/lucent/resource.cpp +83 -0
- package/runtime/cpp/lucent/resource.h +75 -0
- package/runtime/cpp/lucent/scheduler.cpp +38 -124
- package/runtime/cpp/lucent/scheduler.h +102 -83
- package/runtime/cpp/lucent/scope.cpp +308 -0
- package/runtime/cpp/lucent/scope.h +452 -0
- package/runtime/cpp/lucent/sizing.h +263 -0
- package/runtime/cpp/lucent/slots.h +231 -0
- package/runtime/cpp/lucent/trace.cpp +539 -0
- package/runtime/cpp/lucent/trace.h +251 -0
- package/runtime/cpp/lucent/transport.cpp +9 -0
- package/runtime/cpp/lucent/transport.h +341 -0
- package/runtime/cpp/lucent/view.cpp +123 -0
- package/runtime/cpp/lucent/view.h +157 -0
- package/runtime/cpp/rn/LucentComponentView.h +121 -0
- package/runtime/cpp/rn/LucentComponentView.mm +479 -0
- package/runtime/cpp/rn/LucentModule.cpp +37 -7
- package/runtime/cpp/rn/LucentModule.h +3 -0
- package/runtime/cpp/rn/LucentViewChildren.h +80 -0
- package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
- package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
- package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
- package/runtime/cpp/rn/LucentViewRequests.h +104 -0
- package/runtime/cpp/rn/LucentViewSizing.h +345 -0
- package/runtime/cpp/rn/LucentViewSlots.h +242 -0
- package/runtime/cpp/rn/LucentViewValues.h +125 -0
- package/runtime/cpp/rn/LucentViews.h +440 -0
- package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
- package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
- package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
- package/runtime/cpp/third_party/dragonbox/README.md +16 -0
- package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
- package/runtime/js/index.d.ts +11 -1
- package/runtime/js/index.js +84 -8
- package/runtime/js/views.d.ts +38 -0
- package/runtime/js/views.js +226 -0
- package/runtime/native/android/CMakeLists.txt +4 -1
- package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
- package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
- package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
- package/runtime/test/jsi/harness.cpp +9 -0
- package/schemas/build.schema.json +96 -1
- package/schemas/check.schema.json +56 -1
- package/schemas/lucent.schema.json +495 -9
- package/schemas/sdk-diff.schema.json +70 -0
- package/schemas/sdk-lock.schema.json +67 -0
- package/ts-plugin/index.js +85 -4
- package/dist/pipeline-CH7-2cxu.js +0 -232
- package/dist/project-D8tEet0k.js +0 -150
- package/dist/sdk-coverage-BoW_3Efd.js +0 -64
- package/dist/src-BD7f1_aZ.js +0 -11493
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Lucent runtime — a component's values between its host and its Lucent
|
|
2
|
+
// code: the props a commit holds and a command's arguments become Lucent
|
|
3
|
+
// values, event arguments and a request's answer come back as the plain
|
|
4
|
+
// values the renderer's side holds (LucentViews.h).
|
|
5
|
+
//
|
|
6
|
+
// Plain C++ over Lucent's runtime: both platform hosts' generated code uses
|
|
7
|
+
// it.
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <lucent/lucent.h>
|
|
11
|
+
|
|
12
|
+
#include <optional>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <type_traits>
|
|
15
|
+
#include <utility>
|
|
16
|
+
#include <vector>
|
|
17
|
+
|
|
18
|
+
namespace lucent::views {
|
|
19
|
+
|
|
20
|
+
// --- to Lucent ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
inline String lucentString(const std::string& utf8) { return String::fromUtf8(utf8); }
|
|
23
|
+
|
|
24
|
+
template <class L, class R, class F>
|
|
25
|
+
Array<L> lucentArray(const std::vector<R>& items, F&& each) {
|
|
26
|
+
Array<L> out;
|
|
27
|
+
|
|
28
|
+
for (const auto& item : items) out.push(L(each(item)));
|
|
29
|
+
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A nullable value: null, or `each` of it. */
|
|
34
|
+
template <class L, class R, class F>
|
|
35
|
+
Opt<L> lucentNullable(const std::optional<R>& value, F&& each) {
|
|
36
|
+
if (!value) return Opt<L>(null);
|
|
37
|
+
|
|
38
|
+
return Opt<L>(L(each(*value)));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A value that may be missing: undefined, or `each` of it (which may itself
|
|
43
|
+
* be an Opt, for a nullable value).
|
|
44
|
+
*/
|
|
45
|
+
template <class L, class R, class F>
|
|
46
|
+
Opt<L> lucentOptional(const std::optional<R>& value, F&& each) {
|
|
47
|
+
if (!value) return Opt<L>(undefined);
|
|
48
|
+
|
|
49
|
+
if constexpr (IsOpt<std::decay_t<decltype(each(*value))>>::value)
|
|
50
|
+
return each(*value);
|
|
51
|
+
else
|
|
52
|
+
return Opt<L>(L(each(*value)));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** A value the component requires: a TypeError when the commit lacks it. */
|
|
56
|
+
template <class R>
|
|
57
|
+
const R& required(const std::optional<R>& value, const char* what) {
|
|
58
|
+
if (!value) throwTypeError((std::string(what) + " is missing").c_str());
|
|
59
|
+
|
|
60
|
+
return *value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// --- from Lucent -------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
inline std::string utf8(const String& value) { return value.toUtf8(); }
|
|
66
|
+
|
|
67
|
+
/** Fills `out` with `value`'s elements, each through `each(target, element)`. */
|
|
68
|
+
template <class R, class L, class F>
|
|
69
|
+
void assignArray(std::vector<R>& out, const Array<L>& value, F&& each) {
|
|
70
|
+
out.clear();
|
|
71
|
+
out.reserve(value.size());
|
|
72
|
+
|
|
73
|
+
for (size_t i = 0; i < value.size(); ++i) {
|
|
74
|
+
out.emplace_back();
|
|
75
|
+
each(out.back(), value.at(i));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** `out` empty when `value` is absent (undefined or null), else `each(target, value)`. */
|
|
80
|
+
template <class R, class L, class F>
|
|
81
|
+
void assignOpt(std::optional<R>& out, const Opt<L>& value, F&& each) {
|
|
82
|
+
if (!value.has()) {
|
|
83
|
+
out.reset();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
out.emplace();
|
|
88
|
+
each(*out, value.get());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A value that may be missing and may be null: `out` empty when undefined,
|
|
93
|
+
* holding an empty value when null.
|
|
94
|
+
*/
|
|
95
|
+
template <class R, class L, class F>
|
|
96
|
+
void assignOptionalNullable(std::optional<std::optional<R>>& out, const Opt<L>& value, F&& each) {
|
|
97
|
+
if (value.isUndefined()) {
|
|
98
|
+
out.reset();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
out.emplace();
|
|
103
|
+
|
|
104
|
+
if (value.has()) {
|
|
105
|
+
out->emplace();
|
|
106
|
+
each(**out, value.get());
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The message of what Lucent code threw, for a rejected request. */
|
|
111
|
+
inline std::string thrownMessage(std::exception_ptr thrown) {
|
|
112
|
+
try {
|
|
113
|
+
std::rethrow_exception(thrown);
|
|
114
|
+
} catch (const Exception& e) {
|
|
115
|
+
return e.error() ? e.error()->message.toUtf8() : e.what();
|
|
116
|
+
} catch (const std::exception& e) {
|
|
117
|
+
return e.what();
|
|
118
|
+
} catch (...) {
|
|
119
|
+
return "an unknown error";
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
inline std::string errorMessage(const Error& error) { return error ? error->message.toUtf8() : "an unknown error"; }
|
|
124
|
+
|
|
125
|
+
} // namespace lucent::views
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
// Lucent runtime — what the generated Fabric components share: reading the
|
|
2
|
+
// props React commits and the arguments of commands, comparing committed
|
|
3
|
+
// values, building event payloads, and naming a committed mount.
|
|
4
|
+
//
|
|
5
|
+
// Only the generated view sources (cpp/generated/views/) include it; it
|
|
6
|
+
// depends on React Native's renderer headers.
|
|
7
|
+
//
|
|
8
|
+
// Transport: every JavaScript value arrives as itself, except a nullable one
|
|
9
|
+
// (`T | null`), which the component's proxy boxes: `[value]`, `[null]` for
|
|
10
|
+
// null. React sends a removed prop as null, and turns undefined into null
|
|
11
|
+
// inside objects and argument lists, so without the box a missing value and
|
|
12
|
+
// null would look the same.
|
|
13
|
+
#pragma once
|
|
14
|
+
|
|
15
|
+
#include <folly/dynamic.h>
|
|
16
|
+
#include <jsi/jsi.h>
|
|
17
|
+
#include <react/renderer/core/RawProps.h>
|
|
18
|
+
#include <react/renderer/core/RawValue.h>
|
|
19
|
+
#include <react/renderer/core/ReactPrimitives.h>
|
|
20
|
+
|
|
21
|
+
#include <lucent/report.h>
|
|
22
|
+
|
|
23
|
+
#include "LucentViewRequests.h"
|
|
24
|
+
|
|
25
|
+
#include <array>
|
|
26
|
+
#include <atomic>
|
|
27
|
+
#include <bitset>
|
|
28
|
+
#include <cmath>
|
|
29
|
+
#include <cstddef>
|
|
30
|
+
#include <cstdint>
|
|
31
|
+
#include <functional>
|
|
32
|
+
#include <optional>
|
|
33
|
+
#include <stdexcept>
|
|
34
|
+
#include <string>
|
|
35
|
+
#include <unordered_map>
|
|
36
|
+
#include <utility>
|
|
37
|
+
#include <vector>
|
|
38
|
+
|
|
39
|
+
namespace lucent::views {
|
|
40
|
+
|
|
41
|
+
using facebook::react::RawValue;
|
|
42
|
+
|
|
43
|
+
/** A value of another type than its prop, field or argument declares. */
|
|
44
|
+
struct Mismatch : std::invalid_argument {
|
|
45
|
+
using std::invalid_argument::invalid_argument;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** An object's fields, by name. */
|
|
49
|
+
using Fields = std::unordered_map<std::string, RawValue>;
|
|
50
|
+
|
|
51
|
+
// --- reading ---------------------------------------------------------------------------
|
|
52
|
+
//
|
|
53
|
+
// read(raw, out) for each type a component's values have: the ones here, and
|
|
54
|
+
// the ones each component generates for its objects and enums (found by
|
|
55
|
+
// argument-dependent lookup). All are declared before any is defined, so a
|
|
56
|
+
// template finds the others whatever order they nest in.
|
|
57
|
+
|
|
58
|
+
inline void read(const RawValue& raw, double& out);
|
|
59
|
+
inline void read(const RawValue& raw, bool& out);
|
|
60
|
+
inline void read(const RawValue& raw, std::string& out);
|
|
61
|
+
template <typename T>
|
|
62
|
+
void read(const RawValue& raw, std::vector<T>& out);
|
|
63
|
+
/** A nullable value: boxed. */
|
|
64
|
+
template <typename T>
|
|
65
|
+
void read(const RawValue& raw, std::optional<T>& out);
|
|
66
|
+
|
|
67
|
+
inline void read(const RawValue& raw, double& out) {
|
|
68
|
+
if (!raw.hasType<double>()) throw Mismatch("expected a number");
|
|
69
|
+
|
|
70
|
+
out = static_cast<double>(raw);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
inline void read(const RawValue& raw, bool& out) {
|
|
74
|
+
if (!raw.hasType<bool>()) throw Mismatch("expected a boolean");
|
|
75
|
+
|
|
76
|
+
out = static_cast<bool>(raw);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
inline void read(const RawValue& raw, std::string& out) {
|
|
80
|
+
if (!raw.hasType<std::string>()) throw Mismatch("expected a string");
|
|
81
|
+
|
|
82
|
+
out = static_cast<std::string>(raw);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** A string that must be one of `values` (a string literal union). */
|
|
86
|
+
template <std::size_t N>
|
|
87
|
+
void readEnum(const RawValue& raw, std::string& out, const std::array<const char*, N>& values) {
|
|
88
|
+
read(raw, out);
|
|
89
|
+
|
|
90
|
+
for (const char* value : values)
|
|
91
|
+
if (out == value) return;
|
|
92
|
+
|
|
93
|
+
throw Mismatch("\"" + out + "\" is none of the declared strings");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
inline std::vector<RawValue> items(const RawValue& raw) {
|
|
97
|
+
if (!raw.hasType<std::vector<RawValue>>()) throw Mismatch("expected an array");
|
|
98
|
+
|
|
99
|
+
return static_cast<std::vector<RawValue>>(raw);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
template <typename T>
|
|
103
|
+
void read(const RawValue& raw, std::vector<T>& out) {
|
|
104
|
+
auto all = items(raw);
|
|
105
|
+
|
|
106
|
+
out.clear();
|
|
107
|
+
out.reserve(all.size());
|
|
108
|
+
|
|
109
|
+
for (std::size_t i = 0; i < all.size(); ++i) {
|
|
110
|
+
T value{};
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
read(all[i], value);
|
|
114
|
+
} catch (const Mismatch& e) {
|
|
115
|
+
throw Mismatch("[" + std::to_string(i) + "]: " + e.what());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
out.push_back(std::move(value));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
template <typename T>
|
|
123
|
+
void read(const RawValue& raw, std::optional<T>& out) {
|
|
124
|
+
auto box = items(raw);
|
|
125
|
+
|
|
126
|
+
if (box.size() != 1) throw Mismatch("expected a nullable value's box");
|
|
127
|
+
|
|
128
|
+
if (!box[0].hasValue()) {
|
|
129
|
+
out = std::nullopt;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
T value{};
|
|
134
|
+
read(box[0], value);
|
|
135
|
+
out = std::move(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
inline Fields fields(const RawValue& raw) {
|
|
139
|
+
if (!raw.hasType<Fields>()) throw Mismatch("expected an object");
|
|
140
|
+
|
|
141
|
+
return static_cast<Fields>(raw);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** A field the object must have. */
|
|
145
|
+
template <typename T>
|
|
146
|
+
void field(const Fields& from, const char* name, T& out) {
|
|
147
|
+
auto it = from.find(name);
|
|
148
|
+
|
|
149
|
+
if (it == from.end() || !it->second.hasValue())
|
|
150
|
+
throw Mismatch(std::string("missing field ") + name);
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
read(it->second, out);
|
|
154
|
+
} catch (const Mismatch& e) {
|
|
155
|
+
throw Mismatch(std::string(name) + ": " + e.what());
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** A field the object may leave out: missing when it does, or when it is undefined. */
|
|
160
|
+
template <typename T>
|
|
161
|
+
void optionalField(const Fields& from, const char* name, std::optional<T>& out) {
|
|
162
|
+
auto it = from.find(name);
|
|
163
|
+
|
|
164
|
+
if (it == from.end() || !it->second.hasValue()) {
|
|
165
|
+
out = std::nullopt;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
T value{};
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
read(it->second, value);
|
|
173
|
+
} catch (const Mismatch& e) {
|
|
174
|
+
throw Mismatch(std::string(name) + ": " + e.what());
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
out = std::move(value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// --- props -----------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* A prop in a commit, sent under `key`: the source's value when the commit
|
|
184
|
+
* leaves it alone, missing when React removed it (or never set it), or the
|
|
185
|
+
* value sent. A value of another type is reported and read as missing, as
|
|
186
|
+
* React Native's own props fall back to their defaults.
|
|
187
|
+
*
|
|
188
|
+
* `key` must be a string literal: React Native keeps the pointer.
|
|
189
|
+
*/
|
|
190
|
+
template <typename T>
|
|
191
|
+
std::optional<T> prop(
|
|
192
|
+
const facebook::react::RawProps& raw,
|
|
193
|
+
const char* component,
|
|
194
|
+
const char* name,
|
|
195
|
+
const char* key,
|
|
196
|
+
const std::optional<T>& source) {
|
|
197
|
+
const RawValue* value = raw.at(key);
|
|
198
|
+
|
|
199
|
+
if (value == nullptr) return source;
|
|
200
|
+
if (!value->hasValue()) return std::nullopt;
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
T out{};
|
|
204
|
+
read(*value, out);
|
|
205
|
+
return out;
|
|
206
|
+
} catch (const Mismatch& e) {
|
|
207
|
+
logError((std::string("[lucent] ") + component + " prop " + name + ": " + e.what()).c_str());
|
|
208
|
+
return std::nullopt;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Which events JavaScript listens to, by slot, sent under `keys`: React
|
|
214
|
+
* sends true for a function prop and null once it is removed.
|
|
215
|
+
*/
|
|
216
|
+
template <std::size_t N>
|
|
217
|
+
std::bitset<N> handlers(
|
|
218
|
+
const facebook::react::RawProps& raw,
|
|
219
|
+
const std::array<const char*, N>& keys,
|
|
220
|
+
const std::bitset<N>& source) {
|
|
221
|
+
auto out = source;
|
|
222
|
+
|
|
223
|
+
for (std::size_t slot = 0; slot < N; ++slot)
|
|
224
|
+
if (const RawValue* value = raw.at(keys[slot])) out[slot] = value->hasValue();
|
|
225
|
+
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// --- comparing -------------------------------------------------------------------------
|
|
230
|
+
//
|
|
231
|
+
// same(a, b): whether a committed value is unchanged. Numbers compare as
|
|
232
|
+
// Object.is does (NaN is itself; 0 and -0 differ); arrays and objects by
|
|
233
|
+
// their contents.
|
|
234
|
+
|
|
235
|
+
inline bool same(double a, double b) {
|
|
236
|
+
return a == b ? std::signbit(a) == std::signbit(b) : std::isnan(a) && std::isnan(b);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
inline bool same(bool a, bool b) { return a == b; }
|
|
240
|
+
|
|
241
|
+
inline bool same(const std::string& a, const std::string& b) { return a == b; }
|
|
242
|
+
|
|
243
|
+
template <typename T>
|
|
244
|
+
bool same(const std::vector<T>& a, const std::vector<T>& b);
|
|
245
|
+
template <typename T>
|
|
246
|
+
bool same(const std::optional<T>& a, const std::optional<T>& b);
|
|
247
|
+
|
|
248
|
+
template <typename T>
|
|
249
|
+
bool same(const std::vector<T>& a, const std::vector<T>& b) {
|
|
250
|
+
if (a.size() != b.size()) return false;
|
|
251
|
+
|
|
252
|
+
for (std::size_t i = 0; i < a.size(); ++i)
|
|
253
|
+
if (!same(a[i], b[i])) return false;
|
|
254
|
+
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
template <typename T>
|
|
259
|
+
bool same(const std::optional<T>& a, const std::optional<T>& b) {
|
|
260
|
+
return a && b ? same(*a, *b) : !a && !b;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* same(a, b) for a value of any type, the ones a component generates
|
|
265
|
+
* included: a qualified call (lucent::views::same) would not look them up.
|
|
266
|
+
*/
|
|
267
|
+
template <typename T>
|
|
268
|
+
bool sameValue(const T& a, const T& b) {
|
|
269
|
+
return same(a, b);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// --- event payloads and results ---------------------------------------------------------
|
|
273
|
+
//
|
|
274
|
+
// toJs(runtime, value): the JavaScript value an event argument or a
|
|
275
|
+
// command's result is, exactly: nothing is boxed on the way to JavaScript.
|
|
276
|
+
|
|
277
|
+
namespace jsi = facebook::jsi;
|
|
278
|
+
|
|
279
|
+
inline jsi::Value toJs(jsi::Runtime&, double value) { return jsi::Value(value); }
|
|
280
|
+
|
|
281
|
+
inline jsi::Value toJs(jsi::Runtime&, bool value) { return jsi::Value(value); }
|
|
282
|
+
|
|
283
|
+
inline jsi::Value toJs(jsi::Runtime& runtime, const std::string& value) {
|
|
284
|
+
return jsi::String::createFromUtf8(runtime, value);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
template <typename T>
|
|
288
|
+
jsi::Value toJs(jsi::Runtime& runtime, const std::vector<T>& value);
|
|
289
|
+
/** A nullable value: null, or the value. */
|
|
290
|
+
template <typename T>
|
|
291
|
+
jsi::Value toJs(jsi::Runtime& runtime, const std::optional<T>& value);
|
|
292
|
+
|
|
293
|
+
template <typename T>
|
|
294
|
+
jsi::Value toJs(jsi::Runtime& runtime, const std::vector<T>& value) {
|
|
295
|
+
jsi::Array out(runtime, value.size());
|
|
296
|
+
|
|
297
|
+
for (std::size_t i = 0; i < value.size(); ++i) out.setValueAtIndex(runtime, i, toJs(runtime, value[i]));
|
|
298
|
+
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
template <typename T>
|
|
303
|
+
jsi::Value toJs(jsi::Runtime& runtime, const std::optional<T>& value) {
|
|
304
|
+
return value ? toJs(runtime, *value) : jsi::Value::null();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* toJs(runtime, value) for a value of any type, the ones a component
|
|
309
|
+
* generates included: a qualified call (lucent::views::toJs) would not
|
|
310
|
+
* look them up.
|
|
311
|
+
*/
|
|
312
|
+
template <typename T>
|
|
313
|
+
jsi::Value toJsValue(jsi::Runtime& runtime, const T& value) {
|
|
314
|
+
return toJs(runtime, value);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Sets a property of an object being built. */
|
|
318
|
+
template <typename T>
|
|
319
|
+
void set(jsi::Runtime& runtime, jsi::Object& object, const char* name, const T& value) {
|
|
320
|
+
object.setProperty(runtime, name, toJs(runtime, value));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Sets a property the value may leave out: an absent one stays absent. */
|
|
324
|
+
template <typename T>
|
|
325
|
+
void setOptional(jsi::Runtime& runtime, jsi::Object& object, const char* name, const std::optional<T>& value) {
|
|
326
|
+
if (value) set(runtime, object, name, *value);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* An event's arguments, as the payload `{ args: [...] }`: by position,
|
|
331
|
+
* because React Native adds the view's tag to every payload as `target`.
|
|
332
|
+
* An optional argument the call left out is undefined, and the ones it
|
|
333
|
+
* left out at the end are not there, so the callback gets as many
|
|
334
|
+
* arguments as the call passed.
|
|
335
|
+
*/
|
|
336
|
+
class EventArguments {
|
|
337
|
+
public:
|
|
338
|
+
template <typename T>
|
|
339
|
+
void add(jsi::Runtime& runtime, const T& value) {
|
|
340
|
+
values_.push_back(toJs(runtime, value));
|
|
341
|
+
passed_ = values_.size();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
template <typename T>
|
|
345
|
+
void addOptional(jsi::Runtime& runtime, const std::optional<T>& value) {
|
|
346
|
+
if (value)
|
|
347
|
+
add(runtime, *value);
|
|
348
|
+
else
|
|
349
|
+
values_.push_back(jsi::Value::undefined());
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
jsi::Value payload(jsi::Runtime& runtime) {
|
|
353
|
+
jsi::Array args(runtime, passed_);
|
|
354
|
+
|
|
355
|
+
for (std::size_t i = 0; i < passed_; ++i) args.setValueAtIndex(runtime, i, std::move(values_[i]));
|
|
356
|
+
|
|
357
|
+
jsi::Object out(runtime);
|
|
358
|
+
out.setProperty(runtime, "args", args);
|
|
359
|
+
|
|
360
|
+
return out;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private:
|
|
364
|
+
std::vector<jsi::Value> values_;
|
|
365
|
+
std::size_t passed_ = 0;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// --- commands --------------------------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
/** A command's arguments, as the host received them. */
|
|
371
|
+
inline std::vector<RawValue> arguments(const folly::dynamic& args) {
|
|
372
|
+
if (!args.isArray()) throw Mismatch("expected a command's argument list");
|
|
373
|
+
|
|
374
|
+
std::vector<RawValue> out;
|
|
375
|
+
out.reserve(args.size());
|
|
376
|
+
|
|
377
|
+
for (const auto& arg : args) out.emplace_back(arg);
|
|
378
|
+
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** An argument the call must pass. */
|
|
383
|
+
template <typename T>
|
|
384
|
+
void argument(const std::vector<RawValue>& args, std::size_t index, const char* name, T& out) {
|
|
385
|
+
if (index >= args.size() || !args[index].hasValue())
|
|
386
|
+
throw Mismatch(std::string("missing argument ") + name);
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
read(args[index], out);
|
|
390
|
+
} catch (const Mismatch& e) {
|
|
391
|
+
throw Mismatch(std::string(name) + ": " + e.what());
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** An argument the call may leave out. */
|
|
396
|
+
template <typename T>
|
|
397
|
+
void optionalArgument(
|
|
398
|
+
const std::vector<RawValue>& args,
|
|
399
|
+
std::size_t index,
|
|
400
|
+
const char* name,
|
|
401
|
+
std::optional<T>& out) {
|
|
402
|
+
if (index >= args.size() || !args[index].hasValue()) {
|
|
403
|
+
out = std::nullopt;
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
T value{};
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
read(args[index], value);
|
|
411
|
+
} catch (const Mismatch& e) {
|
|
412
|
+
throw Mismatch(std::string(name) + ": " + e.what());
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
out = std::move(value);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// --- mounts ----------------------------------------------------------------------------
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* A committed mount: the host view's tag and the generation the host gave
|
|
422
|
+
* it when the mount reached the UI. Every event, command and result checks
|
|
423
|
+
* the whole token, so a recycled view's new occupant never receives its
|
|
424
|
+
* predecessor's work.
|
|
425
|
+
*/
|
|
426
|
+
struct MountToken {
|
|
427
|
+
facebook::react::Tag tag{0};
|
|
428
|
+
std::uint64_t generation{0};
|
|
429
|
+
|
|
430
|
+
bool operator==(const MountToken&) const = default;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
/** A generation no mount had before, never 0 (0: not mounted). Any thread. */
|
|
434
|
+
inline std::uint64_t nextGeneration() {
|
|
435
|
+
static std::atomic<std::uint64_t> last{0};
|
|
436
|
+
|
|
437
|
+
return last.fetch_add(1, std::memory_order_relaxed) + 1;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
} // namespace lucent::views
|