@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
package/app.plugin.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Expo config plugin. During `expo prebuild` it runs `lucent build` (so the
|
|
3
|
+
// native package exists before `pod install` / Gradle) and makes sure the app's
|
|
4
|
+
// react-native.config.js links it.
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const { spawnSync } = require("node:child_process");
|
|
8
|
+
|
|
9
|
+
let built = false;
|
|
10
|
+
|
|
11
|
+
function buildOnce(projectRoot) {
|
|
12
|
+
if (built) return;
|
|
13
|
+
built = true;
|
|
14
|
+
const cli = path.join(__dirname, "bin/lucent.cjs");
|
|
15
|
+
// No Gradle during prebuild: android/ is half-written, and a Gradle run would cache it so
|
|
16
|
+
// (autolinking with the template's package). The Gradle task this plugin applies resolves
|
|
17
|
+
// the classpath and builds Android when the app is built.
|
|
18
|
+
const r = spawnSync(process.execPath, [cli, "build", "--root", projectRoot], {
|
|
19
|
+
stdio: "inherit",
|
|
20
|
+
env: { ...process.env, LUCENT_NO_GRADLE: "1" },
|
|
21
|
+
});
|
|
22
|
+
if (r.status !== 0)
|
|
23
|
+
throw new Error("lucent build failed; fix the errors above and run prebuild again");
|
|
24
|
+
const rnConfig = path.join(projectRoot, "react-native.config.js");
|
|
25
|
+
const entry = `"lucent": { root: require("path").join(__dirname, ".lucent", "native") }`;
|
|
26
|
+
const text = fs.existsSync(rnConfig) ? fs.readFileSync(rnConfig, "utf8") : undefined;
|
|
27
|
+
if (text === undefined) {
|
|
28
|
+
fs.writeFileSync(rnConfig, `module.exports = {\n dependencies: {\n ${entry},\n },\n};\n`);
|
|
29
|
+
} else if (text.includes('"lucent-native"')) {
|
|
30
|
+
// Earlier versions named the dependency lucent-native.
|
|
31
|
+
fs.writeFileSync(rnConfig, text.replace('"lucent-native"', '"lucent"'));
|
|
32
|
+
} else if (!/["']lucent["']\s*:/.test(text)) {
|
|
33
|
+
throw new Error(`Add ${entry} to the "dependencies" of react-native.config.js`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The line that applies Lucent's Gradle task (gradle/lucent.gradle) in android/app/build.gradle. */
|
|
38
|
+
const GRADLE_LINE = `apply from: new File(new File(["node", "--print", "require.resolve('@lucent-lang/lucent/package.json')"].execute(null, rootDir).text.trim()).parentFile, "gradle/lucent.gradle")`;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* android/app/build.gradle with the Gradle task applied after React
|
|
42
|
+
* Native's plugin (or the last `apply plugin`, or at the top), or
|
|
43
|
+
* undefined when it is applied already.
|
|
44
|
+
*/
|
|
45
|
+
function applyGradleTask(text) {
|
|
46
|
+
if (text.includes("gradle/lucent.gradle")) return undefined;
|
|
47
|
+
const lines = text.split("\n");
|
|
48
|
+
const react = lines.findIndex((l) => /^apply plugin: ["']com\.facebook\.react["']/.test(l));
|
|
49
|
+
const lastApply = lines.reduce((at, l, i) => (l.startsWith("apply plugin:") ? i : at), -1);
|
|
50
|
+
const after = react >= 0 ? react : lastApply;
|
|
51
|
+
lines.splice(after + 1, 0, GRADLE_LINE);
|
|
52
|
+
return lines.join("\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Info.plist entries Lucent packages need (their lucent.json), as the last build recorded them. */
|
|
56
|
+
function packagesInfoPlist(projectRoot) {
|
|
57
|
+
const manifest = path.join(projectRoot, ".lucent", "native", "manifest.json");
|
|
58
|
+
if (!fs.existsSync(manifest)) return {};
|
|
59
|
+
return JSON.parse(fs.readFileSync(manifest, "utf8")).infoPlist || {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function withLucent(config) {
|
|
63
|
+
const { withAppBuildGradle, withDangerousMod, withInfoPlist } = require("expo/config-plugins");
|
|
64
|
+
// Gradle builds run lucent build first, like `lucent init` sets up in bare apps.
|
|
65
|
+
config = withAppBuildGradle(config, (c) => {
|
|
66
|
+
if (c.modResults.language === "groovy")
|
|
67
|
+
c.modResults.contents = applyGradleTask(c.modResults.contents) ?? c.modResults.contents;
|
|
68
|
+
return c;
|
|
69
|
+
});
|
|
70
|
+
// Keys the app sets itself win.
|
|
71
|
+
config = withInfoPlist(config, (c) => {
|
|
72
|
+
buildOnce(c.modRequest.projectRoot);
|
|
73
|
+
for (const [key, { value }] of Object.entries(packagesInfoPlist(c.modRequest.projectRoot))) {
|
|
74
|
+
if (c.modResults[key] === undefined) c.modResults[key] = value;
|
|
75
|
+
}
|
|
76
|
+
return c;
|
|
77
|
+
});
|
|
78
|
+
for (const platform of ["ios", "android"]) {
|
|
79
|
+
config = withDangerousMod(config, [
|
|
80
|
+
platform,
|
|
81
|
+
async (c) => {
|
|
82
|
+
buildOnce(c.modRequest.projectRoot);
|
|
83
|
+
return c;
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
return config;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = withLucent;
|
|
91
|
+
module.exports.GRADLE_LINE = GRADLE_LINE;
|
|
92
|
+
module.exports.applyGradleTask = applyGradleTask;
|
package/bin/lucent.cjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// Runs the bundled CLI, both when installed and in this repository, where
|
|
4
|
+
// `pnpm build` (or the watching `dev` task) keeps dist/ current.
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const { pathToFileURL } = require("node:url");
|
|
8
|
+
|
|
9
|
+
const dist = path.join(__dirname, "../dist/cli.js");
|
|
10
|
+
if (!fs.existsSync(dist)) {
|
|
11
|
+
process.stderr.write(
|
|
12
|
+
"lucent: dist/cli.js is missing; run `pnpm build` in the repository first\n",
|
|
13
|
+
);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
import(pathToFileURL(dist).href);
|
package/core/index.cjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// lucent:core's JavaScript implementation, for tests that run Lucent modules
|
|
3
|
+
// as plain TypeScript: point Jest's moduleNameMapper or Vitest's alias for
|
|
4
|
+
// `lucent:core` here. Installed packages ship it as lib/core.cjs (copied by
|
|
5
|
+
// the package build, vite.config.ts); in this repository it is the
|
|
6
|
+
// compiler's source.
|
|
7
|
+
const fs = require("node:fs");
|
|
8
|
+
const path = require("node:path");
|
|
9
|
+
|
|
10
|
+
const inRepository = fs.existsSync(path.join(__dirname, "../src"));
|
|
11
|
+
module.exports = require(inRepository ? "../../compiler/lib/core.cjs" : "../lib/core.cjs");
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { d as coreJsPath, g as projectFiles, m as moduleNameOf, n as compile, s as runtimeDir } from "./src-BD7f1_aZ.js";
|
|
2
|
+
import { i as table } from "./format-D9fnIMEx.js";
|
|
3
|
+
import { t as renderDiagnostic } from "./diagnostic-BM9sXmwl.js";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import ts from "typescript";
|
|
8
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
//#region ../runtime/test/sources.ts
|
|
11
|
+
function runtimeSources(cppDir) {
|
|
12
|
+
const list = (dir, ext) => fs.readdirSync(path.join(cppDir, dir)).filter((f) => f.endsWith(ext)).map((f) => path.join(cppDir, dir, f));
|
|
13
|
+
return {
|
|
14
|
+
cxx: [...list("lucent", ".cpp"), ...list("lucent/jsi", ".cpp")],
|
|
15
|
+
c: list("third_party/quickjs", ".c")
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const cFlags = [
|
|
19
|
+
"-std=c11",
|
|
20
|
+
"-O2",
|
|
21
|
+
"-w"
|
|
22
|
+
];
|
|
23
|
+
const hostLibs = process.platform === "darwin" ? ["-framework", "CoreFoundation"] : [];
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/cli/commands/bench.ts
|
|
26
|
+
const HERMES_FIX = "lucent bench runs your modules in a desktop Hermes: build one (git clone https://github.com/facebook/hermes ~/hermes && cmake -S ~/hermes -B ~/hermes/build -G Ninja -DCMAKE_BUILD_TYPE=Release && ninja -C ~/hermes/build hermesvm jsi), or point HERMES_DIR at a build";
|
|
27
|
+
async function run({ root, out }) {
|
|
28
|
+
const t = out.theme;
|
|
29
|
+
const fail = (message) => {
|
|
30
|
+
if (out.json) out.data({
|
|
31
|
+
ok: false,
|
|
32
|
+
error: message
|
|
33
|
+
});
|
|
34
|
+
else out.error(`${t.error(t.symbols.fail)} ${message}`);
|
|
35
|
+
return 1;
|
|
36
|
+
};
|
|
37
|
+
const benches = findBenches(root);
|
|
38
|
+
if (!benches.length) return fail("no *.bench.ts file: next to a module, export default { name: () => call(), … }");
|
|
39
|
+
const hermes = process.env.HERMES_DIR || path.join(os.homedir(), "hermes");
|
|
40
|
+
if (!fs.existsSync(path.join(hermes, "build/lib")) || !fs.existsSync(path.join(hermes, "API/jsi"))) return fail(`no Hermes build at ${hermes}. ${HERMES_FIX}`);
|
|
41
|
+
const files = projectFiles(root);
|
|
42
|
+
const result = compile(files, { platforms: ["host"] });
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
for (const d of result.diagnostics) out.error(renderDiagnostic({
|
|
45
|
+
...d,
|
|
46
|
+
file: d.file && path.relative(root, d.file)
|
|
47
|
+
}, d.file && fs.existsSync(d.file) ? fs.readFileSync(d.file, "utf8") : void 0, t));
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
const work = path.join(root, ".lucent/bench");
|
|
51
|
+
const generated = path.join(work, "generated");
|
|
52
|
+
fs.rmSync(generated, {
|
|
53
|
+
recursive: true,
|
|
54
|
+
force: true
|
|
55
|
+
});
|
|
56
|
+
for (const [name, content] of result.files) {
|
|
57
|
+
const file = path.join(generated, name.replace(/^host\//, ""));
|
|
58
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
59
|
+
fs.writeFileSync(file, content);
|
|
60
|
+
}
|
|
61
|
+
if (!out.json) out.print(t.dim("building the native host (the first run compiles the runtime)…"));
|
|
62
|
+
const exe = await buildHost(work, generated, hermes);
|
|
63
|
+
const script = path.join(work, "bench.js");
|
|
64
|
+
fs.writeFileSync(script, benchScript(root, files, benches, result.proxies));
|
|
65
|
+
const r = spawnSync(exe, [script], {
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
timeout: 6e5,
|
|
68
|
+
maxBuffer: 64 << 20
|
|
69
|
+
});
|
|
70
|
+
if (r.status !== 0) return fail(`the benchmark failed:\n${r.stderr || r.stdout}`);
|
|
71
|
+
const cases = r.stdout.trim().split("\n").filter((l) => l.startsWith("{")).map((l) => {
|
|
72
|
+
const c = JSON.parse(l);
|
|
73
|
+
return {
|
|
74
|
+
...c,
|
|
75
|
+
speedup: c.error ? 0 : c.js / c.native
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
if (out.json) {
|
|
79
|
+
out.data({
|
|
80
|
+
ok: cases.every((c) => c.same && !c.error),
|
|
81
|
+
cases
|
|
82
|
+
});
|
|
83
|
+
return cases.every((c) => c.same && !c.error) ? 0 : 1;
|
|
84
|
+
}
|
|
85
|
+
const time = (ms) => ms < 1 ? `${(ms * 1e3).toFixed(1)} µs` : `${ms.toFixed(ms < 10 ? 2 : 1)} ms`;
|
|
86
|
+
const row = (c) => c.error ? [
|
|
87
|
+
c.name,
|
|
88
|
+
t.error(c.error),
|
|
89
|
+
"",
|
|
90
|
+
""
|
|
91
|
+
] : [
|
|
92
|
+
c.name,
|
|
93
|
+
time(c.js),
|
|
94
|
+
time(c.native),
|
|
95
|
+
`${c.speedup >= 1 ? t.success(`${c.speedup.toFixed(1)}x`) : t.warn(`${c.speedup.toFixed(1)}x`)}${c.same ? "" : t.error(" results differ")}`
|
|
96
|
+
];
|
|
97
|
+
const lines = table([[
|
|
98
|
+
"CASE",
|
|
99
|
+
"JS",
|
|
100
|
+
"LUCENT",
|
|
101
|
+
"SPEEDUP"
|
|
102
|
+
].map((h) => t.dim(h)), ...cases.map(row)], 3);
|
|
103
|
+
out.print(`\n${lines[0]}`);
|
|
104
|
+
cases.forEach((c, i) => {
|
|
105
|
+
if (c.file !== cases[i - 1]?.file) out.print(t.bold(c.file));
|
|
106
|
+
out.print(`${lines[i + 1]}`);
|
|
107
|
+
});
|
|
108
|
+
out.print(t.dim("\nper call, best of 5 rounds; desktop Hermes, so devices differ"));
|
|
109
|
+
return cases.every((c) => c.same && !c.error) ? 0 : 1;
|
|
110
|
+
}
|
|
111
|
+
function findBenches(root) {
|
|
112
|
+
const out = [];
|
|
113
|
+
const walk = (dir) => {
|
|
114
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
115
|
+
if (e.name === "node_modules" || e.name.startsWith(".") || e.name === "ios" || e.name === "android") continue;
|
|
116
|
+
const full = path.join(dir, e.name);
|
|
117
|
+
if (e.isDirectory()) walk(full);
|
|
118
|
+
else if (e.name.endsWith(".bench.ts")) out.push(full);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
walk(root);
|
|
122
|
+
return out.sort();
|
|
123
|
+
}
|
|
124
|
+
async function objects(work, sources) {
|
|
125
|
+
const dir = path.join(work, "obj");
|
|
126
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
127
|
+
const jobs = sources.map((s) => {
|
|
128
|
+
const key = createHash("sha256").update(s.cc).update(s.flags.join(" ")).update(s.file).update(fs.readFileSync(s.file)).digest("hex").slice(0, 16);
|
|
129
|
+
return {
|
|
130
|
+
...s,
|
|
131
|
+
obj: path.join(dir, `${path.basename(s.file).replace(/\.\w+$/, "")}-${key}.o`)
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
const todo = jobs.filter((j) => !fs.existsSync(j.obj));
|
|
135
|
+
let next = 0;
|
|
136
|
+
const worker = async () => {
|
|
137
|
+
while (next < todo.length) {
|
|
138
|
+
const j = todo[next++];
|
|
139
|
+
await new Promise((resolve, reject) => {
|
|
140
|
+
const child = spawn(j.cc, [
|
|
141
|
+
...j.flags,
|
|
142
|
+
"-c",
|
|
143
|
+
j.file,
|
|
144
|
+
"-o",
|
|
145
|
+
`${j.obj}.tmp`
|
|
146
|
+
], { stdio: [
|
|
147
|
+
"ignore",
|
|
148
|
+
"ignore",
|
|
149
|
+
"pipe"
|
|
150
|
+
] });
|
|
151
|
+
let err = "";
|
|
152
|
+
child.stderr.on("data", (d) => err += d.toString());
|
|
153
|
+
child.on("error", reject);
|
|
154
|
+
child.on("close", (code) => code === 0 ? (fs.renameSync(`${j.obj}.tmp`, j.obj), resolve()) : reject(/* @__PURE__ */ new Error(`${j.cc} failed on ${j.file}:\n${err}`)));
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
await Promise.all(Array.from({ length: Math.max(1, os.availableParallelism() - 1) }, worker));
|
|
159
|
+
return jobs.map((j) => j.obj);
|
|
160
|
+
}
|
|
161
|
+
async function buildHost(work, generated, hermes) {
|
|
162
|
+
const cpp = path.join(runtimeDir(), "cpp");
|
|
163
|
+
const cxx = process.env.CXX ?? "clang++";
|
|
164
|
+
const flags = [
|
|
165
|
+
"-std=c++20",
|
|
166
|
+
"-ffp-contract=off",
|
|
167
|
+
"-O2",
|
|
168
|
+
"-DNDEBUG",
|
|
169
|
+
"-w",
|
|
170
|
+
`-I${cpp}`,
|
|
171
|
+
`-I${generated}`,
|
|
172
|
+
`-I${hermes}/API`,
|
|
173
|
+
`-I${hermes}/API/jsi`,
|
|
174
|
+
`-I${hermes}/public`
|
|
175
|
+
];
|
|
176
|
+
const rs = runtimeSources(cpp);
|
|
177
|
+
const objs = await objects(work, [
|
|
178
|
+
...fs.readdirSync(generated).filter((f) => f.endsWith(".cpp")).map((f) => ({
|
|
179
|
+
file: path.join(generated, f),
|
|
180
|
+
cc: cxx,
|
|
181
|
+
flags
|
|
182
|
+
})),
|
|
183
|
+
...rs.cxx.map((file) => ({
|
|
184
|
+
file,
|
|
185
|
+
cc: cxx,
|
|
186
|
+
flags
|
|
187
|
+
})),
|
|
188
|
+
...rs.c.map((file) => ({
|
|
189
|
+
file,
|
|
190
|
+
cc: process.env.CC ?? "clang",
|
|
191
|
+
flags: cFlags
|
|
192
|
+
})),
|
|
193
|
+
{
|
|
194
|
+
file: path.join(runtimeDir(), "test/jsi/harness.cpp"),
|
|
195
|
+
cc: cxx,
|
|
196
|
+
flags
|
|
197
|
+
}
|
|
198
|
+
]);
|
|
199
|
+
const exe = path.join(work, "bench-host");
|
|
200
|
+
const r = spawnSync(cxx, [
|
|
201
|
+
...objs,
|
|
202
|
+
`-L${hermes}/build/lib`,
|
|
203
|
+
`-L${hermes}/build/jsi`,
|
|
204
|
+
"-lhermesvm",
|
|
205
|
+
"-ljsi",
|
|
206
|
+
"-lpthread",
|
|
207
|
+
...hostLibs,
|
|
208
|
+
`-Wl,-rpath,${hermes}/build/lib`,
|
|
209
|
+
`-Wl,-rpath,${hermes}/build/jsi`,
|
|
210
|
+
"-o",
|
|
211
|
+
exe
|
|
212
|
+
], { encoding: "utf8" });
|
|
213
|
+
if (r.status !== 0) throw new Error(`linking the benchmark host failed:\n${r.stderr}`);
|
|
214
|
+
return exe;
|
|
215
|
+
}
|
|
216
|
+
function benchScript(root, files, benches, proxies) {
|
|
217
|
+
const defs = {};
|
|
218
|
+
const jsDeps = {};
|
|
219
|
+
const nativeDeps = {};
|
|
220
|
+
const transpile = (source) => ts.transpileModule(source, { compilerOptions: {
|
|
221
|
+
module: ts.ModuleKind.CommonJS,
|
|
222
|
+
target: ts.ScriptTarget.ES2019,
|
|
223
|
+
esModuleInterop: false
|
|
224
|
+
} }).outputText;
|
|
225
|
+
const loader = fs.readFileSync(path.join(runtimeDir(), "js/index.js"), "utf8");
|
|
226
|
+
defs.core = fs.readFileSync(coreJsPath(), "utf8");
|
|
227
|
+
defs.loader = loader;
|
|
228
|
+
defs["react-native"] = "module.exports = { TurboModuleRegistry: { get: function () { return undefined; } } };";
|
|
229
|
+
const resolve = (from, spec) => {
|
|
230
|
+
if (spec === "lucent:core") return "core";
|
|
231
|
+
if (!spec.startsWith(".")) return void 0;
|
|
232
|
+
const base = path.resolve(path.dirname(from), spec);
|
|
233
|
+
return [
|
|
234
|
+
`${base}.ts`,
|
|
235
|
+
base,
|
|
236
|
+
`${base}.tsx`
|
|
237
|
+
].find((f) => files.includes(f) || benches.includes(f));
|
|
238
|
+
};
|
|
239
|
+
const specs = (source) => [...source.matchAll(/(?:from|import)\s+["']([^"']+)["']/g)].map((m) => m[1]);
|
|
240
|
+
for (const file of [...files, ...benches]) {
|
|
241
|
+
const source = fs.readFileSync(file, "utf8");
|
|
242
|
+
defs[file] = transpile(source);
|
|
243
|
+
jsDeps[file] = {};
|
|
244
|
+
nativeDeps[file] = {};
|
|
245
|
+
for (const spec of specs(source)) {
|
|
246
|
+
const target = resolve(file, spec);
|
|
247
|
+
if (!target) continue;
|
|
248
|
+
jsDeps[file][spec] = target;
|
|
249
|
+
nativeDeps[file][spec] = files.includes(target) ? `proxy:${moduleNameOf(target)}` : target;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
for (const [name, proxy] of proxies) {
|
|
253
|
+
defs[`proxy:${name}`] = proxy;
|
|
254
|
+
nativeDeps[`proxy:${name}`] = {
|
|
255
|
+
[`${"../".repeat(name.split("/").length - 1) || "./"}_lucent/runtime.js`]: "loader",
|
|
256
|
+
"react-native": "react-native"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
nativeDeps.loader = {};
|
|
260
|
+
return `var defs = {
|
|
261
|
+
${Object.entries(defs).map(([id, code]) => `${JSON.stringify(id)}: function (module, exports, require) {\n${code}\n}`).join(",\n")}
|
|
262
|
+
};
|
|
263
|
+
var deps = { js: ${JSON.stringify(jsDeps)}, native: ${JSON.stringify(nativeDeps)} };
|
|
264
|
+
var loaded = { js: {}, native: {} };
|
|
265
|
+
function load(world, id) {
|
|
266
|
+
var cache = loaded[world];
|
|
267
|
+
if (cache[id]) return cache[id].exports;
|
|
268
|
+
var module = { exports: {} };
|
|
269
|
+
cache[id] = module;
|
|
270
|
+
defs[id](module, module.exports, function (spec) {
|
|
271
|
+
var target = (deps[world][id] || {})[spec];
|
|
272
|
+
if (target === undefined) throw new Error("cannot resolve " + spec + " from " + id);
|
|
273
|
+
return load(world, target);
|
|
274
|
+
});
|
|
275
|
+
return module.exports;
|
|
276
|
+
}
|
|
277
|
+
// Milliseconds per call: the best of 5 rounds, each repeating the call for
|
|
278
|
+
// at least 30 ms, so fast cases are measured beyond the clock's resolution.
|
|
279
|
+
function best(f) {
|
|
280
|
+
var min = Infinity;
|
|
281
|
+
for (var r = 0; r < 5; r++) {
|
|
282
|
+
var start = Date.now(), calls = 0, elapsed;
|
|
283
|
+
do { f(); calls++; elapsed = Date.now() - start; } while (elapsed < 30);
|
|
284
|
+
min = Math.min(min, elapsed / calls);
|
|
285
|
+
}
|
|
286
|
+
return min;
|
|
287
|
+
}
|
|
288
|
+
var benches = ${JSON.stringify(benches.map((b) => [b, path.relative(root, b)]))};
|
|
289
|
+
for (var i = 0; i < benches.length; i++) {
|
|
290
|
+
var id = benches[i][0], file = benches[i][1];
|
|
291
|
+
var js = load("js", id).default, native = load("native", id).default;
|
|
292
|
+
for (var name in js) {
|
|
293
|
+
try {
|
|
294
|
+
var same = JSON.stringify(js[name]()) === JSON.stringify(native[name]());
|
|
295
|
+
print(JSON.stringify({ file: file, name: name, js: best(js[name]), native: best(native[name]), same: same }));
|
|
296
|
+
} catch (e) {
|
|
297
|
+
print(JSON.stringify({ file: file, name: name, js: 0, native: 0, same: false, error: String(e && e.message || e) }));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
`;
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
304
|
+
export { run };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { n as version } from "./version-bqpIO5sP.js";
|
|
2
|
+
import { i as table, n as duration } from "./format-D9fnIMEx.js";
|
|
3
|
+
import { t as renderDiagnostic } from "./diagnostic-BM9sXmwl.js";
|
|
4
|
+
import { n as nextText, r as plural, t as buildProject } from "./pipeline-CH7-2cxu.js";
|
|
5
|
+
import { t as plainSteps } from "./steps--_h-FFS7.js";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
//#region src/cli/commands/build-check.ts
|
|
9
|
+
async function buildOrCheck(command, { root, flags, out }) {
|
|
10
|
+
const theme = out.theme;
|
|
11
|
+
const notices = [];
|
|
12
|
+
const notify = (n) => {
|
|
13
|
+
notices.push(n);
|
|
14
|
+
out.print(`${n.level === "ok" ? theme.success(theme.symbols.ok) : theme.warn(theme.symbols.warn)} ${n.text}`);
|
|
15
|
+
};
|
|
16
|
+
if (command === "build") out.print(`${theme.brand(theme.symbols.brand)} ${theme.bold("lucent")} ${theme.dim(version())}\n`);
|
|
17
|
+
const steps = command === "check" || out.json ? plainSteps(() => {}, theme) : out.terminal.interactive ? (await import("./live-steps-BGcWFM9M.js")).liveSteps(theme) : plainSteps((l) => out.print(l), theme);
|
|
18
|
+
let result;
|
|
19
|
+
try {
|
|
20
|
+
result = await buildProject(root, {
|
|
21
|
+
mode: command,
|
|
22
|
+
force: !!flags.force,
|
|
23
|
+
platforms: typeof flags.platforms === "string" && flags.platforms ? flags.platforms.split(",") : void 0,
|
|
24
|
+
out: typeof flags.out === "string" ? flags.out : void 0,
|
|
25
|
+
prefetch: true
|
|
26
|
+
}, steps, notify);
|
|
27
|
+
} finally {
|
|
28
|
+
await steps.close();
|
|
29
|
+
}
|
|
30
|
+
if (out.json) {
|
|
31
|
+
if (result.fatal) out.data({
|
|
32
|
+
ok: false,
|
|
33
|
+
error: result.fatal
|
|
34
|
+
});
|
|
35
|
+
else if (command === "check") out.data({
|
|
36
|
+
ok: result.ok,
|
|
37
|
+
modules: result.modules.map((m) => m.name),
|
|
38
|
+
diagnostics: result.diagnostics,
|
|
39
|
+
errors: result.diagnostics.length,
|
|
40
|
+
ms: result.ms
|
|
41
|
+
});
|
|
42
|
+
else out.data({
|
|
43
|
+
ok: result.ok,
|
|
44
|
+
upToDate: result.upToDate,
|
|
45
|
+
out: path.resolve(root, typeof flags.out === "string" ? flags.out : ".lucent/native"),
|
|
46
|
+
modules: result.modules,
|
|
47
|
+
steps: steps.results,
|
|
48
|
+
diagnostics: result.diagnostics,
|
|
49
|
+
notices,
|
|
50
|
+
next: result.next,
|
|
51
|
+
ms: result.ms
|
|
52
|
+
});
|
|
53
|
+
return result.ok ? 0 : 1;
|
|
54
|
+
}
|
|
55
|
+
if (result.fatal) {
|
|
56
|
+
out.error(`${theme.error(theme.symbols.fail)} ${result.fatal}`);
|
|
57
|
+
return 1;
|
|
58
|
+
}
|
|
59
|
+
if (!result.ok) {
|
|
60
|
+
problems(result.diagnostics, result.modules.length, result.ms, command === "build");
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
if (command === "check") {
|
|
64
|
+
out.print(`${theme.success(theme.symbols.ok)} ${plural(result.modules.length, "module")}, no problems ${theme.dim(result.upToDate ? `${duration(result.ms)} (unchanged since the last check)` : duration(result.ms))}`);
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
if (result.upToDate) return 0;
|
|
68
|
+
out.print("");
|
|
69
|
+
for (const line of table(result.modules.map((m, i) => [
|
|
70
|
+
i ? "" : theme.dim("modules"),
|
|
71
|
+
m.name,
|
|
72
|
+
m.platforms.join(" ")
|
|
73
|
+
]))) out.print(line);
|
|
74
|
+
out.print(`${theme.dim("next")} ${nextText(result.next)}`);
|
|
75
|
+
return 0;
|
|
76
|
+
function problems(diagnostics, modules, ms, build) {
|
|
77
|
+
const sources = /* @__PURE__ */ new Map();
|
|
78
|
+
const source = (file) => {
|
|
79
|
+
if (!sources.has(file)) {
|
|
80
|
+
const abs = path.resolve(root, file);
|
|
81
|
+
sources.set(file, fs.existsSync(abs) ? fs.readFileSync(abs, "utf8") : void 0);
|
|
82
|
+
}
|
|
83
|
+
return sources.get(file);
|
|
84
|
+
};
|
|
85
|
+
out.error("");
|
|
86
|
+
for (const d of diagnostics) out.error(`${renderDiagnostic(d, d.file ? source(d.file) : void 0, theme)}\n`);
|
|
87
|
+
const summary = `${plural(diagnostics.length, "error")} ${theme.dim("·")} ${plural(modules, "module")} ${theme.dim("·")} ${duration(ms)}`;
|
|
88
|
+
out.error(` ${theme.error(summary)}${build ? theme.dim(" nothing was written") : ""}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { buildOrCheck as t };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
//#region src/cli/commands/clean.ts
|
|
5
|
+
function size(dir) {
|
|
6
|
+
if (!fs.existsSync(dir)) return 0;
|
|
7
|
+
const stat = fs.statSync(dir);
|
|
8
|
+
if (!stat.isDirectory()) return stat.size;
|
|
9
|
+
return fs.readdirSync(dir).reduce((n, e) => n + size(path.join(dir, e)), 0);
|
|
10
|
+
}
|
|
11
|
+
const mb = (bytes) => `${(bytes / 1e6).toFixed(1)} MB`;
|
|
12
|
+
function run({ root, flags, out }) {
|
|
13
|
+
const t = out.theme;
|
|
14
|
+
const generated = path.join(root, ".lucent");
|
|
15
|
+
if (fs.existsSync(generated)) {
|
|
16
|
+
const freed = size(generated);
|
|
17
|
+
fs.rmSync(generated, {
|
|
18
|
+
recursive: true,
|
|
19
|
+
force: true
|
|
20
|
+
});
|
|
21
|
+
out.print(`${t.success(t.symbols.ok)} removed .lucent ${t.dim(mb(freed))}`);
|
|
22
|
+
} else out.print(`${t.dim(t.symbols.off)} nothing to remove in the project`);
|
|
23
|
+
if (flags.cache) {
|
|
24
|
+
const cache = path.join(process.env.LUCENT_CACHE_DIR || path.join(process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache"), "lucent"), "sdk");
|
|
25
|
+
const freed = size(cache);
|
|
26
|
+
fs.rmSync(cache, {
|
|
27
|
+
recursive: true,
|
|
28
|
+
force: true
|
|
29
|
+
});
|
|
30
|
+
out.print(`${t.success(t.symbols.ok)} removed the SDK cache ${t.dim(`${mb(freed)}, ${cache.replace(os.homedir(), "~")}; SDK modules are extracted again on first use`)}`);
|
|
31
|
+
}
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { run };
|