@lucent-lang/lucent 0.0.4
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/app.plugin.js +92 -0
- package/bin/lucent.cjs +16 -0
- package/core/index.cjs +11 -0
- package/dist/bench-CR3E7e2e.js +304 -0
- package/dist/build-CAIrRSYA.js +7 -0
- package/dist/build-check-QozuNJm5.js +92 -0
- package/dist/check-1SCmYlqA.js +7 -0
- package/dist/clean-BVwpbJ5K.js +35 -0
- package/dist/cli.js +406 -0
- package/dist/codes-CIuwXt1S.js +260 -0
- package/dist/compiler.js +4 -0
- package/dist/confirm-BYF5eY3q.js +34 -0
- package/dist/dashboard-B-NzjjaN.js +91 -0
- package/dist/dev-BYWMP7oC.js +221 -0
- package/dist/diagnostic-BM9sXmwl.js +30 -0
- package/dist/diff-BTDo3Sro.js +44 -0
- package/dist/doctor-C0PrfWT6.js +230 -0
- package/dist/doctor-DdR4JnVc.js +30 -0
- package/dist/explain--vV1oaRl.js +35 -0
- package/dist/format-D9fnIMEx.js +53 -0
- package/dist/init-gIabu1S_.js +207 -0
- package/dist/live-steps-BGcWFM9M.js +79 -0
- package/dist/new-module-C2mtb0op.js +66 -0
- package/dist/package-manager-BnxwCySi.js +30 -0
- package/dist/package.json +3 -0
- package/dist/packages-HyskKIsY.js +150 -0
- package/dist/pipeline-CH7-2cxu.js +232 -0
- package/dist/project-D8tEet0k.js +150 -0
- package/dist/sdk-coverage-BoW_3Efd.js +64 -0
- package/dist/sdk-prefetch-mo04JDZV.js +89 -0
- package/dist/sdk-search-Iv4K3-8a.js +124 -0
- package/dist/sdk-show-CkrdlqlP.js +74 -0
- package/dist/sdks-Xoyiy2E4.js +28 -0
- package/dist/src-BD7f1_aZ.js +11493 -0
- package/dist/steps--_h-FFS7.js +24 -0
- package/dist/theme-uv5bc0aK.js +134 -0
- package/dist/tsconfig-S4uyLNzf.js +59 -0
- package/dist/version-bqpIO5sP.js +26 -0
- package/gradle/lucent-classpath.gradle +38 -0
- package/gradle/lucent-classpath.init.gradle +6 -0
- package/gradle/lucent.gradle +24 -0
- package/lib/core.cjs +106 -0
- package/lib/globals.d.ts +36 -0
- package/lib/sdk/android.d.ts +8 -0
- package/lib/sdk/core.d.ts +23 -0
- package/lib/sdk/ios.d.ts +33 -0
- package/lib/sdk/platform.d.ts +8 -0
- package/lib/sdk/thread.d.ts +8 -0
- package/metro/index.cjs +72 -0
- package/metro/index.d.ts +6 -0
- package/metro/transformer.cjs +70 -0
- package/package.json +61 -0
- package/runtime/cpp/lucent/abort.cpp +61 -0
- package/runtime/cpp/lucent/abort.h +54 -0
- package/runtime/cpp/lucent/array.h +442 -0
- package/runtime/cpp/lucent/async.h +236 -0
- package/runtime/cpp/lucent/builtins.cpp +163 -0
- package/runtime/cpp/lucent/bytes.h +129 -0
- package/runtime/cpp/lucent/console.h +17 -0
- package/runtime/cpp/lucent/core.h +129 -0
- package/runtime/cpp/lucent/date.cpp +441 -0
- package/runtime/cpp/lucent/date.h +82 -0
- package/runtime/cpp/lucent/equality.h +87 -0
- package/runtime/cpp/lucent/function.h +61 -0
- package/runtime/cpp/lucent/generator.h +244 -0
- package/runtime/cpp/lucent/helpers.h +226 -0
- package/runtime/cpp/lucent/jserror.cpp +51 -0
- package/runtime/cpp/lucent/jserror.h +66 -0
- package/runtime/cpp/lucent/jsi/convert.cpp +202 -0
- package/runtime/cpp/lucent/jsi/convert.h +612 -0
- package/runtime/cpp/lucent/jsi/host.cpp +322 -0
- package/runtime/cpp/lucent/jsi/host.h +166 -0
- package/runtime/cpp/lucent/json.cpp +243 -0
- package/runtime/cpp/lucent/json.h +164 -0
- package/runtime/cpp/lucent/json_parse.h +138 -0
- package/runtime/cpp/lucent/jsstring.cpp +874 -0
- package/runtime/cpp/lucent/jsstring.h +152 -0
- package/runtime/cpp/lucent/lucent.h +24 -0
- package/runtime/cpp/lucent/map.h +344 -0
- package/runtime/cpp/lucent/native.cpp +76 -0
- package/runtime/cpp/lucent/native.h +116 -0
- package/runtime/cpp/lucent/number.cpp +545 -0
- package/runtime/cpp/lucent/number.h +190 -0
- package/runtime/cpp/lucent/ops.h +165 -0
- package/runtime/cpp/lucent/platform/android.cpp +437 -0
- package/runtime/cpp/lucent/platform/android.h +117 -0
- package/runtime/cpp/lucent/platform/ios.h +297 -0
- package/runtime/cpp/lucent/platform/ios.mm +31 -0
- package/runtime/cpp/lucent/regexp.cpp +440 -0
- package/runtime/cpp/lucent/regexp.h +107 -0
- package/runtime/cpp/lucent/scheduler.cpp +153 -0
- package/runtime/cpp/lucent/scheduler.h +160 -0
- package/runtime/cpp/lucent/unicode_data.inc +4051 -0
- package/runtime/cpp/rn/LucentModule.cpp +35 -0
- package/runtime/cpp/rn/LucentModule.h +37 -0
- package/runtime/cpp/third_party/quickjs/LICENSE +22 -0
- package/runtime/cpp/third_party/quickjs/README.md +11 -0
- package/runtime/cpp/third_party/quickjs/cutils.c +641 -0
- package/runtime/cpp/third_party/quickjs/cutils.h +457 -0
- package/runtime/cpp/third_party/quickjs/libregexp-opcode.h +73 -0
- package/runtime/cpp/third_party/quickjs/libregexp.c +3448 -0
- package/runtime/cpp/third_party/quickjs/libregexp.h +65 -0
- package/runtime/cpp/third_party/quickjs/libunicode-table.h +5206 -0
- package/runtime/cpp/third_party/quickjs/libunicode.c +2124 -0
- package/runtime/cpp/third_party/quickjs/libunicode.h +196 -0
- package/runtime/js/index.d.ts +7 -0
- package/runtime/js/index.js +60 -0
- package/runtime/native/LucentNative.podspec +28 -0
- package/runtime/native/android/CMakeLists.txt +51 -0
- package/runtime/native/android/build.gradle +22 -0
- package/runtime/native/android/include/lucentnative.h +16 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +25 -0
- package/runtime/native/android/src/main/java/dev/lucent/NativeProxy.java +132 -0
- package/runtime/native/ios/LucentRegistration.mm +23 -0
- package/runtime/native/package.json +6 -0
- package/runtime/native/react-native.config.js +19 -0
- package/runtime/test/jsi/harness.cpp +181 -0
- package/schemas/build.schema.json +152 -0
- package/schemas/check.schema.json +74 -0
- package/schemas/doctor.schema.json +40 -0
- package/schemas/lucent.schema.json +43 -0
- package/schemas/sdk-prefetch.schema.json +37 -0
- package/schemas/sdk-search.schema.json +54 -0
- package/ts-plugin/index.d.ts +4 -0
- package/ts-plugin/index.js +99 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Test host: runs JavaScript in Hermes with Lucent modules installed, the way
|
|
2
|
+
// the React Native TurboModule does on device.
|
|
3
|
+
//
|
|
4
|
+
// harness <script.js>...
|
|
5
|
+
//
|
|
6
|
+
// Globals available to scripts:
|
|
7
|
+
// __lucent object with every registered Lucent module
|
|
8
|
+
// print(...args) writes a line to stdout
|
|
9
|
+
// __reference undefined unless a reference script defines it
|
|
10
|
+
//
|
|
11
|
+
// The event loop runs JS tasks posted by Lucent, Hermes microtasks and
|
|
12
|
+
// timers until nothing is pending.
|
|
13
|
+
#include <hermes/hermes.h>
|
|
14
|
+
#include <jsi/jsi.h>
|
|
15
|
+
|
|
16
|
+
#include <chrono>
|
|
17
|
+
#include <condition_variable>
|
|
18
|
+
#include <cstdio>
|
|
19
|
+
#include <deque>
|
|
20
|
+
#include <fstream>
|
|
21
|
+
#include <iostream>
|
|
22
|
+
#include <mutex>
|
|
23
|
+
#include <sstream>
|
|
24
|
+
|
|
25
|
+
#include "lucent/jsi/convert.h"
|
|
26
|
+
#include "lucent/jsi/host.h"
|
|
27
|
+
|
|
28
|
+
namespace jsi = facebook::jsi;
|
|
29
|
+
using lucent::js::Host;
|
|
30
|
+
using lucent::js::JsTask;
|
|
31
|
+
|
|
32
|
+
namespace {
|
|
33
|
+
|
|
34
|
+
std::mutex queueMutex;
|
|
35
|
+
std::condition_variable queueCv;
|
|
36
|
+
std::deque<JsTask> queue;
|
|
37
|
+
|
|
38
|
+
void post(JsTask task) {
|
|
39
|
+
{
|
|
40
|
+
std::lock_guard<std::mutex> g(queueMutex);
|
|
41
|
+
queue.push_back(std::move(task));
|
|
42
|
+
}
|
|
43
|
+
queueCv.notify_one();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
struct Timer {
|
|
47
|
+
std::chrono::steady_clock::time_point at;
|
|
48
|
+
std::shared_ptr<jsi::Function> fn;
|
|
49
|
+
};
|
|
50
|
+
std::vector<Timer> timers;
|
|
51
|
+
|
|
52
|
+
std::string readFile(const char* path) {
|
|
53
|
+
std::ifstream in(path);
|
|
54
|
+
if (!in) {
|
|
55
|
+
std::fprintf(stderr, "cannot read %s\n", path);
|
|
56
|
+
std::exit(2);
|
|
57
|
+
}
|
|
58
|
+
std::stringstream ss;
|
|
59
|
+
ss << in.rdbuf();
|
|
60
|
+
return ss.str();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
bool runLoop(jsi::Runtime& rt, double timeoutMs) {
|
|
64
|
+
auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(static_cast<int64_t>(timeoutMs));
|
|
65
|
+
for (;;) {
|
|
66
|
+
rt.drainMicrotasks();
|
|
67
|
+
JsTask task;
|
|
68
|
+
{
|
|
69
|
+
std::unique_lock<std::mutex> g(queueMutex);
|
|
70
|
+
if (!queue.empty()) {
|
|
71
|
+
task = std::move(queue.front());
|
|
72
|
+
queue.pop_front();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (task) {
|
|
76
|
+
task(rt);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
auto now = std::chrono::steady_clock::now();
|
|
80
|
+
bool ranTimer = false;
|
|
81
|
+
for (size_t i = 0; i < timers.size(); i++) {
|
|
82
|
+
if (timers[i].at <= now) {
|
|
83
|
+
auto fn = timers[i].fn;
|
|
84
|
+
timers.erase(timers.begin() + static_cast<std::ptrdiff_t>(i));
|
|
85
|
+
fn->call(rt);
|
|
86
|
+
ranTimer = true;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (ranTimer) continue;
|
|
91
|
+
bool lucentIdle = lucent::Scheduler::instance().pendingWork() == 0;
|
|
92
|
+
if (lucentIdle && timers.empty()) {
|
|
93
|
+
// Give in-flight posts a moment to land.
|
|
94
|
+
std::unique_lock<std::mutex> g(queueMutex);
|
|
95
|
+
if (queue.empty() && !queueCv.wait_for(g, std::chrono::milliseconds(20), [] { return !queue.empty(); })) {
|
|
96
|
+
if (lucent::Scheduler::instance().pendingWork() == 0) return true;
|
|
97
|
+
}
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (now > deadline) return false;
|
|
101
|
+
std::unique_lock<std::mutex> g(queueMutex);
|
|
102
|
+
queueCv.wait_for(g, std::chrono::milliseconds(1));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
} // namespace
|
|
107
|
+
|
|
108
|
+
/// Replaced by hosts that add globals of their own (scripts/bench.ts).
|
|
109
|
+
__attribute__((weak)) void installHarnessExtras(jsi::Runtime&) {}
|
|
110
|
+
|
|
111
|
+
int main(int argc, char** argv) {
|
|
112
|
+
if (argc < 2) {
|
|
113
|
+
std::fprintf(stderr, "usage: harness <script.js>...\n");
|
|
114
|
+
return 2;
|
|
115
|
+
}
|
|
116
|
+
auto config = ::hermes::vm::RuntimeConfig::Builder().withMicrotaskQueue(true).build();
|
|
117
|
+
std::unique_ptr<jsi::Runtime> runtime = facebook::hermes::makeHermesRuntime(config);
|
|
118
|
+
jsi::Runtime& rt = *runtime;
|
|
119
|
+
int status = 0;
|
|
120
|
+
{
|
|
121
|
+
std::shared_ptr<Host> host = Host::create(rt, post);
|
|
122
|
+
rt.global().setProperty(rt, "__lucent", host->modules(rt));
|
|
123
|
+
// What @lucent-lang/runtime's loader looks for outside React Native.
|
|
124
|
+
rt.global().setProperty(rt, "__lucentModules", rt.global().getProperty(rt, "__lucent"));
|
|
125
|
+
installHarnessExtras(rt);
|
|
126
|
+
// A new host for this runtime, the old one invalidated, as when an app
|
|
127
|
+
// reinstalls Lucent without a new runtime (tests of the exports JS kept).
|
|
128
|
+
rt.global().setProperty(rt, "__lucentReplaceHost",
|
|
129
|
+
jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "__lucentReplaceHost"), 0,
|
|
130
|
+
[&host](jsi::Runtime& rt, const jsi::Value&, const jsi::Value*, size_t) {
|
|
131
|
+
host->invalidate();
|
|
132
|
+
host = Host::create(rt, post);
|
|
133
|
+
return jsi::Value::undefined();
|
|
134
|
+
}));
|
|
135
|
+
rt.global().setProperty(
|
|
136
|
+
rt, "print",
|
|
137
|
+
jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "print"), 1,
|
|
138
|
+
[](jsi::Runtime& rt, const jsi::Value&, const jsi::Value* args, size_t n) {
|
|
139
|
+
std::string line;
|
|
140
|
+
for (size_t i = 0; i < n; i++) {
|
|
141
|
+
if (i) line += " ";
|
|
142
|
+
line += args[i].toString(rt).utf8(rt);
|
|
143
|
+
}
|
|
144
|
+
std::printf("%s\n", line.c_str());
|
|
145
|
+
std::fflush(stdout);
|
|
146
|
+
return jsi::Value::undefined();
|
|
147
|
+
}));
|
|
148
|
+
rt.global().setProperty(
|
|
149
|
+
rt, "setTimeout",
|
|
150
|
+
jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "setTimeout"), 2,
|
|
151
|
+
[](jsi::Runtime& rt, const jsi::Value&, const jsi::Value* args, size_t n) {
|
|
152
|
+
double ms = n > 1 && args[1].isNumber() ? args[1].getNumber() : 0;
|
|
153
|
+
timers.push_back(Timer{std::chrono::steady_clock::now() + std::chrono::microseconds(static_cast<int64_t>(ms * 1000)),
|
|
154
|
+
std::make_shared<jsi::Function>(args[0].getObject(rt).getFunction(rt))});
|
|
155
|
+
return jsi::Value::undefined();
|
|
156
|
+
}));
|
|
157
|
+
try {
|
|
158
|
+
for (int i = 1; i < argc; i++) {
|
|
159
|
+
rt.evaluateJavaScript(std::make_shared<jsi::StringBuffer>(readFile(argv[i])), argv[i]);
|
|
160
|
+
if (!runLoop(rt, 20000)) {
|
|
161
|
+
std::fprintf(stderr, "timed out waiting for pending work\n");
|
|
162
|
+
status = 3;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
jsi::Value failures = rt.global().getProperty(rt, "__failures");
|
|
166
|
+
if (failures.isNumber() && failures.getNumber() > 0) status = 1;
|
|
167
|
+
} catch (const jsi::JSError& e) {
|
|
168
|
+
std::fprintf(stderr, "Uncaught JS error: %s\n%s\n", e.getMessage().c_str(), e.getStack().c_str());
|
|
169
|
+
status = 1;
|
|
170
|
+
}
|
|
171
|
+
timers.clear();
|
|
172
|
+
{
|
|
173
|
+
std::lock_guard<std::mutex> g(queueMutex);
|
|
174
|
+
queue.clear();
|
|
175
|
+
}
|
|
176
|
+
host->invalidate();
|
|
177
|
+
}
|
|
178
|
+
rt.global().setProperty(rt, "__lucentHost", jsi::Value::undefined());
|
|
179
|
+
runtime.reset();
|
|
180
|
+
return status;
|
|
181
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "https://lucent-lang.dev/schemas/build.schema.json",
|
|
3
|
+
"title": "lucent build --json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["ok", "upToDate", "out", "modules", "steps", "diagnostics", "notices", "next", "ms"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"ok": {
|
|
9
|
+
"type": "boolean",
|
|
10
|
+
"description": "The native package was written (or was up to date); the exit code is 0 exactly then"
|
|
11
|
+
},
|
|
12
|
+
"upToDate": {
|
|
13
|
+
"type": "boolean",
|
|
14
|
+
"description": "Nothing changed since the last build, so nothing was done"
|
|
15
|
+
},
|
|
16
|
+
"out": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Absolute path of the native package"
|
|
19
|
+
},
|
|
20
|
+
"modules": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"required": ["name", "platforms"],
|
|
25
|
+
"additionalProperties": false,
|
|
26
|
+
"properties": {
|
|
27
|
+
"name": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"platforms": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"items": {
|
|
33
|
+
"enum": ["ios", "android", "shared"]
|
|
34
|
+
},
|
|
35
|
+
"description": "The platforms the module has code for, or shared"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"steps": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"required": ["name", "label", "status"],
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"properties": {
|
|
47
|
+
"name": {
|
|
48
|
+
"enum": ["android-dependencies", "up-to-date", "sdk", "check", "generate", "package"]
|
|
49
|
+
},
|
|
50
|
+
"label": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"status": {
|
|
54
|
+
"enum": ["ok", "cached", "skipped", "failed"]
|
|
55
|
+
},
|
|
56
|
+
"detail": {
|
|
57
|
+
"type": "string"
|
|
58
|
+
},
|
|
59
|
+
"ms": {
|
|
60
|
+
"type": "number",
|
|
61
|
+
"minimum": 0
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"diagnostics": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"required": ["code", "message"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"code": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"pattern": "^LUCENT\\d{4}$",
|
|
75
|
+
"description": "Stable code; lucent explain <code> describes it"
|
|
76
|
+
},
|
|
77
|
+
"message": {
|
|
78
|
+
"type": "string"
|
|
79
|
+
},
|
|
80
|
+
"file": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "Relative to the project root"
|
|
83
|
+
},
|
|
84
|
+
"line": {
|
|
85
|
+
"type": "integer",
|
|
86
|
+
"minimum": 1
|
|
87
|
+
},
|
|
88
|
+
"column": {
|
|
89
|
+
"type": "integer",
|
|
90
|
+
"minimum": 1
|
|
91
|
+
},
|
|
92
|
+
"start": {
|
|
93
|
+
"type": "integer",
|
|
94
|
+
"minimum": 0,
|
|
95
|
+
"description": "Offset of the span in the file"
|
|
96
|
+
},
|
|
97
|
+
"length": {
|
|
98
|
+
"type": "integer",
|
|
99
|
+
"minimum": 0
|
|
100
|
+
},
|
|
101
|
+
"fix": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"description": "How to fix it, in one line"
|
|
104
|
+
},
|
|
105
|
+
"docs": {
|
|
106
|
+
"type": "string",
|
|
107
|
+
"description": "Where the code is explained"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"notices": {
|
|
113
|
+
"type": "array",
|
|
114
|
+
"items": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"required": ["level", "text"],
|
|
117
|
+
"additionalProperties": false,
|
|
118
|
+
"properties": {
|
|
119
|
+
"level": {
|
|
120
|
+
"enum": ["ok", "warn"]
|
|
121
|
+
},
|
|
122
|
+
"text": {
|
|
123
|
+
"type": "string"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"next": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"required": ["rebuild", "podInstall", "reload"],
|
|
131
|
+
"additionalProperties": false,
|
|
132
|
+
"properties": {
|
|
133
|
+
"rebuild": {
|
|
134
|
+
"type": "boolean",
|
|
135
|
+
"description": "Native code changed: rebuild the app"
|
|
136
|
+
},
|
|
137
|
+
"podInstall": {
|
|
138
|
+
"type": "boolean",
|
|
139
|
+
"description": "Native files were added or removed: run pod install before the iOS build"
|
|
140
|
+
},
|
|
141
|
+
"reload": {
|
|
142
|
+
"type": "boolean",
|
|
143
|
+
"description": "Only the JavaScript proxies changed: reloading the app is enough"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"ms": {
|
|
148
|
+
"type": "number",
|
|
149
|
+
"minimum": 0
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "https://lucent-lang.dev/schemas/check.schema.json",
|
|
3
|
+
"title": "lucent check --json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["ok", "modules", "diagnostics", "errors", "ms"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"ok": {
|
|
9
|
+
"type": "boolean",
|
|
10
|
+
"description": "No errors; the exit code is 0 exactly then"
|
|
11
|
+
},
|
|
12
|
+
"modules": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": {
|
|
15
|
+
"type": "string"
|
|
16
|
+
},
|
|
17
|
+
"description": "Module names, as JavaScript loads them"
|
|
18
|
+
},
|
|
19
|
+
"diagnostics": {
|
|
20
|
+
"type": "array",
|
|
21
|
+
"items": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"required": ["code", "message"],
|
|
24
|
+
"properties": {
|
|
25
|
+
"code": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^LUCENT\\d{4}$",
|
|
28
|
+
"description": "Stable code; lucent explain <code> describes it"
|
|
29
|
+
},
|
|
30
|
+
"message": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
},
|
|
33
|
+
"file": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Relative to the project root"
|
|
36
|
+
},
|
|
37
|
+
"line": {
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"minimum": 1
|
|
40
|
+
},
|
|
41
|
+
"column": {
|
|
42
|
+
"type": "integer",
|
|
43
|
+
"minimum": 1
|
|
44
|
+
},
|
|
45
|
+
"start": {
|
|
46
|
+
"type": "integer",
|
|
47
|
+
"minimum": 0,
|
|
48
|
+
"description": "Offset of the span in the file"
|
|
49
|
+
},
|
|
50
|
+
"length": {
|
|
51
|
+
"type": "integer",
|
|
52
|
+
"minimum": 0
|
|
53
|
+
},
|
|
54
|
+
"fix": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"description": "How to fix it, in one line"
|
|
57
|
+
},
|
|
58
|
+
"docs": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "Where the code is explained"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"errors": {
|
|
66
|
+
"type": "integer",
|
|
67
|
+
"minimum": 0
|
|
68
|
+
},
|
|
69
|
+
"ms": {
|
|
70
|
+
"type": "number",
|
|
71
|
+
"minimum": 0
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "https://lucent-lang.dev/schemas/doctor.schema.json",
|
|
3
|
+
"title": "lucent doctor --json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["ok", "checks"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"ok": {
|
|
9
|
+
"type": "boolean",
|
|
10
|
+
"description": "No check failed; the exit code is 0 exactly then"
|
|
11
|
+
},
|
|
12
|
+
"checks": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"required": ["id", "label", "status", "detail"],
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"properties": {
|
|
19
|
+
"id": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Stable: node, package-manager, react-native, expo, xcode, simulator, cocoapods, android-sdk, ndk, jdk, gradle-task, metro, tsconfig, sdk-cache, versions"
|
|
22
|
+
},
|
|
23
|
+
"label": {
|
|
24
|
+
"type": "string"
|
|
25
|
+
},
|
|
26
|
+
"status": {
|
|
27
|
+
"enum": ["ok", "warn", "fail", "skip"]
|
|
28
|
+
},
|
|
29
|
+
"detail": {
|
|
30
|
+
"type": "string"
|
|
31
|
+
},
|
|
32
|
+
"fix": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "What to do about a failure or warning"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "https://lucent-lang.dev/schemas/lucent.schema.json",
|
|
3
|
+
"title": "lucent.json",
|
|
4
|
+
"description": "What a Lucent package's platform code needs from the app that installs it. It sits next to the package's package.json. The app's build merges it with its other Lucent packages'; two packages that disagree on a value fail the build.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"ios": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"description": "What the iOS code needs.",
|
|
11
|
+
"additionalProperties": false,
|
|
12
|
+
"properties": {
|
|
13
|
+
"pods": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"description": "CocoaPods the native package depends on: pod name → version requirement, as in a Podfile (\"~> 1.0\"). Their frameworks can be imported from lucent:ios/* once installed.",
|
|
16
|
+
"additionalProperties": { "type": "string" }
|
|
17
|
+
},
|
|
18
|
+
"infoPlist": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"description": "Info.plist entries: key → string value. The Expo config plugin adds them, unless the app sets the key itself. For bare apps, lucent build names the keys the app's Info.plist lacks.",
|
|
21
|
+
"additionalProperties": { "type": "string" }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"android": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"description": "What the Android code needs.",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"properties": {
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"description": "Gradle artifacts: \"group:artifact\" → version. They become api dependencies of the native package, so lucent:android/* can import their classes.",
|
|
33
|
+
"additionalProperties": { "type": "string" }
|
|
34
|
+
},
|
|
35
|
+
"permissions": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"description": "Android permissions for the manifest, such as \"android.permission.CAMERA\". Permissions the SDK methods you call require are added without listing them here.",
|
|
38
|
+
"items": { "type": "string" }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "https://lucent-lang.dev/schemas/sdk-prefetch.schema.json",
|
|
3
|
+
"title": "lucent sdk prefetch --json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["ok", "modules"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"ok": {
|
|
9
|
+
"type": "boolean"
|
|
10
|
+
},
|
|
11
|
+
"modules": {
|
|
12
|
+
"type": "array",
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["module", "status"],
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"properties": {
|
|
18
|
+
"module": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "lucent:<platform>/<module>"
|
|
21
|
+
},
|
|
22
|
+
"status": {
|
|
23
|
+
"enum": ["ok", "cached", "failed"],
|
|
24
|
+
"description": "ok: extracted now"
|
|
25
|
+
},
|
|
26
|
+
"ms": {
|
|
27
|
+
"type": "number",
|
|
28
|
+
"minimum": 0
|
|
29
|
+
},
|
|
30
|
+
"error": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "https://lucent-lang.dev/schemas/sdk-search.schema.json",
|
|
3
|
+
"title": "lucent sdk search --json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["term", "searched", "matches"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"term": {
|
|
9
|
+
"type": "string"
|
|
10
|
+
},
|
|
11
|
+
"searched": {
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"minimum": 0,
|
|
14
|
+
"description": "Modules searched: the project's imports and the SDK cache"
|
|
15
|
+
},
|
|
16
|
+
"matches": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"required": ["platform", "module", "kind", "name", "import"],
|
|
21
|
+
"additionalProperties": false,
|
|
22
|
+
"properties": {
|
|
23
|
+
"platform": {
|
|
24
|
+
"enum": ["ios", "android"]
|
|
25
|
+
},
|
|
26
|
+
"module": {
|
|
27
|
+
"type": "string"
|
|
28
|
+
},
|
|
29
|
+
"kind": {
|
|
30
|
+
"enum": [
|
|
31
|
+
"class",
|
|
32
|
+
"protocol",
|
|
33
|
+
"interface",
|
|
34
|
+
"enum",
|
|
35
|
+
"struct",
|
|
36
|
+
"method",
|
|
37
|
+
"property",
|
|
38
|
+
"function",
|
|
39
|
+
"constant"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"name": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "The type, or Type.member(params) for members"
|
|
45
|
+
},
|
|
46
|
+
"import": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "The import line that brings the type in"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TypeScript language-service plugin: adds Lucent diagnostics to *.lucent.ts
|
|
3
|
+
// (index.js, not .cjs: tsserver resolves plugin paths without "exports").
|
|
4
|
+
// files. tsserver loads plugins with require() and its own `typescript`,
|
|
5
|
+
// while the compiler is an ES module that walks ASTs from its own
|
|
6
|
+
// `typescript`; so the plugin imports the compiler asynchronously and hands
|
|
7
|
+
// it file paths and unsaved text, never the editor's AST.
|
|
8
|
+
|
|
9
|
+
const LUCENT_FILE = /\.lucent\.tsx?$/;
|
|
10
|
+
const TYPESCRIPT_PASSTHROUGH = "LUCENT9001";
|
|
11
|
+
|
|
12
|
+
function createPlugin(loadCompiler) {
|
|
13
|
+
return function init({ typescript: ts }) {
|
|
14
|
+
function create(info) {
|
|
15
|
+
const log = (msg) => info.project.projectService.logger.info(`[lucent] ${msg}`);
|
|
16
|
+
let compiler;
|
|
17
|
+
loadCompiler().then(
|
|
18
|
+
(c) => {
|
|
19
|
+
compiler = c;
|
|
20
|
+
info.project.refreshDiagnostics();
|
|
21
|
+
},
|
|
22
|
+
(e) => log(`could not load the Lucent compiler: ${e && e.stack ? e.stack : e}`),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// One check serves every file until any Lucent source changes.
|
|
26
|
+
let cache = { key: undefined, byFile: new Map() };
|
|
27
|
+
function lucentDiagnostics(fileName) {
|
|
28
|
+
const files = info.project.getFileNames().filter((f) => LUCENT_FILE.test(f));
|
|
29
|
+
const key = files
|
|
30
|
+
.map((f) => `${f}@${info.languageServiceHost.getScriptVersion(f)}`)
|
|
31
|
+
.join("|");
|
|
32
|
+
if (cache.key !== key) {
|
|
33
|
+
const byFile = new Map();
|
|
34
|
+
const readSource = (f) => {
|
|
35
|
+
const snap = files.includes(f)
|
|
36
|
+
? info.languageServiceHost.getScriptSnapshot(f)
|
|
37
|
+
: undefined;
|
|
38
|
+
return snap ? snap.getText(0, snap.getLength()) : undefined;
|
|
39
|
+
};
|
|
40
|
+
for (const d of compiler.checkSources(files, readSource)) {
|
|
41
|
+
if (d.code === TYPESCRIPT_PASSTHROUGH || !d.file) continue;
|
|
42
|
+
const list = byFile.get(d.file) || [];
|
|
43
|
+
list.push(d);
|
|
44
|
+
byFile.set(d.file, list);
|
|
45
|
+
}
|
|
46
|
+
cache = { key, byFile };
|
|
47
|
+
}
|
|
48
|
+
return cache.byFile.get(fileName) || [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const proxy = Object.create(null);
|
|
52
|
+
for (const k of Object.keys(info.languageService)) {
|
|
53
|
+
const member = info.languageService[k];
|
|
54
|
+
proxy[k] = typeof member === "function" ? member.bind(info.languageService) : member;
|
|
55
|
+
}
|
|
56
|
+
proxy.getSemanticDiagnostics = (fileName) => {
|
|
57
|
+
const own = info.languageService.getSemanticDiagnostics(fileName);
|
|
58
|
+
if (!compiler || !LUCENT_FILE.test(fileName)) return own;
|
|
59
|
+
let extra;
|
|
60
|
+
try {
|
|
61
|
+
const file = info.languageService.getProgram()?.getSourceFile(fileName);
|
|
62
|
+
extra = lucentDiagnostics(fileName).map((d) => ({
|
|
63
|
+
file,
|
|
64
|
+
start: d.start ?? 0,
|
|
65
|
+
length: d.length ?? 0,
|
|
66
|
+
messageText: [
|
|
67
|
+
`${d.code}: ${d.message}`,
|
|
68
|
+
d.fix && `fix: ${d.fix}`,
|
|
69
|
+
d.docs && `docs: ${d.docs}`,
|
|
70
|
+
]
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
.join("\n"),
|
|
73
|
+
category: ts.DiagnosticCategory.Error,
|
|
74
|
+
code: Number(d.code.replace(/^LUCENT/, "")),
|
|
75
|
+
source: "lucent",
|
|
76
|
+
}));
|
|
77
|
+
} catch (e) {
|
|
78
|
+
log(`check failed: ${e && e.stack ? e.stack : e}`);
|
|
79
|
+
return own;
|
|
80
|
+
}
|
|
81
|
+
return [...own, ...extra];
|
|
82
|
+
};
|
|
83
|
+
return proxy;
|
|
84
|
+
}
|
|
85
|
+
return { create };
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Published, the compiler is bundled into dist/; in this repository (which
|
|
90
|
+
// has the sources) it is the workspace package.
|
|
91
|
+
const path = require("node:path");
|
|
92
|
+
const fs = require("node:fs");
|
|
93
|
+
const { pathToFileURL } = require("node:url");
|
|
94
|
+
const compiler = fs.existsSync(path.join(__dirname, "../src"))
|
|
95
|
+
? "@lucent-lang/compiler"
|
|
96
|
+
: pathToFileURL(path.join(__dirname, "../dist/compiler.js")).href;
|
|
97
|
+
|
|
98
|
+
module.exports = createPlugin(() => import(compiler));
|
|
99
|
+
module.exports.createPlugin = createPlugin;
|