@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.
Files changed (125) hide show
  1. package/app.plugin.js +92 -0
  2. package/bin/lucent.cjs +16 -0
  3. package/core/index.cjs +11 -0
  4. package/dist/bench-CR3E7e2e.js +304 -0
  5. package/dist/build-CAIrRSYA.js +7 -0
  6. package/dist/build-check-QozuNJm5.js +92 -0
  7. package/dist/check-1SCmYlqA.js +7 -0
  8. package/dist/clean-BVwpbJ5K.js +35 -0
  9. package/dist/cli.js +406 -0
  10. package/dist/codes-CIuwXt1S.js +260 -0
  11. package/dist/compiler.js +4 -0
  12. package/dist/confirm-BYF5eY3q.js +34 -0
  13. package/dist/dashboard-B-NzjjaN.js +91 -0
  14. package/dist/dev-BYWMP7oC.js +221 -0
  15. package/dist/diagnostic-BM9sXmwl.js +30 -0
  16. package/dist/diff-BTDo3Sro.js +44 -0
  17. package/dist/doctor-C0PrfWT6.js +230 -0
  18. package/dist/doctor-DdR4JnVc.js +30 -0
  19. package/dist/explain--vV1oaRl.js +35 -0
  20. package/dist/format-D9fnIMEx.js +53 -0
  21. package/dist/init-gIabu1S_.js +207 -0
  22. package/dist/live-steps-BGcWFM9M.js +79 -0
  23. package/dist/new-module-C2mtb0op.js +66 -0
  24. package/dist/package-manager-BnxwCySi.js +30 -0
  25. package/dist/package.json +3 -0
  26. package/dist/packages-HyskKIsY.js +150 -0
  27. package/dist/pipeline-CH7-2cxu.js +232 -0
  28. package/dist/project-D8tEet0k.js +150 -0
  29. package/dist/sdk-coverage-BoW_3Efd.js +64 -0
  30. package/dist/sdk-prefetch-mo04JDZV.js +89 -0
  31. package/dist/sdk-search-Iv4K3-8a.js +124 -0
  32. package/dist/sdk-show-CkrdlqlP.js +74 -0
  33. package/dist/sdks-Xoyiy2E4.js +28 -0
  34. package/dist/src-BD7f1_aZ.js +11493 -0
  35. package/dist/steps--_h-FFS7.js +24 -0
  36. package/dist/theme-uv5bc0aK.js +134 -0
  37. package/dist/tsconfig-S4uyLNzf.js +59 -0
  38. package/dist/version-bqpIO5sP.js +26 -0
  39. package/gradle/lucent-classpath.gradle +38 -0
  40. package/gradle/lucent-classpath.init.gradle +6 -0
  41. package/gradle/lucent.gradle +24 -0
  42. package/lib/core.cjs +106 -0
  43. package/lib/globals.d.ts +36 -0
  44. package/lib/sdk/android.d.ts +8 -0
  45. package/lib/sdk/core.d.ts +23 -0
  46. package/lib/sdk/ios.d.ts +33 -0
  47. package/lib/sdk/platform.d.ts +8 -0
  48. package/lib/sdk/thread.d.ts +8 -0
  49. package/metro/index.cjs +72 -0
  50. package/metro/index.d.ts +6 -0
  51. package/metro/transformer.cjs +70 -0
  52. package/package.json +61 -0
  53. package/runtime/cpp/lucent/abort.cpp +61 -0
  54. package/runtime/cpp/lucent/abort.h +54 -0
  55. package/runtime/cpp/lucent/array.h +442 -0
  56. package/runtime/cpp/lucent/async.h +236 -0
  57. package/runtime/cpp/lucent/builtins.cpp +163 -0
  58. package/runtime/cpp/lucent/bytes.h +129 -0
  59. package/runtime/cpp/lucent/console.h +17 -0
  60. package/runtime/cpp/lucent/core.h +129 -0
  61. package/runtime/cpp/lucent/date.cpp +441 -0
  62. package/runtime/cpp/lucent/date.h +82 -0
  63. package/runtime/cpp/lucent/equality.h +87 -0
  64. package/runtime/cpp/lucent/function.h +61 -0
  65. package/runtime/cpp/lucent/generator.h +244 -0
  66. package/runtime/cpp/lucent/helpers.h +226 -0
  67. package/runtime/cpp/lucent/jserror.cpp +51 -0
  68. package/runtime/cpp/lucent/jserror.h +66 -0
  69. package/runtime/cpp/lucent/jsi/convert.cpp +202 -0
  70. package/runtime/cpp/lucent/jsi/convert.h +612 -0
  71. package/runtime/cpp/lucent/jsi/host.cpp +322 -0
  72. package/runtime/cpp/lucent/jsi/host.h +166 -0
  73. package/runtime/cpp/lucent/json.cpp +243 -0
  74. package/runtime/cpp/lucent/json.h +164 -0
  75. package/runtime/cpp/lucent/json_parse.h +138 -0
  76. package/runtime/cpp/lucent/jsstring.cpp +874 -0
  77. package/runtime/cpp/lucent/jsstring.h +152 -0
  78. package/runtime/cpp/lucent/lucent.h +24 -0
  79. package/runtime/cpp/lucent/map.h +344 -0
  80. package/runtime/cpp/lucent/native.cpp +76 -0
  81. package/runtime/cpp/lucent/native.h +116 -0
  82. package/runtime/cpp/lucent/number.cpp +545 -0
  83. package/runtime/cpp/lucent/number.h +190 -0
  84. package/runtime/cpp/lucent/ops.h +165 -0
  85. package/runtime/cpp/lucent/platform/android.cpp +437 -0
  86. package/runtime/cpp/lucent/platform/android.h +117 -0
  87. package/runtime/cpp/lucent/platform/ios.h +297 -0
  88. package/runtime/cpp/lucent/platform/ios.mm +31 -0
  89. package/runtime/cpp/lucent/regexp.cpp +440 -0
  90. package/runtime/cpp/lucent/regexp.h +107 -0
  91. package/runtime/cpp/lucent/scheduler.cpp +153 -0
  92. package/runtime/cpp/lucent/scheduler.h +160 -0
  93. package/runtime/cpp/lucent/unicode_data.inc +4051 -0
  94. package/runtime/cpp/rn/LucentModule.cpp +35 -0
  95. package/runtime/cpp/rn/LucentModule.h +37 -0
  96. package/runtime/cpp/third_party/quickjs/LICENSE +22 -0
  97. package/runtime/cpp/third_party/quickjs/README.md +11 -0
  98. package/runtime/cpp/third_party/quickjs/cutils.c +641 -0
  99. package/runtime/cpp/third_party/quickjs/cutils.h +457 -0
  100. package/runtime/cpp/third_party/quickjs/libregexp-opcode.h +73 -0
  101. package/runtime/cpp/third_party/quickjs/libregexp.c +3448 -0
  102. package/runtime/cpp/third_party/quickjs/libregexp.h +65 -0
  103. package/runtime/cpp/third_party/quickjs/libunicode-table.h +5206 -0
  104. package/runtime/cpp/third_party/quickjs/libunicode.c +2124 -0
  105. package/runtime/cpp/third_party/quickjs/libunicode.h +196 -0
  106. package/runtime/js/index.d.ts +7 -0
  107. package/runtime/js/index.js +60 -0
  108. package/runtime/native/LucentNative.podspec +28 -0
  109. package/runtime/native/android/CMakeLists.txt +51 -0
  110. package/runtime/native/android/build.gradle +22 -0
  111. package/runtime/native/android/include/lucentnative.h +16 -0
  112. package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +25 -0
  113. package/runtime/native/android/src/main/java/dev/lucent/NativeProxy.java +132 -0
  114. package/runtime/native/ios/LucentRegistration.mm +23 -0
  115. package/runtime/native/package.json +6 -0
  116. package/runtime/native/react-native.config.js +19 -0
  117. package/runtime/test/jsi/harness.cpp +181 -0
  118. package/schemas/build.schema.json +152 -0
  119. package/schemas/check.schema.json +74 -0
  120. package/schemas/doctor.schema.json +40 -0
  121. package/schemas/lucent.schema.json +43 -0
  122. package/schemas/sdk-prefetch.schema.json +37 -0
  123. package/schemas/sdk-search.schema.json +54 -0
  124. package/ts-plugin/index.d.ts +4 -0
  125. package/ts-plugin/index.js +99 -0
@@ -0,0 +1,24 @@
1
+ import { a as visibleWidth, n as duration } from "./format-D9fnIMEx.js";
2
+ //#region src/cli/ui/steps.ts
3
+ const TIME_COLUMN = 40;
4
+ function stepLine(r, theme) {
5
+ const text = `${r.status === "failed" ? theme.error(theme.symbols.fail) : r.status === "skipped" ? theme.dim(theme.symbols.off) : theme.success(theme.symbols.ok)} ${r.label}${r.detail ? ` ${theme.dim(r.detail)}` : ""}`;
6
+ const time = r.status === "cached" ? "cached" : r.ms !== void 0 ? duration(r.ms) : "";
7
+ if (!time) return text;
8
+ return `${text}${" ".repeat(Math.max(2, TIME_COLUMN - visibleWidth(text)))}${theme.dim(time)}`;
9
+ }
10
+ function plainSteps(print, theme) {
11
+ const results = [];
12
+ return {
13
+ results,
14
+ start: () => {},
15
+ finish(r) {
16
+ results.push(r);
17
+ print(stepLine(r, theme));
18
+ },
19
+ flush: async () => {},
20
+ close: async () => {}
21
+ };
22
+ }
23
+ //#endregion
24
+ export { stepLine as n, plainSteps as t };
@@ -0,0 +1,134 @@
1
+ import "node:module";
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
21
+ value: mod,
22
+ enumerable: true
23
+ }) : target, mod));
24
+ //#endregion
25
+ //#region src/cli/ui/theme.ts
26
+ var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
27
+ let p = process || {};
28
+ let argv = p.argv || [];
29
+ let env = p.env || {};
30
+ let isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
31
+ let formatter = (open, close, replace = open) => (input) => {
32
+ let string = "" + input, index = string.indexOf(close, open.length);
33
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
34
+ };
35
+ let replaceClose = (string, close, replace, index) => {
36
+ let result = "", cursor = 0;
37
+ do {
38
+ result += string.substring(cursor, index) + replace;
39
+ cursor = index + close.length;
40
+ index = string.indexOf(close, cursor);
41
+ } while (~index);
42
+ return result + string.substring(cursor);
43
+ };
44
+ let createColors = (enabled = isColorSupported) => {
45
+ let f = enabled ? formatter : () => String;
46
+ return {
47
+ isColorSupported: enabled,
48
+ reset: f("\x1B[0m", "\x1B[0m"),
49
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
50
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
51
+ italic: f("\x1B[3m", "\x1B[23m"),
52
+ underline: f("\x1B[4m", "\x1B[24m"),
53
+ inverse: f("\x1B[7m", "\x1B[27m"),
54
+ hidden: f("\x1B[8m", "\x1B[28m"),
55
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
56
+ black: f("\x1B[30m", "\x1B[39m"),
57
+ red: f("\x1B[31m", "\x1B[39m"),
58
+ green: f("\x1B[32m", "\x1B[39m"),
59
+ yellow: f("\x1B[33m", "\x1B[39m"),
60
+ blue: f("\x1B[34m", "\x1B[39m"),
61
+ magenta: f("\x1B[35m", "\x1B[39m"),
62
+ cyan: f("\x1B[36m", "\x1B[39m"),
63
+ white: f("\x1B[37m", "\x1B[39m"),
64
+ gray: f("\x1B[90m", "\x1B[39m"),
65
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
66
+ bgRed: f("\x1B[41m", "\x1B[49m"),
67
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
68
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
69
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
70
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
71
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
72
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
73
+ blackBright: f("\x1B[90m", "\x1B[39m"),
74
+ redBright: f("\x1B[91m", "\x1B[39m"),
75
+ greenBright: f("\x1B[92m", "\x1B[39m"),
76
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
77
+ blueBright: f("\x1B[94m", "\x1B[39m"),
78
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
79
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
80
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
81
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
82
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
83
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
84
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
85
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
86
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
87
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
88
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
89
+ };
90
+ };
91
+ module.exports = createColors();
92
+ module.exports.createColors = createColors;
93
+ })))(), 1);
94
+ const UNICODE = {
95
+ brand: "◆",
96
+ ok: "✓",
97
+ fail: "✗",
98
+ warn: "!",
99
+ busy: "◐",
100
+ on: "●",
101
+ off: "○",
102
+ bar: "│",
103
+ rule: "─",
104
+ ellipsis: "…"
105
+ };
106
+ const ASCII = {
107
+ brand: "*",
108
+ ok: "+",
109
+ fail: "x",
110
+ warn: "!",
111
+ busy: "~",
112
+ on: "o",
113
+ off: ".",
114
+ bar: "|",
115
+ rule: "-",
116
+ ellipsis: "..."
117
+ };
118
+ function createTheme(terminal) {
119
+ const c = import_picocolors.default.createColors(terminal.color);
120
+ const violet = terminal.color ? (s) => `\x1b[38;5;141m${s}\x1b[39m` : (s) => s;
121
+ return {
122
+ terminal,
123
+ symbols: terminal.unicode ? UNICODE : ASCII,
124
+ brand: violet,
125
+ success: c.green,
126
+ error: c.red,
127
+ warn: c.yellow,
128
+ progress: c.cyan,
129
+ dim: c.dim,
130
+ bold: c.bold
131
+ };
132
+ }
133
+ //#endregion
134
+ export { createTheme as t };
@@ -0,0 +1,59 @@
1
+ import ts from "typescript";
2
+ //#region src/cli/tsconfig.ts
3
+ const ENTRY = "\"lucent:*\": [\"./.lucent/native/types/*\"]";
4
+ function withLucentPaths(text) {
5
+ const { error } = ts.parseConfigFileTextToJson("tsconfig.json", text);
6
+ if (error) throw new Error(`tsconfig.json: ${ts.flattenDiagnosticMessageText(error.messageText, "\n")}`);
7
+ const sf = ts.parseJsonText("tsconfig.json", text);
8
+ const root = sf.statements[0]?.expression;
9
+ if (!root || !ts.isObjectLiteralExpression(root)) throw new Error("tsconfig.json: expected an object");
10
+ const options = property(root, "compilerOptions");
11
+ if (!options) return insert(text, sf, root, `"compilerOptions": { "paths": { ${ENTRY} } }`);
12
+ const paths = property(options, "paths");
13
+ if (!paths) return insert(text, sf, options, `"paths": { ${ENTRY} }`);
14
+ if (named(paths, "lucent:*")) return void 0;
15
+ return insert(text, sf, paths, ENTRY);
16
+ }
17
+ function withLucentTsconfig(text) {
18
+ const indexed = withCompilerOption(text, "noUncheckedIndexedAccess", "true");
19
+ return withLucentPaths(indexed ?? text) ?? indexed;
20
+ }
21
+ function withCompilerOption(text, name, value) {
22
+ const { error } = ts.parseConfigFileTextToJson("tsconfig.json", text);
23
+ if (error) throw new Error(`tsconfig.json: ${ts.flattenDiagnosticMessageText(error.messageText, "\n")}`);
24
+ const sf = ts.parseJsonText("tsconfig.json", text);
25
+ const root = sf.statements[0]?.expression;
26
+ if (!root || !ts.isObjectLiteralExpression(root)) throw new Error("tsconfig.json: expected an object");
27
+ const options = property(root, "compilerOptions");
28
+ if (!options) return insert(text, sf, root, `"compilerOptions": { "${name}": ${value} }`);
29
+ const current = named(options, name);
30
+ if (!current) return insert(text, sf, options, `"${name}": ${value}`);
31
+ if (current.initializer.getText(sf) === value) return void 0;
32
+ return `${text.slice(0, current.initializer.getStart(sf))}${value}${text.slice(current.initializer.getEnd())}`;
33
+ }
34
+ function named(o, name) {
35
+ return o.properties.find((p) => ts.isPropertyAssignment(p) && ts.isStringLiteral(p.name) && p.name.text === name);
36
+ }
37
+ function property(o, name) {
38
+ const value = named(o, name)?.initializer;
39
+ if (value === void 0) return void 0;
40
+ if (!ts.isObjectLiteralExpression(value)) throw new Error(`tsconfig.json: "${name}" should be an object`);
41
+ return value;
42
+ }
43
+ function insert(text, sf, o, member) {
44
+ const open = o.getStart(sf);
45
+ const last = o.properties.at(-1);
46
+ if (!last) return `${text.slice(0, open)}{ ${member} }${text.slice(o.getEnd())}`;
47
+ const line = (pos) => sf.getLineAndCharacterOfPosition(pos).line;
48
+ if (line(last.getStart(sf)) === line(open)) return `${text.slice(0, last.getEnd())}, ${member}${text.slice(last.getEnd())}`;
49
+ const lineStart = sf.getPositionOfLineAndCharacter(line(last.getStart(sf)), 0);
50
+ const indent = text.slice(lineStart, last.getStart(sf));
51
+ const comma = /^\s*,/.exec(text.slice(last.getEnd()));
52
+ if (comma) {
53
+ const at = last.getEnd() + comma[0].length;
54
+ return `${text.slice(0, at)}\n${indent}${member},${text.slice(at)}`;
55
+ }
56
+ return `${text.slice(0, last.getEnd())},\n${indent}${member}${text.slice(last.getEnd())}`;
57
+ }
58
+ //#endregion
59
+ export { withLucentTsconfig as n, withLucentPaths as t };
@@ -0,0 +1,26 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ //#region src/cli/version.ts
5
+ let found;
6
+ function lucentPackage() {
7
+ if (found) return found;
8
+ for (let dir = path.dirname(fileURLToPath(import.meta.url)); path.dirname(dir) !== dir; dir = path.dirname(dir)) {
9
+ const file = path.join(dir, "package.json");
10
+ if (!fs.existsSync(file)) continue;
11
+ const pkg = JSON.parse(fs.readFileSync(file, "utf8"));
12
+ if (pkg.name === "@lucent-lang/lucent") return found = {
13
+ dir,
14
+ version: pkg.version ?? "0.0.0"
15
+ };
16
+ }
17
+ throw new Error("lucent is not inside the @lucent-lang/lucent package");
18
+ }
19
+ function version() {
20
+ return lucentPackage().version;
21
+ }
22
+ function packageFile(relative) {
23
+ return path.join(lucentPackage().dir, relative);
24
+ }
25
+ //#endregion
26
+ export { version as n, packageFile as t };
@@ -0,0 +1,38 @@
1
+ // Registers lucentClasspath in the app project it is applied to: writes the
2
+ // app's resolved compile classpath (AARs and jars) to
3
+ // .lucent/android-classpath.json, so lucent:android/<package> can bind any
4
+ // library the app links (AndroidX, Play services, Firebase…).
5
+ // Applied by lucent.gradle (the app's Gradle task) and by
6
+ // lucent-classpath.init.gradle (when lucent build asks Gradle). In an app
7
+ // with lucent.gradle, lucent build's run applies it twice: once registers.
8
+ if (ext.has("lucentClasspathApplied")) return
9
+ ext.lucentClasspathApplied = true
10
+ pluginManager.withPlugin("com.android.application") {
11
+ // Registered after the Android plugin's own afterEvaluate, which creates the variants' configurations.
12
+ afterEvaluate {
13
+ def out = new File(rootProject.projectDir.parentFile, ".lucent/android-classpath.json")
14
+ def artifactType = Attribute.of("artifactType", String)
15
+ def compileClasspath = configurations.findByName("debugCompileClasspath") ?: configurations.findByName("releaseCompileClasspath")
16
+ if (compileClasspath == null) return
17
+ // Libraries from repositories only: the app's other projects (autolinked
18
+ // modules, Lucent's own) would have to be built first, and their preBuild
19
+ // runs lucentBuild, which needs this classpath.
20
+ def viewOf = { String type ->
21
+ compileClasspath.incoming.artifactView {
22
+ lenient(true)
23
+ componentFilter { it instanceof org.gradle.api.artifacts.component.ModuleComponentIdentifier }
24
+ attributes.attribute(artifactType, type)
25
+ }.files
26
+ }
27
+ def aars = viewOf("aar")
28
+ def jars = viewOf("jar")
29
+ tasks.register("lucentClasspath") {
30
+ inputs.files(aars, jars)
31
+ outputs.file(out)
32
+ doLast {
33
+ out.parentFile.mkdirs()
34
+ out.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson([aars: aars.files*.absolutePath.sort(), jars: jars.files*.absolutePath.sort()]))
35
+ }
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,6 @@
1
+ // Gradle init script, passed by lucent build with --init-script: registers
2
+ // lucentClasspath in the app project without touching the app's build files.
3
+ def script = new File(buildscript.sourceFile.parentFile, "lucent-classpath.gradle")
4
+ gradle.allprojects { project ->
5
+ if (project.path == ":app") project.apply(from: script)
6
+ }
@@ -0,0 +1,24 @@
1
+ // Lucent's Gradle task. lucent init applies it in android/app/build.gradle
2
+ // (and the Expo config plugin during prebuild):
3
+ //
4
+ // apply from: new File(new File(["node", "--print", "require.resolve('@lucent-lang/lucent/package.json')"].execute(null, rootDir).text.trim()).parentFile, "gradle/lucent.gradle")
5
+ //
6
+ // Every build then runs `lucent build` first, so .lucent/native matches the
7
+ // *.lucent.ts sources however the app is built: Android Studio, ./gradlew, CI.
8
+ def lucentDir = buildscript.sourceFile.parentFile
9
+ apply from: new File(lucentDir, "lucent-classpath.gradle")
10
+
11
+ def lucentBuild = tasks.register("lucentBuild", Exec) {
12
+ description = "Compiles the app's *.lucent.ts modules into .lucent/native"
13
+ group = "lucent"
14
+ dependsOn "lucentClasspath"
15
+ workingDir rootProject.projectDir.parentFile
16
+ // lucentClasspath just resolved the classpath in this build: lucent build must not start Gradle again.
17
+ environment "LUCENT_GRADLE_CLASSPATH", "1"
18
+ commandLine "node", new File(lucentDir, "../bin/lucent.cjs").canonicalPath, "build"
19
+ }
20
+
21
+ // Before any project's build, the native package's included.
22
+ rootProject.allprojects { p ->
23
+ p.tasks.matching { it.name == "preBuild" }.configureEach { dependsOn(lucentBuild) }
24
+ }
package/lib/core.cjs ADDED
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ // JavaScript implementations of lucent:core, used when a Lucent module
3
+ // runs as plain JavaScript: the differential e2e harness, lucent bench's
4
+ // JavaScript side, and apps' own tests (@lucent-lang/lucent/core). Native
5
+ // builds use the C++ runtime.
6
+
7
+ function delay(ms, signal) {
8
+ return new Promise((resolve, reject) => {
9
+ if (!signal) return void setTimeout(resolve, ms);
10
+ if (signal.aborted) return void reject(signal.reason);
11
+ const onAbort = () => {
12
+ clearTimeout(timer);
13
+ reject(signal.reason);
14
+ };
15
+ const timer = setTimeout(() => {
16
+ signal.removeEventListener("abort", onAbort);
17
+ resolve();
18
+ }, ms);
19
+ signal.addEventListener("abort", onAbort);
20
+ });
21
+ }
22
+
23
+ function error(code, message) {
24
+ const e = new Error(message);
25
+ e.code = code;
26
+ return e;
27
+ }
28
+
29
+ function errorCode(e) {
30
+ return typeof e.code === "string" ? e.code : undefined;
31
+ }
32
+
33
+ function utf8Encode(s) {
34
+ const out = [];
35
+ for (let i = 0; i < s.length; i++) {
36
+ let cp = s.charCodeAt(i);
37
+ if (cp >= 0xd800 && cp <= 0xdbff && i + 1 < s.length) {
38
+ const lo = s.charCodeAt(i + 1);
39
+ if (lo >= 0xdc00 && lo <= 0xdfff) {
40
+ cp = 0x10000 + ((cp - 0xd800) << 10) + (lo - 0xdc00);
41
+ i++;
42
+ } else cp = 0xfffd;
43
+ } else if (cp >= 0xd800 && cp <= 0xdfff) cp = 0xfffd;
44
+ if (cp < 0x80) out.push(cp);
45
+ else if (cp < 0x800) out.push(0xc0 | (cp >> 6), 0x80 | (cp & 63));
46
+ else if (cp < 0x10000) out.push(0xe0 | (cp >> 12), 0x80 | ((cp >> 6) & 63), 0x80 | (cp & 63));
47
+ else
48
+ out.push(
49
+ 0xf0 | (cp >> 18),
50
+ 0x80 | ((cp >> 12) & 63),
51
+ 0x80 | ((cp >> 6) & 63),
52
+ 0x80 | (cp & 63),
53
+ );
54
+ }
55
+ return new Uint8Array(out);
56
+ }
57
+
58
+ function utf8Decode(bytes) {
59
+ let out = "";
60
+ let i = 0;
61
+ const n = bytes.length;
62
+ const cont = (k) => (i + k < n && (bytes[i + k] & 0xc0) === 0x80 ? bytes[i + k] & 63 : -1);
63
+ while (i < n) {
64
+ const c = bytes[i];
65
+ let cp = 0xfffd;
66
+ let len = 1;
67
+ if (c < 0x80) cp = c;
68
+ else if ((c & 0xe0) === 0xc0) {
69
+ const a = cont(1);
70
+ if (a >= 0 && c >= 0xc2) {
71
+ cp = ((c & 31) << 6) | a;
72
+ len = 2;
73
+ }
74
+ } else if ((c & 0xf0) === 0xe0) {
75
+ const a = cont(1),
76
+ b = cont(2);
77
+ if (a >= 0 && b >= 0) {
78
+ const v = ((c & 15) << 12) | (a << 6) | b;
79
+ if (v >= 0x800) {
80
+ cp = v;
81
+ len = 3;
82
+ }
83
+ }
84
+ } else if ((c & 0xf8) === 0xf0) {
85
+ const a = cont(1),
86
+ b = cont(2),
87
+ d = cont(3);
88
+ if (a >= 0 && b >= 0 && d >= 0) {
89
+ const v = ((c & 7) << 18) | (a << 12) | (b << 6) | d;
90
+ if (v >= 0x10000 && v <= 0x10ffff) {
91
+ cp = v;
92
+ len = 4;
93
+ }
94
+ }
95
+ }
96
+ out += String.fromCodePoint(cp);
97
+ i += len;
98
+ }
99
+ return out;
100
+ }
101
+
102
+ function now() {
103
+ return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
104
+ }
105
+
106
+ module.exports = { delay, error, errorCode, utf8Encode, utf8Decode, now };
@@ -0,0 +1,36 @@
1
+ // Globals available to Lucent modules beyond the ES2022 library. Only the
2
+ // Lucent compiler loads this file; React Native, Node and the DOM declare
3
+ // compatible versions, so the same source type-checks in an app's editor.
4
+
5
+ interface Console {
6
+ log(...data: unknown[]): void;
7
+ info(...data: unknown[]): void;
8
+ debug(...data: unknown[]): void;
9
+ warn(...data: unknown[]): void;
10
+ error(...data: unknown[]): void;
11
+ }
12
+
13
+ declare var console: Console;
14
+
15
+ interface AbortSignal {
16
+ readonly aborted: boolean;
17
+ /** Not available in Lucent code: catch the error from throwIfAborted() or delay() instead. */
18
+ readonly reason: any;
19
+ throwIfAborted(): void;
20
+ addEventListener(type: "abort", listener: () => void): void;
21
+ }
22
+
23
+ declare var AbortSignal: {
24
+ prototype: AbortSignal;
25
+ };
26
+
27
+ interface AbortController {
28
+ readonly signal: AbortSignal;
29
+ /** In Lucent code, `reason` must be an Error. */
30
+ abort(reason?: any): void;
31
+ }
32
+
33
+ declare var AbortController: {
34
+ prototype: AbortController;
35
+ new (): AbortController;
36
+ };
@@ -0,0 +1,8 @@
1
+ // lucent:android — Android helpers for platform code.
2
+ import type { Context } from "lucent:android/android.content";
3
+
4
+ /** The application Context. */
5
+ export declare function appContext(): Context;
6
+
7
+ /** Whether the device runs at least API level `api` (`Build.VERSION.SDK_INT >= api`). */
8
+ export declare function available(platform: "android", api: number): boolean;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Helpers available to Lucent modules. Each one has a native implementation
3
+ * in the Lucent runtime and a JavaScript implementation (the e2e harness's
4
+ * core.js) so the same source also runs as plain TypeScript.
5
+ */
6
+
7
+ /** Resolves after `ms` milliseconds; rejects with the signal's reason if it aborts first. */
8
+ export declare function delay(ms: number, signal?: AbortSignal): Promise<void>;
9
+
10
+ /** An Error with a machine-readable `code`, visible to JavaScript as `error.code`. */
11
+ export declare function error(code: string, message: string): Error;
12
+
13
+ /** The `code` of an error created with `error()`, or undefined. */
14
+ export declare function errorCode(e: Error): string | undefined;
15
+
16
+ /** UTF-8 encoding of a string (like `new TextEncoder().encode(s)`). */
17
+ export declare function utf8Encode(s: string): Uint8Array;
18
+
19
+ /** Decodes UTF-8 bytes (like `new TextDecoder().decode(b)`), replacing invalid sequences. */
20
+ export declare function utf8Decode(bytes: Uint8Array): string;
21
+
22
+ /** Milliseconds from a monotonic clock, for measuring durations. */
23
+ export declare function now(): number;
@@ -0,0 +1,33 @@
1
+ // lucent:ios — iOS helpers for platform code.
2
+
3
+ /** Whether the OS is at least `major.minor` (Swift's `#available`). */
4
+ export declare function available(platform: "ios", major: number, minor?: number): boolean;
5
+
6
+ /** The root of Objective-C objects. Values of type `Any` (`id`) arrive as NSObjects. */
7
+ export declare class NSObject {
8
+ private readonly __lucent_NSObject: never;
9
+ protected constructor();
10
+ }
11
+
12
+ /** What can go where Objective-C takes `Any` (`id`), CoreFoundation values included. */
13
+ export type ObjCValue = string | number | boolean | Uint8Array | Date | NSObject | null;
14
+
15
+ /** The main dispatch queue (dispatch_get_main_queue()), for APIs that take a queue. */
16
+ export declare function mainQueue(): NSObject;
17
+
18
+ /** Swift's `as? String`: the string an `Any` holds, or null. */
19
+ export declare function asString(value: NSObject | null): string | null;
20
+ /** Swift's `as? Double` (an NSNumber). */
21
+ export declare function asNumber(value: NSObject | null): number | null;
22
+ /** Swift's `as? Bool` (an NSNumber). */
23
+ export declare function asBoolean(value: NSObject | null): boolean | null;
24
+ /** Swift's `as? Data`. */
25
+ export declare function asData(value: NSObject | null): Uint8Array | null;
26
+ /** Swift's `as? Date`. */
27
+ export declare function asDate(value: NSObject | null): Date | null;
28
+
29
+ /** An out-parameter of a C function (`CFTypeRef *`): pass it, then read `value`. */
30
+ export declare class Out<T> {
31
+ constructor();
32
+ readonly value: T | null;
33
+ }
@@ -0,0 +1,8 @@
1
+ // lucent:platform — which platform the code runs on.
2
+
3
+ /**
4
+ * The platform: `if (PLATFORM === "ios") { … } else { … }` and
5
+ * `PLATFORM === "ios" ? … : …` compile each platform's branch only, so a
6
+ * shared module can use `lucent:ios/…` and `lucent:android/…` in them.
7
+ */
8
+ export declare const PLATFORM: "ios" | "android";
@@ -0,0 +1,8 @@
1
+ // lucent:thread — thread affinity for platform code.
2
+
3
+ /**
4
+ * Runs `f` on the platform's main thread (the main queue on iOS, the main
5
+ * Looper on Android) and resolves with its result. Main-thread-only SDK
6
+ * APIs may only be used inside `f`.
7
+ */
8
+ export declare function main<T>(f: () => T): Promise<T>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ // Metro integration. `*.lucent.ts` files stay the source of truth for types;
3
+ // when bundling, their contents are replaced by the generated JS proxy that
4
+ // forwards to the native module (written by `lucent build`).
5
+ const path = require("node:path");
6
+ const { spawn } = require("node:child_process");
7
+
8
+ /**
9
+ * Wraps a Metro config:
10
+ * module.exports = withLucent(getDefaultConfig(__dirname));
11
+ *
12
+ * When Metro runs as a dev server, `lucent dev --compact` keeps
13
+ * `.lucent/native` up to date while you edit *.lucent.ts files, one line
14
+ * per build in Metro's output (it never reads Metro's keys; for the full
15
+ * dashboard, run `lucent dev` in a terminal of its own). Set
16
+ * `{ watch: false }` or LUCENT_WATCH=0 to turn that off (LUCENT_WATCH=1 forces it).
17
+ */
18
+ function withLucent(config, options = {}) {
19
+ if (shouldWatch(options)) startWatcher(config.projectRoot || process.cwd());
20
+ const upstream =
21
+ (config.transformer && config.transformer.babelTransformerPath) || defaultTransformer();
22
+ // Transformer workers inherit the environment.
23
+ process.env.LUCENT_UPSTREAM_TRANSFORMER = upstream;
24
+ return {
25
+ ...config,
26
+ transformer: {
27
+ ...config.transformer,
28
+ babelTransformerPath: path.join(__dirname, "transformer.cjs"),
29
+ },
30
+ };
31
+ }
32
+
33
+ function defaultTransformer() {
34
+ for (const candidate of [
35
+ "@react-native/metro-babel-transformer",
36
+ "@expo/metro-config/babel-transformer",
37
+ ]) {
38
+ try {
39
+ return require.resolve(candidate, { paths: [process.cwd()] });
40
+ } catch {
41
+ // try the next one
42
+ }
43
+ }
44
+ throw new Error(
45
+ "Lucent: could not find Metro's babel transformer; set transformer.babelTransformerPath first",
46
+ );
47
+ }
48
+
49
+ function shouldWatch(options) {
50
+ if (options.watch !== undefined) return options.watch;
51
+ if (process.env.LUCENT_WATCH === "0") return false;
52
+ if (process.env.LUCENT_WATCH === "1") return true;
53
+ // Dev servers: `react-native start`, `expo start`, `expo run:ios|android`.
54
+ return process.argv.some((a) => a === "start" || a === "run:ios" || a === "run:android");
55
+ }
56
+
57
+ let watcher;
58
+
59
+ function startWatcher(root) {
60
+ // Metro may load the config more than once in a process.
61
+ if (watcher || process.env.LUCENT_WATCH_CHILD) return;
62
+ const bin = path.join(__dirname, "../bin/lucent.cjs");
63
+ // stdin stays Metro's: its keys (r reload, d dev menu) are not ours.
64
+ watcher = spawn(process.execPath, [bin, "dev", "--compact", "--root", root], {
65
+ stdio: ["ignore", "inherit", "inherit"],
66
+ env: { ...process.env, LUCENT_WATCH_CHILD: "1" },
67
+ });
68
+ // Ctrl-C reaches the watcher through the process group; this covers the rest.
69
+ process.on("exit", () => watcher.kill());
70
+ }
71
+
72
+ module.exports = { withLucent };
@@ -0,0 +1,6 @@
1
+ export interface LucentMetroOptions {
2
+ /** Rebuild .lucent/native while the dev server runs (default: on for dev servers; LUCENT_WATCH=0/1 overrides). */
3
+ watch?: boolean;
4
+ }
5
+
6
+ export declare function withLucent<T extends object>(config: T, options?: LucentMetroOptions): T;