@lucent-lang/lucent 0.0.4 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +97 -0
  2. package/app.plugin.js +46 -10
  3. package/dist/{bench-CR3E7e2e.js → bench-eEez4jhQ.js} +10 -6
  4. package/dist/{build-CAIrRSYA.js → build-CpUjeHFz.js} +1 -1
  5. package/dist/{build-check-QozuNJm5.js → build-check-BA1ZEp9s.js} +28 -13
  6. package/dist/{check-1SCmYlqA.js → check-BZx3FgI6.js} +1 -1
  7. package/dist/cli.js +62 -13
  8. package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
  9. package/dist/compiler.js +4 -4
  10. package/dist/{dashboard-B-NzjjaN.js → dashboard-CC-HXhcd.js} +2 -2
  11. package/dist/{dev-BYWMP7oC.js → dev-DI5yL8zx.js} +62 -23
  12. package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
  13. package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
  14. package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
  15. package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
  16. package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
  17. package/dist/pipeline-BM_VM2X_.js +700 -0
  18. package/dist/project-BKNoTEEp.js +342 -0
  19. package/dist/sdk-coverage-iiv3JqMp.js +113 -0
  20. package/dist/sdk-diff-ByJ1Gw-F.js +109 -0
  21. package/dist/sdk-lock-BQn7IyRs.js +38 -0
  22. package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-Bv2pNega.js} +2 -2
  23. package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-CPGJm_Ne.js} +2 -2
  24. package/dist/{sdk-show-CkrdlqlP.js → sdk-show-BUkLqRnA.js} +5 -4
  25. package/dist/sdk-usage-0ZFMAYg1.js +95 -0
  26. package/dist/src-ChnmWLwn.js +40851 -0
  27. package/dist/trace-Bi4hkmPx.js +139 -0
  28. package/lib/core.cjs +368 -1
  29. package/lib/sdk/android.d.ts +54 -1
  30. package/lib/sdk/compose.d.ts +94 -0
  31. package/lib/sdk/compose.schemas.json.gz +0 -0
  32. package/lib/sdk/core.d.ts +201 -0
  33. package/lib/sdk/ios.d.ts +75 -2
  34. package/lib/sdk/ui.d.ts +129 -0
  35. package/metro/transformer.cjs +11 -2
  36. package/package.json +4 -3
  37. package/runtime/cpp/lucent/abort.cpp +59 -11
  38. package/runtime/cpp/lucent/abort.h +22 -6
  39. package/runtime/cpp/lucent/array.h +12 -2
  40. package/runtime/cpp/lucent/async.h +101 -21
  41. package/runtime/cpp/lucent/bigint.cpp +898 -0
  42. package/runtime/cpp/lucent/bigint.h +302 -0
  43. package/runtime/cpp/lucent/buffer.cpp +228 -0
  44. package/runtime/cpp/lucent/buffer.h +328 -0
  45. package/runtime/cpp/lucent/builtins.cpp +4 -1
  46. package/runtime/cpp/lucent/bytes.h +21 -2
  47. package/runtime/cpp/lucent/callback.cpp +29 -0
  48. package/runtime/cpp/lucent/callback.h +230 -0
  49. package/runtime/cpp/lucent/compute.cpp +370 -0
  50. package/runtime/cpp/lucent/compute.h +455 -0
  51. package/runtime/cpp/lucent/conversions.h +111 -0
  52. package/runtime/cpp/lucent/core.h +16 -5
  53. package/runtime/cpp/lucent/equality.h +107 -28
  54. package/runtime/cpp/lucent/execution.cpp +416 -0
  55. package/runtime/cpp/lucent/execution.h +270 -0
  56. package/runtime/cpp/lucent/extension.cpp +97 -0
  57. package/runtime/cpp/lucent/extension.h +210 -0
  58. package/runtime/cpp/lucent/helpers.h +12 -5
  59. package/runtime/cpp/lucent/items.h +52 -0
  60. package/runtime/cpp/lucent/jserror.cpp +9 -0
  61. package/runtime/cpp/lucent/jserror.h +11 -0
  62. package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
  63. package/runtime/cpp/lucent/jsi/convert.h +128 -59
  64. package/runtime/cpp/lucent/jsi/host.cpp +213 -35
  65. package/runtime/cpp/lucent/jsi/host.h +116 -16
  66. package/runtime/cpp/lucent/json.h +19 -1
  67. package/runtime/cpp/lucent/lifecycle.cpp +260 -0
  68. package/runtime/cpp/lucent/lifecycle.h +124 -0
  69. package/runtime/cpp/lucent/lucent.h +7 -0
  70. package/runtime/cpp/lucent/map.h +41 -7
  71. package/runtime/cpp/lucent/native.cpp +35 -47
  72. package/runtime/cpp/lucent/native.h +116 -26
  73. package/runtime/cpp/lucent/number.cpp +38 -38
  74. package/runtime/cpp/lucent/number.h +33 -0
  75. package/runtime/cpp/lucent/operation.h +65 -0
  76. package/runtime/cpp/lucent/ops.h +14 -6
  77. package/runtime/cpp/lucent/platform/android.cpp +223 -10
  78. package/runtime/cpp/lucent/platform/android.h +227 -4
  79. package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
  80. package/runtime/cpp/lucent/platform/android_requests.h +140 -0
  81. package/runtime/cpp/lucent/platform/compose.h +256 -0
  82. package/runtime/cpp/lucent/platform/ios.h +325 -25
  83. package/runtime/cpp/lucent/platform/ios.mm +1 -14
  84. package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
  85. package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
  86. package/runtime/cpp/lucent/platform/swiftui.h +190 -0
  87. package/runtime/cpp/lucent/presentation.cpp +11 -0
  88. package/runtime/cpp/lucent/presentation.h +109 -0
  89. package/runtime/cpp/lucent/reactive.cpp +406 -0
  90. package/runtime/cpp/lucent/reactive.h +566 -0
  91. package/runtime/cpp/lucent/report.cpp +40 -0
  92. package/runtime/cpp/lucent/report.h +16 -0
  93. package/runtime/cpp/lucent/resource.cpp +83 -0
  94. package/runtime/cpp/lucent/resource.h +75 -0
  95. package/runtime/cpp/lucent/scheduler.cpp +38 -124
  96. package/runtime/cpp/lucent/scheduler.h +102 -83
  97. package/runtime/cpp/lucent/scope.cpp +308 -0
  98. package/runtime/cpp/lucent/scope.h +452 -0
  99. package/runtime/cpp/lucent/sizing.h +263 -0
  100. package/runtime/cpp/lucent/slots.h +231 -0
  101. package/runtime/cpp/lucent/trace.cpp +539 -0
  102. package/runtime/cpp/lucent/trace.h +251 -0
  103. package/runtime/cpp/lucent/transport.cpp +9 -0
  104. package/runtime/cpp/lucent/transport.h +341 -0
  105. package/runtime/cpp/lucent/view.cpp +123 -0
  106. package/runtime/cpp/lucent/view.h +157 -0
  107. package/runtime/cpp/rn/LucentComponentView.h +121 -0
  108. package/runtime/cpp/rn/LucentComponentView.mm +479 -0
  109. package/runtime/cpp/rn/LucentModule.cpp +37 -7
  110. package/runtime/cpp/rn/LucentModule.h +3 -0
  111. package/runtime/cpp/rn/LucentViewChildren.h +80 -0
  112. package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
  113. package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
  114. package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
  115. package/runtime/cpp/rn/LucentViewRequests.h +104 -0
  116. package/runtime/cpp/rn/LucentViewSizing.h +345 -0
  117. package/runtime/cpp/rn/LucentViewSlots.h +242 -0
  118. package/runtime/cpp/rn/LucentViewValues.h +125 -0
  119. package/runtime/cpp/rn/LucentViews.h +440 -0
  120. package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
  121. package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
  122. package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
  123. package/runtime/cpp/third_party/dragonbox/README.md +16 -0
  124. package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
  125. package/runtime/js/index.d.ts +11 -1
  126. package/runtime/js/index.js +84 -8
  127. package/runtime/js/views.d.ts +38 -0
  128. package/runtime/js/views.js +226 -0
  129. package/runtime/native/android/CMakeLists.txt +4 -1
  130. package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
  131. package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
  132. package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
  133. package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
  134. package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
  135. package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
  136. package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
  137. package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
  138. package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
  139. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
  140. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
  141. package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
  142. package/runtime/test/jsi/harness.cpp +9 -0
  143. package/schemas/build.schema.json +96 -1
  144. package/schemas/check.schema.json +56 -1
  145. package/schemas/lucent.schema.json +495 -9
  146. package/schemas/sdk-diff.schema.json +70 -0
  147. package/schemas/sdk-lock.schema.json +67 -0
  148. package/ts-plugin/index.js +85 -4
  149. package/dist/pipeline-CH7-2cxu.js +0 -232
  150. package/dist/project-D8tEet0k.js +0 -150
  151. package/dist/sdk-coverage-BoW_3Efd.js +0 -64
  152. package/dist/src-BD7f1_aZ.js +0 -11493
@@ -0,0 +1,700 @@
1
+ import { C as bindExtensions, F as moduleNamespace, M as platformOf, N as projectFiles, P as usesPlatforms, S as inNativePackage, Z as sdkModuleArtifacts, d as isUpToDate, j as moduleNameOf, l as deferredLibraryGradle, n as compile, p as writeNativePackage, q as extractionCount, s as sdkModule, u as inputsKey, v as resolveNative } from "./src-ChnmWLwn.js";
2
+ import { n as lucentPackages } from "./packages-CSmKeaAh.js";
3
+ import { a as projectHashes, c as sdkImports, i as pendingAndroidModules, l as staleAutolinking, n as mapLucentPaths, o as projectSdk, r as missingAppEntries, s as resolveAndroidDependencies, t as backgroundPrefetch, u as writeLinkedPackage } from "./project-BKNoTEEp.js";
4
+ import { a as lockProblems, c as sdkUsage, d as writeUsage, i as frozenFailure, l as usageReadable, n as USAGE_FILE, o as readUsage, s as sdkUnavailable, t as LOCK_FILE, u as usedModules } from "./sdk-usage-0ZFMAYg1.js";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import { createHash } from "node:crypto";
8
+ const ACTION_KINDS = [
9
+ "reinstall",
10
+ "relink",
11
+ "compile-native",
12
+ "repackage",
13
+ "reload-js"
14
+ ];
15
+ function contentHash(data) {
16
+ return createHash("sha256").update(data).digest("hex").slice(0, 16);
17
+ }
18
+ function fileArtifact(root, file) {
19
+ return {
20
+ key: path.relative(root, path.resolve(file)).split(path.sep).join("/"),
21
+ hash: fs.existsSync(file) ? contentHash(fs.readFileSync(file)) : "missing"
22
+ };
23
+ }
24
+ function byKey(a, b) {
25
+ return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
26
+ }
27
+ var BuildGraph = class {
28
+ nodes = /* @__PURE__ */ new Map();
29
+ timings = {};
30
+ startedAt = {};
31
+ started = Date.now();
32
+ mode;
33
+ constructor(mode) {
34
+ this.mode = mode;
35
+ }
36
+ record(id, kind, status, fields = {}) {
37
+ if (this.nodes.has(id)) throw new Error(`build graph: node ${id} recorded twice`);
38
+ const inputs = [...fields.inputs ?? []].sort(byKey);
39
+ const outputs = [...fields.outputs ?? []].sort(byKey);
40
+ const node = {
41
+ id,
42
+ kind,
43
+ status,
44
+ inputs,
45
+ outputs,
46
+ hash: contentHash(JSON.stringify({
47
+ kind,
48
+ inputs,
49
+ outputs
50
+ })),
51
+ ...fields.detail !== void 0 ? { detail: fields.detail } : {},
52
+ ...fields.log !== void 0 ? { log: fields.log } : {}
53
+ };
54
+ this.nodes.set(id, node);
55
+ if (fields.ms !== void 0) {
56
+ this.timings[id] = fields.ms;
57
+ this.startedAt[id] = Math.max(0, Date.now() - fields.ms - this.started);
58
+ }
59
+ return node;
60
+ }
61
+ get(id) {
62
+ return this.nodes.get(id);
63
+ }
64
+ toRecord(requiredAction, pendingActions = []) {
65
+ const nodes = [...this.nodes.values()];
66
+ return {
67
+ schemaVersion: 1,
68
+ mode: this.mode,
69
+ nodes,
70
+ requiredAction,
71
+ pendingActions,
72
+ timings: { ...this.timings },
73
+ startedAt: { ...this.startedAt }
74
+ };
75
+ }
76
+ };
77
+ function writeBuildRecord(file, record) {
78
+ fs.mkdirSync(path.dirname(file), { recursive: true });
79
+ const tmp = `${file}.${process.pid}.tmp`;
80
+ fs.writeFileSync(tmp, `${JSON.stringify(record, null, 2)}\n`);
81
+ fs.renameSync(tmp, file);
82
+ }
83
+ function requiredAction(next, targets, changedUnits) {
84
+ if (next.podInstall) return {
85
+ kind: "relink",
86
+ targets,
87
+ dependencyChanges: ["native files added or removed"]
88
+ };
89
+ if (next.rebuild) return {
90
+ kind: "compile-native",
91
+ targets,
92
+ changedUnits: [...changedUnits].sort()
93
+ };
94
+ if (next.reload) return { kind: "reload-js" };
95
+ return { kind: "none" };
96
+ }
97
+ //#endregion
98
+ //#region src/cli/changes.ts
99
+ const IGNORED = {
100
+ kind: void 0,
101
+ targets: []
102
+ };
103
+ const OUTPUT_ROLES = [
104
+ [/^manifest\.json$/, () => IGNORED],
105
+ [/^types\//, () => IGNORED],
106
+ [/^js\/_lucent\/identity\.js$/, () => IGNORED],
107
+ [/^js\//, () => ({
108
+ kind: "reload-js",
109
+ targets: "all"
110
+ })],
111
+ [/^cpp\/generated\/(ios|android|host)\//, (m) => ({
112
+ kind: "compile-native",
113
+ targets: [m[1]]
114
+ })],
115
+ [/^cpp\//, () => ({
116
+ kind: "compile-native",
117
+ targets: "all"
118
+ })],
119
+ [/^ios\//, () => ({
120
+ kind: "compile-native",
121
+ targets: ["ios"]
122
+ })],
123
+ [/^android\/src\/main\/java\//, () => ({
124
+ kind: "compile-native",
125
+ targets: ["android"]
126
+ })],
127
+ [/^android\/src\/main\/AndroidManifest\.xml$/, () => ({
128
+ kind: "reinstall",
129
+ targets: ["android"]
130
+ })],
131
+ [/^LucentNative\.podspec$/, () => ({
132
+ kind: "relink",
133
+ targets: ["ios"]
134
+ })],
135
+ [/^android\//, () => ({
136
+ kind: "relink",
137
+ targets: ["android"]
138
+ })]
139
+ ];
140
+ const FIELD_ACTIONS = {
141
+ ios: {
142
+ nativeSources: "compile-native",
143
+ resources: "repackage",
144
+ resourceBundles: "repackage",
145
+ vendoredFrameworks: "relink"
146
+ },
147
+ android: {
148
+ nativeSources: "compile-native",
149
+ resources: "repackage",
150
+ assets: "repackage",
151
+ libraries: "relink",
152
+ nativeLibraries: "relink"
153
+ }
154
+ };
155
+ const APP_CONFIGURATION = ["infoPlist", "entitlements"];
156
+ function packageRoles(file, manifests) {
157
+ const roles = [];
158
+ for (const manifest of manifests) for (const platform of ["ios", "android"]) for (const [field, kind] of Object.entries(FIELD_ACTIONS[platform])) {
159
+ const listed = manifest[platform][field];
160
+ if ((Array.isArray(listed) ? listed : Object.values(listed).flat()).some((p) => file === inNativePackage(p) || file.startsWith(`${inNativePackage(p)}/`))) roles.push({
161
+ kind,
162
+ targets: [platform]
163
+ });
164
+ }
165
+ return roles;
166
+ }
167
+ function rolesOf(file, change) {
168
+ if (file.startsWith("packages/")) {
169
+ const roles = packageRoles(file, [change.manifest, ...change.previous ? [change.previous] : []]);
170
+ return roles.length ? roles : [{
171
+ kind: "relink",
172
+ targets: "all"
173
+ }];
174
+ }
175
+ for (const [pattern, role] of OUTPUT_ROLES) {
176
+ const m = pattern.exec(file);
177
+ if (m) return [role(m)];
178
+ }
179
+ return [{
180
+ kind: "relink",
181
+ targets: "all"
182
+ }];
183
+ }
184
+ const canonical = (v) => JSON.stringify(v, (_, x) => x && typeof x === "object" && !Array.isArray(x) ? Object.fromEntries(Object.entries(x).sort(([a], [b]) => a < b ? -1 : 1)) : x);
185
+ function classifyChanges(change) {
186
+ const actions = /* @__PURE__ */ new Map();
187
+ const need = (kind, targets, file) => {
188
+ const on = (targets === "all" ? change.targets : targets).filter((t) => change.targets.includes(t));
189
+ if (!on.length) return;
190
+ const action = actions.get(kind) ?? {
191
+ targets: /* @__PURE__ */ new Set(),
192
+ files: /* @__PURE__ */ new Set()
193
+ };
194
+ for (const t of on) action.targets.add(t);
195
+ action.files.add(file);
196
+ actions.set(kind, action);
197
+ };
198
+ for (const file of [...change.written, ...change.removed]) {
199
+ if (file === "resolved.json") {
200
+ for (const key of APP_CONFIGURATION) if (canonical(change.manifest.ios[key]) !== canonical(change.previous?.ios[key] ?? {})) need("reinstall", ["ios"], `resolved.json#ios.${key}`);
201
+ continue;
202
+ }
203
+ for (const role of rolesOf(file, change)) if (role.kind) need(role.kind, role.targets, file);
204
+ }
205
+ for (const file of [...change.added, ...change.removed]) for (const role of rolesOf(file, change)) if (role.kind && role.kind !== "reload-js") need("relink", role.targets === "all" ? ["ios"] : role.targets.filter((t) => t === "ios"), file);
206
+ return ACTION_KINDS.filter((kind) => actions.has(kind)).map((kind) => {
207
+ const { targets, files } = actions.get(kind);
208
+ return {
209
+ kind,
210
+ targets: change.targets.filter((t) => targets.has(t)),
211
+ files: [...files].sort()
212
+ };
213
+ });
214
+ }
215
+ const ACTION_TEXT = {
216
+ reinstall: "reinstall the app",
217
+ relink: "relink native dependencies",
218
+ "compile-native": "recompile native code",
219
+ repackage: "repackage resources",
220
+ "reload-js": "reload JavaScript"
221
+ };
222
+ function filesText(files, shown = 2) {
223
+ const more = files.length - shown;
224
+ return `${files.slice(0, shown).join(", ")}${more > 0 ? ` +${more} more` : ""}`;
225
+ }
226
+ //#endregion
227
+ //#region src/cli/pipeline.ts
228
+ const NONE = {
229
+ rebuild: false,
230
+ podInstall: false,
231
+ reload: false
232
+ };
233
+ async function buildProject(root, options, steps, notify) {
234
+ const t0 = Date.now();
235
+ const build = options.mode === "build";
236
+ let files = [];
237
+ let native;
238
+ let nativeInputs = [];
239
+ const graph = new BuildGraph(options.mode);
240
+ let targets = [];
241
+ let changedUnits = [];
242
+ const skipped = [];
243
+ const outcome = (o) => {
244
+ const next = o.next ?? NONE;
245
+ const actions = o.actions ?? [];
246
+ const record = graph.toRecord(requiredAction(next, targets, changedUnits), actions);
247
+ writeBuildRecord(path.join(root, ".lucent/build-record.json"), record);
248
+ return {
249
+ ok: false,
250
+ upToDate: false,
251
+ files,
252
+ modules: moduleSummary(files, void 0),
253
+ diagnostics: [],
254
+ warnings: [],
255
+ next,
256
+ actions,
257
+ nativeInputs,
258
+ record,
259
+ skipped,
260
+ ms: Date.now() - t0,
261
+ ...o
262
+ };
263
+ };
264
+ const tResolve = Date.now();
265
+ try {
266
+ files = projectFiles(root);
267
+ const hashes = projectHashes(root);
268
+ native = resolveNative(lucentPackages(root), { hashes });
269
+ hashes.save();
270
+ nativeInputs = native.read;
271
+ } catch (e) {
272
+ graph.record("resolve", "resolve", "failed", {
273
+ detail: e.message,
274
+ ms: Date.now() - tResolve
275
+ });
276
+ return outcome({ fatal: e.message });
277
+ }
278
+ const superseded = (step) => {
279
+ graph.record(step, step === "generate" ? "generate" : "check", "skipped", { detail: "superseded by a newer change" });
280
+ return outcome({ superseded: true });
281
+ };
282
+ graph.record("resolve", "resolve", "ok", {
283
+ inputs: [{
284
+ key: "native-dependencies",
285
+ hash: contentHash(JSON.stringify(native.manifest))
286
+ }, ...packageInputs(native)],
287
+ ms: Date.now() - tResolve
288
+ });
289
+ if (options.signal?.aborted) return superseded("check");
290
+ let extensions = [];
291
+ if (native.extensions.length) {
292
+ const t = Date.now();
293
+ const inputs = native.extensions.map((e) => ({
294
+ key: `ext/${e.name}`,
295
+ hash: e.hash
296
+ }));
297
+ try {
298
+ extensions = bindExtensions(native.extensions);
299
+ } catch (e) {
300
+ graph.record("extract:extensions", "extract", "failed", {
301
+ inputs,
302
+ detail: e.message,
303
+ ms: Date.now() - t
304
+ });
305
+ return outcome({ fatal: e.message });
306
+ }
307
+ graph.record("extract:extensions", "extract", "ok", {
308
+ inputs,
309
+ ms: Date.now() - t
310
+ });
311
+ }
312
+ const sdk = projectSdk(root, native);
313
+ const outDir = path.resolve(root, options.out ?? ".lucent/native");
314
+ let lock;
315
+ const lockProblem = (detail) => {
316
+ graph.record("resolve:lock", "resolve", "failed", { detail });
317
+ return outcome({ fatal: detail });
318
+ };
319
+ if (options.frozen) {
320
+ try {
321
+ lock = readUsage(path.join(root, LOCK_FILE));
322
+ } catch (e) {
323
+ return lockProblem(e.message);
324
+ }
325
+ if (!lock) return lockProblem(`--frozen needs ${LOCK_FILE}: run lucent sdk lock and commit it`);
326
+ for (const p of lock.targets) {
327
+ if (options.platforms && !options.platforms.includes(p)) continue;
328
+ const why = sdkUnavailable(p, sdk);
329
+ if (why) return lockProblem(`the SDK lock requires ${p}, whose SDK is unavailable: ${why}`);
330
+ }
331
+ }
332
+ const libraryGradle = path.join(outDir, "android/build.gradle");
333
+ const configured = fs.existsSync(libraryGradle) ? fs.readFileSync(libraryGradle, "utf8") : void 0;
334
+ let platforms = options.platforms;
335
+ let deferAndroid = false;
336
+ let deferReason = "its dependencies are resolved by the Gradle build";
337
+ if (build && (!platforms || platforms.includes("android"))) {
338
+ if (writeLinkedPackage(root, outDir, native) && process.env.LUCENT_GRADLE_CLASSPATH) {
339
+ const detail = `this Gradle build read the app's autolinking config before ${path.relative(root, outDir)} existed, so the app would not link Lucent: build again`;
340
+ graph.record("resolve:android", "resolve", "failed", { detail });
341
+ return outcome({ fatal: detail });
342
+ }
343
+ steps.start("android-dependencies", "Android dependencies");
344
+ await steps.flush();
345
+ const t = Date.now();
346
+ const deps = resolveAndroidDependencies(root, files, sdk, native.manifest, !!options.force);
347
+ const label = "Android dependencies";
348
+ graph.record("resolve:android", "resolve", deps.status === "resolved" ? "ok" : deps.status === "deferred" ? "skipped" : deps.status === "failed" ? "failed" : "cached", {
349
+ ...deps.status === "failed" ? { detail: deps.detail } : {},
350
+ ms: Date.now() - t
351
+ });
352
+ if (deps.status === "resolved") steps.finish({
353
+ name: "android-dependencies",
354
+ label,
355
+ status: "ok",
356
+ detail: "resolved with Gradle",
357
+ ms: Date.now() - t
358
+ });
359
+ else if (deps.status === "cached") steps.finish({
360
+ name: "android-dependencies",
361
+ label,
362
+ status: "cached"
363
+ });
364
+ else if (deps.status === "failed") steps.finish({
365
+ name: "android-dependencies",
366
+ label,
367
+ status: "failed",
368
+ detail: deps.detail
369
+ });
370
+ else if (deps.status === "deferred") {
371
+ deferAndroid = true;
372
+ steps.finish({
373
+ name: "android-dependencies",
374
+ label,
375
+ status: "skipped",
376
+ detail: "resolved by the Gradle build"
377
+ });
378
+ notify({
379
+ level: "warn",
380
+ text: "the app's Android dependencies are not resolved yet: skipped Android here; the Gradle build compiles it (its lucentBuild task)"
381
+ });
382
+ }
383
+ }
384
+ if (!deferAndroid && !platforms?.includes("android")) {
385
+ const pending = pendingAndroidModules(root, files, sdk).map((m) => `lucent:android/${m}`);
386
+ if (pending.length) {
387
+ deferAndroid = true;
388
+ deferReason = "the app has no Android project yet";
389
+ notify({
390
+ level: "warn",
391
+ text: `${pending.join(", ")} ${pending.length === 1 ? "is" : "are"} untyped: the app has no Android project to resolve ${pending.length === 1 ? "it" : "them"} yet. Checked after expo prebuild, or once android/ exists${platforms ? "" : "; skipped Android here"}`
392
+ });
393
+ }
394
+ }
395
+ if (!platforms && files.some((f) => platformOf(f) || usesPlatforms(f))) {
396
+ for (const p of ["ios", "android"]) {
397
+ const why = sdkUnavailable(p, sdk);
398
+ if (why) {
399
+ skipped.push({
400
+ platform: p,
401
+ reason: why
402
+ });
403
+ notify({
404
+ level: "warn",
405
+ text: `${why}; skipped ${p === "ios" ? "iOS" : "Android"} (build it with --platforms ${p} once the SDK is installed)`
406
+ });
407
+ } else if (p === "android" && deferAndroid) skipped.push({
408
+ platform: p,
409
+ reason: deferReason
410
+ });
411
+ }
412
+ const installed = ["ios", "android"].filter((p) => !skipped.some((s) => s.platform === p));
413
+ if (!installed.length) {
414
+ const detail = deferAndroid ? `no platform to build here: ${skipped.map((s) => `${s.platform}: ${s.reason}`).join("; ")}` : "no platform SDK is installed";
415
+ graph.record("resolve:platforms", "resolve", "failed", { detail });
416
+ return outcome({ fatal: detail });
417
+ }
418
+ platforms = installed;
419
+ if (build && options.prefetch) backgroundPrefetch(root, files, installed);
420
+ }
421
+ for (const { platform, reason } of skipped) if (lock?.targets.includes(platform)) return lockProblem(`the SDK lock requires ${platform}, which this build skips: ${reason}`);
422
+ if (build && outDir === path.join(root, ".lucent/native")) {
423
+ const mapped = mapLucentPaths(root);
424
+ if (mapped) notify(mapped);
425
+ }
426
+ const modules = moduleSummary(files, platforms);
427
+ const moduleCount = modules.length;
428
+ targets = [...platforms ?? ["ios", "android"]];
429
+ const built = (platforms ?? []).filter((p) => p !== "host");
430
+ const checkInputs = [...files.map((f) => fileArtifact(root, f)), {
431
+ key: "targets",
432
+ hash: contentHash(targets.join(","))
433
+ }];
434
+ const key = inputsKey(files, outDir) + (platforms ? `:${platforms.join(",")}` : "") + `:${createHash("sha256").update(JSON.stringify(native.manifest)).digest("hex").slice(0, 12)}`;
435
+ const checked = path.join(root, ".lucent/check.json");
436
+ const cacheable = !options.force && !lock && usageReadable(path.join(root, ".lucent/sdk-usage.json"));
437
+ if (!build && cacheable) {
438
+ if ((fs.existsSync(checked) ? JSON.parse(fs.readFileSync(checked, "utf8")) : {}).inputs === key) {
439
+ graph.record("check", "check", "cached", { inputs: checkInputs });
440
+ return outcome({
441
+ ok: true,
442
+ upToDate: true,
443
+ modules
444
+ });
445
+ }
446
+ }
447
+ if (build && cacheable && isUpToDate(outDir, key)) {
448
+ graph.record("check", "check", "cached", { inputs: checkInputs });
449
+ graph.record("generate", "generate", "cached", { outputs: packageArtifacts(root, outDir) });
450
+ steps.finish({
451
+ name: "up-to-date",
452
+ label: "Up to date",
453
+ status: "ok",
454
+ detail: `${plural(moduleCount, "module")}, nothing to build`,
455
+ ms: Date.now() - t0
456
+ });
457
+ return outcome({
458
+ ok: true,
459
+ upToDate: true,
460
+ modules
461
+ });
462
+ }
463
+ const imports = sdkImports(files);
464
+ const wanted = ["ios", "android"].flatMap((p) => (platforms ?? []).includes(p) ? imports[p].map((m) => [p, m]) : []);
465
+ if (wanted.length) {
466
+ const names = wanted.map(([, m]) => m);
467
+ const listed = names.length > 3 ? `${names.slice(0, 3).join(" · ")} +${names.length - 3} more` : names.join(" · ");
468
+ steps.start("sdk", `SDK bindings ${listed}`);
469
+ await steps.flush();
470
+ const t = Date.now();
471
+ const before = extractionCount();
472
+ for (const [p, m] of wanted) sdkModule(p, m, sdk);
473
+ const extracted = extractionCount() - before;
474
+ const read = wanted.map(([p, m]) => ({
475
+ key: `${p}/${m}`,
476
+ artifacts: sdkModuleArtifacts(p, m, sdk)
477
+ }));
478
+ const artifacts = new Map(read.flatMap((r) => r.artifacts).map((a) => [a.id, a]));
479
+ const identity = (a) => `${a.id}#${a.contentHash}`;
480
+ graph.record("extract", "extract", extracted ? "ok" : "cached", {
481
+ inputs: [...read.map((r) => ({
482
+ key: r.key,
483
+ hash: contentHash(r.artifacts.map(identity).join("\n"))
484
+ })), ...[...artifacts.values()].map((a) => ({
485
+ key: a.id,
486
+ hash: a.contentHash
487
+ }))],
488
+ detail: listed,
489
+ ms: Date.now() - t
490
+ });
491
+ steps.finish({
492
+ name: "sdk",
493
+ label: "SDK bindings",
494
+ status: extracted ? "ok" : "cached",
495
+ detail: listed,
496
+ ms: extracted ? Date.now() - t : void 0
497
+ });
498
+ }
499
+ if (lock) {
500
+ const locked = Object.keys(lock.modules).filter((k) => built.includes(k.split("/")[0]));
501
+ const problems = lockProblems(lock, usedModules([...wanted.map(([p, m]) => `${p}/${m}`), ...locked], sdk));
502
+ if (problems.length) {
503
+ graph.record("extract:lock", "extract", "failed", { detail: problems.join("\n") });
504
+ return outcome({ fatal: frozenFailure(problems) });
505
+ }
506
+ }
507
+ steps.start("check", `Checking ${plural(moduleCount, "module")}`);
508
+ await steps.flush();
509
+ const tCheck = Date.now();
510
+ const deferred = deferAndroid && !platforms?.includes("android") ? ["android"] : [];
511
+ const result = compile(files, {
512
+ platforms,
513
+ sdk,
514
+ extensions,
515
+ deferred
516
+ });
517
+ const relative = (d) => ({
518
+ ...d,
519
+ file: d.file && path.relative(root, d.file)
520
+ });
521
+ const diagnostics = result.diagnostics.map(relative);
522
+ const warnings = (result.warnings ?? []).map(relative);
523
+ if (!result.ok) {
524
+ graph.record("check", "check", "failed", {
525
+ inputs: checkInputs,
526
+ detail: plural(diagnostics.length, "error"),
527
+ ms: Date.now() - tCheck
528
+ });
529
+ steps.finish({
530
+ name: "check",
531
+ label: `Checked ${plural(moduleCount, "module")}`,
532
+ status: "failed",
533
+ detail: plural(diagnostics.length, "error"),
534
+ ms: Date.now() - tCheck
535
+ });
536
+ return outcome({
537
+ modules,
538
+ diagnostics,
539
+ warnings
540
+ });
541
+ }
542
+ const names = [...result.proxies.keys()];
543
+ const usage = sdkUsage(result.sdkUses ?? [], built, wanted.map(([p, m]) => `${p}/${m}`), sdk);
544
+ writeUsage(path.join(root, USAGE_FILE), usage);
545
+ const unlocked = lock ? lockProblems(lock, usage.modules, usage.symbols) : [];
546
+ if (unlocked.length) {
547
+ graph.record("check", "check", "failed", {
548
+ inputs: checkInputs,
549
+ detail: unlocked.join("\n"),
550
+ ms: Date.now() - tCheck
551
+ });
552
+ steps.finish({
553
+ name: "check",
554
+ label: `Checked ${plural(moduleCount, "module")}`,
555
+ status: "failed",
556
+ detail: `not the SDK symbols ${LOCK_FILE} records`,
557
+ ms: Date.now() - tCheck
558
+ });
559
+ return outcome({
560
+ modules,
561
+ warnings,
562
+ usage,
563
+ fatal: frozenFailure(unlocked)
564
+ });
565
+ }
566
+ graph.record("check", "check", "ok", {
567
+ inputs: checkInputs,
568
+ ms: Date.now() - tCheck
569
+ });
570
+ steps.finish({
571
+ name: "check",
572
+ label: `Checked ${plural(names.length, "module")}`,
573
+ status: "ok",
574
+ ms: Date.now() - tCheck
575
+ });
576
+ if (!build) {
577
+ fs.mkdirSync(path.dirname(checked), { recursive: true });
578
+ fs.writeFileSync(checked, `${JSON.stringify({ inputs: key })}\n`);
579
+ return outcome({
580
+ ok: true,
581
+ modules,
582
+ warnings,
583
+ usage
584
+ });
585
+ }
586
+ if (options.signal?.aborted) return superseded("generate");
587
+ const resolvedFile = path.join(outDir, "resolved.json");
588
+ const previous = fs.existsSync(resolvedFile) ? JSON.parse(fs.readFileSync(resolvedFile, "utf8")) : void 0;
589
+ const tWrite = Date.now();
590
+ const w = writeNativePackage(result, outDir, {
591
+ inputsKey: key,
592
+ native,
593
+ androidDeferred: deferred.includes("android")
594
+ });
595
+ const inPackage = (f) => path.relative(outDir, f).split(path.sep).join("/");
596
+ const written = new Set(w.written.map(inPackage));
597
+ if (written.has("react-native.config.js")) staleAutolinking(root);
598
+ if (process.env.LUCENT_GRADLE_CLASSPATH && configured !== void 0 && fs.readFileSync(libraryGradle, "utf8") !== configured && configured !== deferredLibraryGradle(native)) {
599
+ const detail = `this Gradle build configured the Lucent Android library before its build.gradle changed (${path.relative(root, libraryGradle)}: Kotlin shims, packages' Android needs), so it would build it as it was: build again`;
600
+ graph.record("generate", "generate", "failed", {
601
+ detail,
602
+ ms: Date.now() - tWrite
603
+ });
604
+ return outcome({ fatal: detail });
605
+ }
606
+ graph.record("generate", "generate", "ok", {
607
+ outputs: packageArtifacts(root, outDir),
608
+ detail: `${written.size} written, ${w.removed.length} removed`,
609
+ ms: Date.now() - tWrite
610
+ });
611
+ changedUnits = [...written].filter((f) => f.startsWith("cpp/"));
612
+ const changed = names.filter((n) => [...written].some((f) => f.startsWith("cpp/generated/") && path.basename(f).startsWith(`${moduleNamespace(n)}.`))).length;
613
+ steps.finish({
614
+ name: "generate",
615
+ label: "Generated C++",
616
+ status: "ok",
617
+ detail: changed === names.length ? `${changed} changed` : `${changed} changed, ${names.length - changed} cached`,
618
+ ms: Date.now() - tWrite
619
+ });
620
+ steps.finish({
621
+ name: "package",
622
+ label: "Native package",
623
+ status: "ok",
624
+ detail: path.relative(root, outDir) || "."
625
+ });
626
+ for (const n of missingAppEntries(root, native.manifest)) notify(n);
627
+ const nativeChanged = w.removed.length > 0 || [...written].some((f) => !f.startsWith("js/") && f !== "manifest.json");
628
+ return outcome({
629
+ ok: true,
630
+ modules,
631
+ warnings,
632
+ usage,
633
+ actions: classifyChanges({
634
+ written: [...written],
635
+ added: w.added.map(inPackage),
636
+ removed: w.removed.map(inPackage),
637
+ manifest: native.manifest,
638
+ previous,
639
+ targets
640
+ }),
641
+ next: {
642
+ rebuild: nativeChanged,
643
+ podInstall: w.structureChanged,
644
+ reload: !nativeChanged && [...written].some((f) => f.startsWith("js/"))
645
+ }
646
+ });
647
+ }
648
+ function packageInputs(native) {
649
+ const { ios, android } = native.manifest;
650
+ const listed = [
651
+ ...ios.nativeSources,
652
+ ...ios.resources,
653
+ ...Object.values(ios.resourceBundles).flat(),
654
+ ...ios.vendoredFrameworks,
655
+ ...android.nativeSources,
656
+ ...android.resources,
657
+ ...android.assets,
658
+ ...android.libraries,
659
+ ...android.nativeLibraries
660
+ ];
661
+ return [...new Map(listed.map((p) => [inNativePackage(p), p.hash])).entries()].map(([key, hash]) => ({
662
+ key,
663
+ hash
664
+ }));
665
+ }
666
+ function packageArtifacts(root, outDir) {
667
+ const out = [];
668
+ const walk = (dir) => {
669
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
670
+ const full = path.join(dir, e.name);
671
+ if (e.isDirectory()) walk(full);
672
+ else if (full !== path.join(outDir, "manifest.json")) out.push(fileArtifact(root, full));
673
+ }
674
+ };
675
+ if (fs.existsSync(outDir)) walk(outDir);
676
+ return out;
677
+ }
678
+ function moduleSummary(files, platforms) {
679
+ const built = (platforms ?? ["ios", "android"]).filter((p) => p !== "host");
680
+ const byModule = /* @__PURE__ */ new Map();
681
+ for (const f of files) {
682
+ const set = byModule.get(moduleNameOf(f)) ?? /* @__PURE__ */ new Set();
683
+ const p = platformOf(f);
684
+ if (p) set.add(p);
685
+ else if (usesPlatforms(f)) for (const b of built) set.add(b);
686
+ byModule.set(moduleNameOf(f), set);
687
+ }
688
+ return [...byModule].map(([name, set]) => ({
689
+ name,
690
+ platforms: set.size ? ["ios", "android"].filter((p) => set.has(p)) : ["shared"]
691
+ }));
692
+ }
693
+ function plural(n, word) {
694
+ return `${n} ${word}${n === 1 ? "" : "s"}`;
695
+ }
696
+ function nextText(next) {
697
+ return next.rebuild ? `rebuild the app${next.podInstall ? " (iOS: pod install first)" : ""}` : next.reload ? "reload the app" : "nothing to do: the native package did not change";
698
+ }
699
+ //#endregion
700
+ export { filesText as a, ACTION_TEXT as i, nextText as n, plural as r, buildProject as t };