@lucent-lang/lucent 0.1.2 → 0.1.3

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 (31) hide show
  1. package/bin/lucent.cjs +2 -0
  2. package/dist/{bench-Ec_oPBhN.js → bench-BLZugR80.js} +1 -1
  3. package/dist/{build-B2E9SkG_.js → build-C1Uct_mH.js} +1 -1
  4. package/dist/{build-check-DYWM9LHo.js → build-check-hlYTmPRd.js} +1 -1
  5. package/dist/{check-DzsBZmBa.js → check-BksBP--n.js} +1 -1
  6. package/dist/cli.js +15 -11
  7. package/dist/{codes-D-6LxUqD.js → codes-B6AgFgAq.js} +24 -2
  8. package/dist/compiler.js +3 -3
  9. package/dist/{dashboard--hlNXMUi.js → dashboard-CzOJghrS.js} +1 -1
  10. package/dist/{dev-ClJalHD4.js → dev-CtaNF3sP.js} +3 -3
  11. package/dist/{explain-Byit07F-.js → explain-ImANcx5a.js} +1 -1
  12. package/dist/{pipeline-rDlFUUa1.js → pipeline-DXFTV3Mg.js} +21 -7
  13. package/dist/{project-MN6cwMwO.js → project-Bp9I-fLP.js} +254 -5
  14. package/dist/{sdk-coverage-DL81IAi9.js → sdk-coverage-BpbKBG_O.js} +33 -4
  15. package/dist/{sdk-diff-DbPQmOSD.js → sdk-diff-BueKDJQB.js} +3 -3
  16. package/dist/{sdk-lock-BWHZdSu-.js → sdk-lock-CKLnZ0Wl.js} +2 -2
  17. package/dist/{sdk-prefetch-D-MAqtZs.js → sdk-prefetch-rk6MuMe6.js} +2 -2
  18. package/dist/{sdk-search-BDercg36.js → sdk-search-U_2M81lq.js} +2 -2
  19. package/dist/{sdk-show-DEdKobD7.js → sdk-show-DG6H40fp.js} +2 -2
  20. package/dist/{sdk-usage-D_ly-uLw.js → sdk-usage-Cc7u1i0L.js} +1 -1
  21. package/dist/{src-Bzpv4-ox.js → src-DMbXwPlK.js} +2156 -522
  22. package/lib/sdk/android.d.ts +12 -0
  23. package/lib/sdk/compose.d.ts +17 -4
  24. package/lib/sdk/ui.d.ts +21 -0
  25. package/package.json +4 -4
  26. package/runtime/cpp/lucent/platform/android.cpp +152 -16
  27. package/runtime/cpp/lucent/platform/android.h +79 -2
  28. package/runtime/cpp/lucent/platform/ios.h +7 -0
  29. package/runtime/cpp/lucent/platform/ios_ui.h +20 -0
  30. package/runtime/cpp/rn/LucentComponentView.mm +12 -0
  31. package/runtime/cpp/rn/LucentViewSizing.h +9 -1
package/bin/lucent.cjs CHANGED
@@ -13,4 +13,6 @@ if (!fs.existsSync(dist)) {
13
13
  );
14
14
  process.exit(1);
15
15
  }
16
+ // Node keeps the compiled code of the CLI and TypeScript on disk: each run starts about 0.1 s sooner.
17
+ require("node:module").enableCompileCache?.();
16
18
  import(pathToFileURL(dist).href);
@@ -1,4 +1,4 @@
1
- import { E as identityScript, N as projectFiles, O as coreJsPath, f as runtimeDir, j as moduleNameOf, n as compile } from "./src-Bzpv4-ox.js";
1
+ import { D as identityScript, M as moduleNameOf, P as projectFiles, f as runtimeDir, k as coreJsPath, n as compile } from "./src-DMbXwPlK.js";
2
2
  import { i as table } from "./format-D9fnIMEx.js";
3
3
  import { t as renderDiagnostic } from "./diagnostic-BvwLZQS1.js";
4
4
  import fs from "node:fs";
@@ -1,4 +1,4 @@
1
- import { t as buildOrCheck } from "./build-check-DYWM9LHo.js";
1
+ import { t as buildOrCheck } from "./build-check-hlYTmPRd.js";
2
2
  //#region src/cli/commands/build.ts
3
3
  function run(invocation) {
4
4
  return buildOrCheck("build", invocation);
@@ -1,7 +1,7 @@
1
1
  import { n as version } from "./version-bqpIO5sP.js";
2
2
  import { i as table, n as duration } from "./format-D9fnIMEx.js";
3
3
  import { t as renderDiagnostic } from "./diagnostic-BvwLZQS1.js";
4
- import { a as filesText, i as ACTION_TEXT, n as nextText, r as plural, t as buildProject } from "./pipeline-rDlFUUa1.js";
4
+ import { a as filesText, i as ACTION_TEXT, n as nextText, r as plural, t as buildProject } from "./pipeline-DXFTV3Mg.js";
5
5
  import { t as plainSteps } from "./steps--_h-FFS7.js";
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
@@ -1,4 +1,4 @@
1
- import { t as buildOrCheck } from "./build-check-DYWM9LHo.js";
1
+ import { t as buildOrCheck } from "./build-check-hlYTmPRd.js";
2
2
  //#region src/cli/commands/check.ts
3
3
  function run(invocation) {
4
4
  return buildOrCheck("check", invocation);
package/dist/cli.js CHANGED
@@ -115,7 +115,7 @@ const commands = [
115
115
  description: "Fail unless the SDKs and SDK symbols are the ones lucent-sdk.lock.json records, with every target it lists (CI, releases)"
116
116
  }
117
117
  ],
118
- load: () => import("./build-B2E9SkG_.js")
118
+ load: () => import("./build-C1Uct_mH.js")
119
119
  },
120
120
  {
121
121
  name: "check",
@@ -124,7 +124,7 @@ const commands = [
124
124
  name: "frozen",
125
125
  description: "Fail unless the SDKs and SDK symbols are the ones lucent-sdk.lock.json records, with every target it lists (CI, releases)"
126
126
  }],
127
- load: () => import("./check-DzsBZmBa.js")
127
+ load: () => import("./check-BksBP--n.js")
128
128
  },
129
129
  {
130
130
  name: "dev",
@@ -133,7 +133,7 @@ const commands = [
133
133
  name: "compact",
134
134
  description: "One line per build instead of the dashboard (Metro runs it so)"
135
135
  }],
136
- load: () => import("./dev-ClJalHD4.js")
136
+ load: () => import("./dev-CtaNF3sP.js")
137
137
  },
138
138
  {
139
139
  name: "doctor",
@@ -173,13 +173,13 @@ const commands = [
173
173
  name: "explain",
174
174
  summary: "What a LUCENT diagnostic code means, and how to fix it (every code without one)",
175
175
  flags: [],
176
- load: () => import("./explain-Byit07F-.js")
176
+ load: () => import("./explain-ImANcx5a.js")
177
177
  },
178
178
  {
179
179
  name: "bench",
180
180
  summary: "Time the cases of your *.bench.ts files natively and as JavaScript, and show the speedup (needs a desktop Hermes)",
181
181
  flags: [],
182
- load: () => import("./bench-Ec_oPBhN.js")
182
+ load: () => import("./bench-BLZugR80.js")
183
183
  },
184
184
  {
185
185
  name: "trace",
@@ -208,13 +208,13 @@ const commands = [
208
208
  name: "sdk search",
209
209
  summary: "Find SDK classes and members by name, with the import to copy (your imports and the SDK cache)",
210
210
  flags: [],
211
- load: () => import("./sdk-search-BDercg36.js")
211
+ load: () => import("./sdk-search-U_2M81lq.js")
212
212
  },
213
213
  {
214
214
  name: "sdk show",
215
215
  summary: "Print the declaration Lucent code sees for an SDK type or member: android.os.Vibrator, UIKit.UIDevice.current",
216
216
  flags: [],
217
- load: () => import("./sdk-show-DEdKobD7.js")
217
+ load: () => import("./sdk-show-DG6H40fp.js")
218
218
  },
219
219
  {
220
220
  name: "sdk prefetch",
@@ -237,7 +237,7 @@ const commands = [
237
237
  description: "Every module of every installed SDK"
238
238
  }
239
239
  ],
240
- load: () => import("./sdk-prefetch-D-MAqtZs.js")
240
+ load: () => import("./sdk-prefetch-rk6MuMe6.js")
241
241
  },
242
242
  {
243
243
  name: "sdk lock",
@@ -247,7 +247,7 @@ const commands = [
247
247
  value: "list",
248
248
  description: "Targets to record: ios, android (default: every platform the project has code for, each needing its SDK)"
249
249
  }],
250
- load: () => import("./sdk-lock-BWHZdSu-.js")
250
+ load: () => import("./sdk-lock-CKLnZ0Wl.js")
251
251
  },
252
252
  {
253
253
  name: "sdk diff",
@@ -256,7 +256,7 @@ const commands = [
256
256
  name: "all",
257
257
  description: "Also the other members of the modules the project uses (the locked schemas must be in this machine's SDK cache)"
258
258
  }],
259
- load: () => import("./sdk-diff-DbPQmOSD.js")
259
+ load: () => import("./sdk-diff-BueKDJQB.js")
260
260
  },
261
261
  {
262
262
  name: "sdk coverage",
@@ -285,9 +285,13 @@ const commands = [
285
285
  {
286
286
  name: "members",
287
287
  description: "List every member with its stage, key and reason"
288
+ },
289
+ {
290
+ name: "views",
291
+ description: "With views on (LUCENT_VIEWS=fabric), list each view class's JSX attributes by rule"
288
292
  }
289
293
  ],
290
- load: () => import("./sdk-coverage-DL81IAi9.js")
294
+ load: () => import("./sdk-coverage-BpbKBG_O.js")
291
295
  }
292
296
  ];
293
297
  //#endregion
@@ -36,6 +36,7 @@ const Codes = {
36
36
  ComponentMainThread: "LUCENT3022",
37
37
  ComponentPlatforms: "LUCENT3023",
38
38
  ToolkitBody: "LUCENT3024",
39
+ NativeViewJsx: "LUCENT3025",
39
40
  BorrowEscape: "LUCENT3030",
40
41
  UseAfterMove: "LUCENT3031",
41
42
  TypeScript: "LUCENT9001"
@@ -58,6 +59,11 @@ const TOOLKIT_TITLE = {
58
59
  "title.lucent.tsx": "import type { TextView } from \"lucent:android/android.widget\";\nimport type { View } from \"lucent:swiftui\";\nexport declare function Title(props: { title: string }): View | TextView;\n",
59
60
  "title.android.lucent.tsx": "import { appContext } from \"lucent:android\";\nimport { TextView } from \"lucent:android/android.widget\";\nimport { effect } from \"lucent:ui\";\nexport function Title(props: { title: string }): TextView {\n const text = new TextView(appContext());\n effect(() => text.setText(props.title));\n return text;\n}\n"
60
61
  };
62
+ const NATIVE_TITLE = {
63
+ "package.json": "{ \"name\": \"example-app\" }\n",
64
+ "title.lucent.tsx": "import type { View } from \"lucent:android/android.view\";\nimport type { UIView } from \"lucent:ios/UIKit\";\nexport declare function Title(props: { title: string }): UIView | View;\n",
65
+ "title.android.lucent.tsx": "import type { View } from \"lucent:android/android.view\";\nimport { TextView } from \"lucent:android/android.widget\";\nexport function Title(props: { title: string }): View {\n return <TextView text={props.title} />;\n}\n"
66
+ };
61
67
  const Explanations = {
62
68
  LUCENT1001: {
63
69
  title: "Syntax outside the subset",
@@ -151,7 +157,7 @@ const Explanations = {
151
157
  LUCENT2002: {
152
158
  title: "Type without a native representation",
153
159
  summary: "A type with no native representation: intersections, `symbol`, `object`, `WeakMap`, `Intl`, or an index signature mixed with properties.",
154
- details: "Each type maps to one native representation. An intersection can combine unrelated layouts, `symbol` is not implemented yet, and `object` says nothing about the layout.",
160
+ details: "Each type maps to one native representation. An intersection can combine unrelated layouts, `symbol` is not implemented yet, and `object` says nothing about the layout. A native member whose types cannot cross yet is named with its symbol and artifact: wrap it in Swift or Kotlin of your own.",
155
161
  fix: "spell the combined type out as one object type, or use a concrete type",
156
162
  wrong: ex("type Named = { name: string };\ntype Aged = { age: number };\nexport function label(p: Named & Aged): string {\n return `${p.name} (${p.age})`;\n}\n"),
157
163
  right: ex("type Person = { name: string; age: number };\nexport function label(p: Person): string {\n return `${p.name} (${p.age})`;\n}\n")
@@ -384,6 +390,22 @@ const Explanations = {
384
390
  sdk: "ios",
385
391
  views: true
386
392
  },
393
+ LUCENT3025: {
394
+ title: "Native view JSX that cannot be compiled",
395
+ summary: "JSX of UIKit or Android views that Lucent cannot make: a view, attribute or child its declarations do not provide for.",
396
+ details: "A component can return its platform's views as JSX, declared as returning UIView or View (internal, under LUCENT_VIEWS=fabric). A tag takes what its class's declarations provide: writable properties, setters, control or listener events, and children where it inserts views at an index. Each attribute is kept up to date like an effect, from when the view mounts.",
397
+ fix: "write each attribute on its element, and set what the declarations do not provide for in setup code",
398
+ wrong: {
399
+ ...NATIVE_TITLE,
400
+ "title.ios.lucent.tsx": "import { UILabel, type UIView } from \"lucent:ios/UIKit\";\nexport function Title(props: { title: string }): UIView {\n const attributes = { text: props.title };\n return <UILabel {...attributes} />;\n}\n"
401
+ },
402
+ right: {
403
+ ...NATIVE_TITLE,
404
+ "title.ios.lucent.tsx": "import { UILabel, type UIView } from \"lucent:ios/UIKit\";\nexport function Title(props: { title: string }): UIView {\n return <UILabel text={props.title} />;\n}\n"
405
+ },
406
+ sdk: "ios",
407
+ views: true
408
+ },
387
409
  LUCENT3030: {
388
410
  title: "Borrowed bytes that outlive their borrow",
389
411
  summary: "A span that withRead or withWrite lends escapes its callback: returned, stored, kept by a closure or callee, or held across await.",
@@ -403,7 +425,7 @@ const Explanations = {
403
425
  LUCENT9001: {
404
426
  title: "TypeScript error",
405
427
  summary: "A TypeScript error. Lucent stops at type errors, because it compiles from the checker's types.",
406
- details: "Lucent compiles only programs that type-check: every value's native type comes from the TypeScript checker. The message is TypeScript's own.",
428
+ details: "Lucent compiles only programs that type-check: every value's native type comes from the TypeScript checker. The message is TypeScript's own. When a native library's type lacks a member, Lucent adds which module and installed version declare it, or that its iOS module needs importing.",
407
429
  fix: "fix the type error; your editor shows the same message",
408
430
  wrong: ex("export function double(n: number): number {\n return n + \"\";\n}\n"),
409
431
  right: ex("export function double(n: number): number {\n return n * 2;\n}\n")
package/dist/compiler.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as Explanations, r as docsUrl, t as Codes } from "./codes-D-6LxUqD.js";
2
- import { A as findLucentFiles, C as bindExtensions, D as LUCENT_EXTENSION, E as identityScript, F as moduleNamespace, G as cachedModules, I as extensionDts, J as forgetLoadedSdks, L as sdkDts, M as platformOf, N as projectFiles, O as coreJsPath, P as usesPlatforms, Q as sdkNames, R as coverage, S as inNativePackage, T as RUNTIME_ABI, W as podsSearchPaths, X as sdkAvailable, Y as prefetch, _ as VIEW_CONTRACT_VERSION, a as report, b as PACKAGE_FIELDS, c as sdkModules, d as isUpToDate, f as runtimeDir, g as libraryBuildGradle, h as toolkitsFrom, i as projectExtensions, j as moduleNameOf, k as coreTypesPath, l as deferredLibraryGradle, m as jsxToolkits, n as compile, o as sdkDeclarations, p as writeNativePackage, q as extractionCount, r as compileDirectory, s as sdkModule, t as checkSources, tt as formatDiagnostic, u as inputsKey, v as resolveNative, w as forgetExtensionHeaders, x as fileHashes, y as EXTENSION_FIELDS } from "./src-Bzpv4-ox.js";
1
+ import { n as Explanations, r as docsUrl, t as Codes } from "./codes-B6AgFgAq.js";
2
+ import { $ as sdkNames, A as coreTypesPath, C as inNativePackage, D as identityScript, E as RUNTIME_ABI, F as usesPlatforms, G as podsSearchPaths, I as moduleNamespace, J as extractionCount, K as cachedModules, L as extensionDts, M as moduleNameOf, N as platformOf, O as LUCENT_EXTENSION, P as projectFiles, R as sdkDts, S as fileHashes, T as forgetExtensionHeaders, X as prefetch, Y as forgetLoadedSdks, Z as sdkAvailable, _ as libraryBuildGradle, a as report, b as EXTENSION_FIELDS, c as sdkModules, d as isUpToDate, f as runtimeDir, g as toolkitsFrom, h as jsxToolkits, i as projectExtensions, it as formatDiagnostic, j as findLucentFiles, k as coreJsPath, l as deferredLibraryGradle, m as viewCoverage, n as compile, o as sdkDeclarations, p as writeNativePackage, r as compileDirectory, s as sdkModule, t as checkSources, u as inputsKey, v as VIEW_CONTRACT_VERSION, w as bindExtensions, x as PACKAGE_FIELDS, y as resolveNative, z as coverage } from "./src-DMbXwPlK.js";
3
3
  import { i as satisfies, n as lucentPackages, r as lucentVersion } from "./packages-CSmKeaAh.js";
4
- export { Codes, EXTENSION_FIELDS, Explanations, LUCENT_EXTENSION, PACKAGE_FIELDS, RUNTIME_ABI, VIEW_CONTRACT_VERSION, bindExtensions, cachedModules, checkSources, compile, compileDirectory, coreJsPath, coreTypesPath, deferredLibraryGradle, docsUrl, extensionDts, extractionCount, fileHashes, findLucentFiles, forgetExtensionHeaders, forgetLoadedSdks, formatDiagnostic, identityScript, inNativePackage, inputsKey, isUpToDate, jsxToolkits, libraryBuildGradle, lucentPackages, lucentVersion, moduleNameOf, moduleNamespace, platformOf, podsSearchPaths, prefetch as prefetchSdk, projectExtensions, projectFiles, report, resolveNative, runtimeDir, satisfies, sdkAvailable, coverage as sdkCoverage, sdkDeclarations, sdkDts, sdkModule, sdkModules, sdkNames, toolkitsFrom, usesPlatforms, writeNativePackage };
4
+ export { Codes, EXTENSION_FIELDS, Explanations, LUCENT_EXTENSION, PACKAGE_FIELDS, RUNTIME_ABI, VIEW_CONTRACT_VERSION, bindExtensions, cachedModules, checkSources, compile, compileDirectory, coreJsPath, coreTypesPath, deferredLibraryGradle, docsUrl, extensionDts, extractionCount, fileHashes, findLucentFiles, forgetExtensionHeaders, forgetLoadedSdks, formatDiagnostic, identityScript, inNativePackage, inputsKey, isUpToDate, jsxToolkits, libraryBuildGradle, lucentPackages, lucentVersion, moduleNameOf, moduleNamespace, platformOf, podsSearchPaths, prefetch as prefetchSdk, projectExtensions, projectFiles, report, resolveNative, runtimeDir, satisfies, sdkAvailable, coverage as sdkCoverage, sdkDeclarations, sdkDts, sdkModule, sdkModules, sdkNames, toolkitsFrom, usesPlatforms, viewCoverage, writeNativePackage };
@@ -1,5 +1,5 @@
1
1
  import { i as table, n as duration, t as codeFrame } from "./format-D9fnIMEx.js";
2
- import { i as ACTION_TEXT, n as nextText } from "./pipeline-rDlFUUa1.js";
2
+ import { i as ACTION_TEXT, n as nextText } from "./pipeline-DXFTV3Mg.js";
3
3
  import { Box, Text, render, useApp, useInput } from "ink";
4
4
  import { useState, useSyncExternalStore } from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,8 +1,8 @@
1
1
  import { t as createTheme } from "./theme-uv5bc0aK.js";
2
2
  import { n as version } from "./version-bqpIO5sP.js";
3
- import { D as LUCENT_EXTENSION, M as platformOf, j as moduleNameOf } from "./src-Bzpv4-ox.js";
3
+ import { M as moduleNameOf, N as platformOf, O as LUCENT_EXTENSION } from "./src-DMbXwPlK.js";
4
4
  import { n as lucentPackages } from "./packages-CSmKeaAh.js";
5
- import { i as ACTION_TEXT, n as nextText, r as plural, t as buildProject } from "./pipeline-rDlFUUa1.js";
5
+ import { i as ACTION_TEXT, n as nextText, r as plural, t as buildProject } from "./pipeline-DXFTV3Mg.js";
6
6
  import { t as plainSteps } from "./steps--_h-FFS7.js";
7
7
  import { n as systemProbe, t as diagnose } from "./doctor-CTAX3euc.js";
8
8
  import fs from "node:fs";
@@ -226,7 +226,7 @@ async function run({ root, flags, out }) {
226
226
  session.stop();
227
227
  return 0;
228
228
  }
229
- const { dashboard } = await import("./dashboard--hlNXMUi.js");
229
+ const { dashboard } = await import("./dashboard-CzOJghrS.js");
230
230
  await dashboard({
231
231
  session,
232
232
  theme: out.theme,
@@ -1,4 +1,4 @@
1
- import { n as Explanations, r as docsUrl } from "./codes-D-6LxUqD.js";
1
+ import { n as Explanations, r as docsUrl } from "./codes-B6AgFgAq.js";
2
2
  import { i as table, r as link } from "./format-D9fnIMEx.js";
3
3
  //#region src/cli/commands/explain.ts
4
4
  function run({ positionals, out }) {
@@ -1,7 +1,7 @@
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-Bzpv4-ox.js";
1
+ import { C as inNativePackage, F as usesPlatforms, I as moduleNamespace, J as extractionCount, M as moduleNameOf, N as platformOf, P as projectFiles, Q as sdkModuleArtifacts, d as isUpToDate, l as deferredLibraryGradle, n as compile, p as writeNativePackage, s as sdkModule, u as inputsKey, w as bindExtensions, y as resolveNative } from "./src-DMbXwPlK.js";
2
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-MN6cwMwO.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-D_ly-uLw.js";
3
+ import { a as pendingAndroidModules, c as resolveAndroidDependencies, d as writeLinkedPackage, i as missingAppEntries, l as sdkImports, n as hasAndroidProject, o as projectHashes, r as mapLucentPaths, s as projectSdk, t as backgroundPrefetch, u as staleAutolinking } from "./project-Bp9I-fLP.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-Cc7u1i0L.js";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
7
  import { createHash } from "node:crypto";
@@ -382,13 +382,23 @@ async function buildProject(root, options, steps, notify) {
382
382
  }
383
383
  }
384
384
  if (!deferAndroid && !platforms?.includes("android")) {
385
- const pending = pendingAndroidModules(root, files, sdk).map((m) => `lucent:android/${m}`);
386
- if (pending.length) {
385
+ const leftOut = !!platforms;
386
+ const pending = pendingAndroidModules(root, files, sdk, leftOut).map((m) => `lucent:android/${m}`);
387
+ const subject = `${pending.join(", ")} ${pending.length === 1 ? "is" : "are"} untyped`;
388
+ const them = pending.length === 1 ? "it" : "them";
389
+ if (pending.length && leftOut && hasAndroidProject(root)) {
390
+ deferAndroid = true;
391
+ deferReason = "its dependencies are resolved by the Android build";
392
+ notify({
393
+ level: "warn",
394
+ text: `${subject}: this build leaves Android out, and the Android build (lucent build --platforms android, or the app's Gradle build) resolves ${them}`
395
+ });
396
+ } else if (pending.length) {
387
397
  deferAndroid = true;
388
398
  deferReason = "the app has no Android project yet";
389
399
  notify({
390
400
  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"}`
401
+ text: `${subject}: the app has no Android project to resolve ${them} yet. Checked after expo prebuild, or once android/ exists${platforms ? "" : "; skipped Android here"}`
392
402
  });
393
403
  }
394
404
  }
@@ -590,7 +600,11 @@ async function buildProject(root, options, steps, notify) {
590
600
  const w = writeNativePackage(result, outDir, {
591
601
  inputsKey: key,
592
602
  native,
593
- androidDeferred: deferred.includes("android")
603
+ androidDeferred: deferred.includes("android"),
604
+ app: {
605
+ ...sdk.ios?.deploymentTarget ? { deploymentTarget: sdk.ios.deploymentTarget } : {},
606
+ swiftPackages: sdk.ios?.swiftPackages?.packages ?? []
607
+ }
594
608
  });
595
609
  const inPackage = (f) => path.relative(outDir, f).split(path.sep).join("/");
596
610
  const written = new Set(w.written.map(inPackage));
@@ -1,5 +1,5 @@
1
1
  import { t as packageFile } from "./version-bqpIO5sP.js";
2
- import { J as forgetLoadedSdks, W as podsSearchPaths, c as sdkModules, f as runtimeDir, g as libraryBuildGradle, v as resolveNative, x as fileHashes } from "./src-Bzpv4-ox.js";
2
+ import { G as podsSearchPaths, S as fileHashes, Y as forgetLoadedSdks, _ as libraryBuildGradle, c as sdkModules, f as runtimeDir, nt as cacheRoot, rt as hash, y as resolveNative } from "./src-DMbXwPlK.js";
3
3
  import { n as lucentPackages } from "./packages-CSmKeaAh.js";
4
4
  import { t as withLucentPaths } from "./tsconfig-S4uyLNzf.js";
5
5
  import fs from "node:fs";
@@ -7,6 +7,246 @@ import path from "node:path";
7
7
  import os from "node:os";
8
8
  import { createHash } from "node:crypto";
9
9
  import { spawn, spawnSync } from "node:child_process";
10
+ //#region ../bindgen/src/xcode.ts
11
+ function xcodeApp(iosDir) {
12
+ const name = (fs.existsSync(iosDir) ? fs.readdirSync(iosDir) : []).filter((f) => f.endsWith(".xcodeproj") && f !== "Pods.xcodeproj").sort()[0];
13
+ if (!name) return void 0;
14
+ const project = path.join(iosDir, name);
15
+ const file = path.join(project, "project.pbxproj");
16
+ if (!fs.existsSync(file)) return void 0;
17
+ const root = parsePlist(fs.readFileSync(file, "utf8"));
18
+ const objects = root.objects ?? {};
19
+ const object = (id) => typeof id === "string" ? objects[id] : void 0;
20
+ const list = (v) => Array.isArray(v) ? v : [];
21
+ const projectObject = object(root.rootObject);
22
+ const app = list(projectObject?.targets).map(object).find((t) => t?.productType === "com.apple.product-type.application");
23
+ const deploymentOf = (owner) => {
24
+ const configs = list(object(owner?.buildConfigurationList)?.buildConfigurations).map(object).sort((a, b) => Number(b?.name === "Debug") - Number(a?.name === "Debug"));
25
+ for (const c of configs) {
26
+ const value = (c?.buildSettings)?.IPHONEOS_DEPLOYMENT_TARGET;
27
+ if (typeof value === "string") return value;
28
+ }
29
+ };
30
+ const deploymentTarget = deploymentOf(app) ?? deploymentOf(projectObject);
31
+ const referenced = new Set(list(projectObject?.packageReferences).map(object).map((r) => r?.repositoryURL).filter((url) => typeof url === "string").map(identityOf));
32
+ const resolved = [path.join(iosDir, name.replace(/\.xcodeproj$/, ".xcworkspace")), path.join(project, "project.xcworkspace")].map((w) => path.join(w, "xcshareddata/swiftpm/Package.resolved")).find((f) => fs.existsSync(f));
33
+ const packages = (resolved ? pinsOf(resolved) : []).filter((p) => referenced.has(p.identity));
34
+ return {
35
+ project,
36
+ ...deploymentTarget ? { deploymentTarget } : {},
37
+ packages,
38
+ ...resolved ? { resolved } : {}
39
+ };
40
+ }
41
+ function identityOf(location) {
42
+ return path.basename(location.replace(/\/+$/, "")).replace(/\.git$/, "").toLowerCase();
43
+ }
44
+ function pinsOf(resolved) {
45
+ const json = JSON.parse(fs.readFileSync(resolved, "utf8"));
46
+ return (json.pins ?? json.object?.pins?.map((p) => ({
47
+ location: p.repositoryURL,
48
+ state: p.state
49
+ })) ?? []).flatMap((p) => {
50
+ const location = p.location;
51
+ const revision = p.state?.revision;
52
+ if (!location || !revision) return [];
53
+ const version = p.state?.version;
54
+ return [{
55
+ identity: "identity" in p && p.identity || identityOf(location),
56
+ location,
57
+ ...version ? { version } : {},
58
+ revision
59
+ }];
60
+ });
61
+ }
62
+ function parsePlist(text) {
63
+ let i = 0;
64
+ const skip = () => {
65
+ for (;;) {
66
+ while (i < text.length && /\s/.test(text[i])) i++;
67
+ if (text.startsWith("//", i)) i = text.indexOf("\n", i) < 0 ? text.length : text.indexOf("\n", i);
68
+ else if (text.startsWith("/*", i)) i = text.indexOf("*/", i) + 2;
69
+ else return;
70
+ }
71
+ };
72
+ const expect = (c) => {
73
+ skip();
74
+ if (text[i] !== c) throw new Error(`project.pbxproj: expected ${c} at ${i}`);
75
+ i++;
76
+ };
77
+ const string = () => {
78
+ skip();
79
+ if (text[i] === "\"") {
80
+ let out = "";
81
+ for (i++; text[i] !== "\""; i++) {
82
+ if (i >= text.length) throw new Error("project.pbxproj: an unterminated string");
83
+ if (text[i] === "\\") {
84
+ const c = text[++i];
85
+ out += c === "n" ? "\n" : c === "t" ? " " : c;
86
+ } else out += text[i];
87
+ }
88
+ i++;
89
+ return out;
90
+ }
91
+ const start = i;
92
+ while (i < text.length && /[\w$./:-]/.test(text[i])) i++;
93
+ if (start === i) throw new Error(`project.pbxproj: a value expected at ${i}`);
94
+ return text.slice(start, i);
95
+ };
96
+ const value = () => {
97
+ skip();
98
+ if (text[i] === "{") {
99
+ i++;
100
+ const dict = {};
101
+ for (skip(); text[i] !== "}"; skip()) {
102
+ const key = string();
103
+ expect("=");
104
+ dict[key] = value();
105
+ expect(";");
106
+ }
107
+ i++;
108
+ return dict;
109
+ }
110
+ if (text[i] === "(") {
111
+ i++;
112
+ const array = [];
113
+ for (skip(); text[i] !== ")"; skip()) {
114
+ array.push(value());
115
+ skip();
116
+ if (text[i] === ",") i++;
117
+ }
118
+ i++;
119
+ return array;
120
+ }
121
+ return string();
122
+ };
123
+ return value();
124
+ }
125
+ const pinName = (p) => `${p.identity}@${p.version ?? p.revision.slice(0, 12)}`;
126
+ function swiftPackages(app, opts = {}) {
127
+ const target = app.deploymentTarget ?? "15.1";
128
+ const pins = app.resolved ? pinsOf(app.resolved) : [];
129
+ const xcode = run("xcodebuild", ["-version"], process.cwd());
130
+ const out = {
131
+ modules: [],
132
+ packages: [],
133
+ ...app.resolved ? { resolved: app.resolved } : {},
134
+ failures: []
135
+ };
136
+ if (!app.packages.length) return out;
137
+ if (!xcode.ok) {
138
+ out.failures.push(`xcodebuild did not run: ${xcode.output}`);
139
+ return out;
140
+ }
141
+ for (const pin of app.packages) {
142
+ const key = hash([
143
+ pin.identity,
144
+ pin.revision,
145
+ target,
146
+ xcode.output
147
+ ]);
148
+ const dir = path.join(cacheRoot(opts.cacheDir), "spm", `${pin.identity}-${key}`);
149
+ const done = path.join(dir, "modules.json");
150
+ try {
151
+ if (!fs.existsSync(done)) build(pin, dir, target, app.resolved);
152
+ const built = JSON.parse(fs.readFileSync(done, "utf8"));
153
+ out.packages.push({
154
+ ...pin,
155
+ products: built.products
156
+ });
157
+ out.modules.push(...built.modules);
158
+ } catch (e) {
159
+ out.failures.push(`${pinName(pin)}: ${e.message}`);
160
+ }
161
+ }
162
+ for (const m of out.modules) {
163
+ const owner = pins.find((p) => p.identity === m.package.slice(0, m.package.lastIndexOf("@")));
164
+ if (owner) m.package = pinName(owner);
165
+ else if (m.package.endsWith("@")) m.package += "unpinned";
166
+ }
167
+ return out;
168
+ }
169
+ function build(pin, dir, target, resolved) {
170
+ const src = path.join(dir, "src");
171
+ const derived = path.join(dir, "build");
172
+ fs.rmSync(dir, {
173
+ recursive: true,
174
+ force: true
175
+ });
176
+ fs.mkdirSync(dir, { recursive: true });
177
+ const clone = run("git", [
178
+ "clone",
179
+ "--quiet",
180
+ pin.location,
181
+ src
182
+ ], dir);
183
+ if (!clone.ok) throw new Error(`git clone ${pin.location} failed: ${clone.output}`);
184
+ const checkout = run("git", [
185
+ "-c",
186
+ "advice.detachedHead=false",
187
+ "checkout",
188
+ "--quiet",
189
+ pin.revision
190
+ ], src);
191
+ if (!checkout.ok) throw new Error(`git checkout ${pin.revision} failed: ${checkout.output}`);
192
+ if (resolved) fs.copyFileSync(resolved, path.join(src, "Package.resolved"));
193
+ const manifest = run("swift", ["package", "dump-package"], src);
194
+ if (!manifest.ok) throw new Error(`swift package dump-package failed: ${manifest.output}`);
195
+ const products = JSON.parse(manifest.stdout).products.filter((p) => "library" in p.type).map((p) => p.name);
196
+ if (!products.length) throw new Error("its manifest declares no library product");
197
+ for (const product of products) {
198
+ const r = run("xcodebuild", [
199
+ "build",
200
+ "-quiet",
201
+ "-scheme",
202
+ product,
203
+ "-destination",
204
+ "generic/platform=iOS Simulator",
205
+ "-derivedDataPath",
206
+ derived,
207
+ "-skipPackagePluginValidation",
208
+ `IPHONEOS_DEPLOYMENT_TARGET=${target}`
209
+ ], src);
210
+ if (!r.ok) throw new Error(`xcodebuild of ${product} failed: ${r.output.split("\n").slice(-20).join("\n")}`);
211
+ }
212
+ const built = path.join(derived, "Build/Products/Debug-iphonesimulator");
213
+ const checkouts = path.join(derived, "SourcePackages/checkouts");
214
+ const ownerOf = (module) => {
215
+ for (const id of fs.existsSync(checkouts) ? fs.readdirSync(checkouts) : []) if (fs.existsSync(path.join(checkouts, id, "Sources", module))) return id.toLowerCase();
216
+ return pin.identity;
217
+ };
218
+ const modules = fs.readdirSync(built).flatMap((f) => {
219
+ const [, module, kind] = /^(.+)\.(swiftmodule|framework)$/.exec(f) ?? [];
220
+ if (!module) return [];
221
+ const framework = kind === "framework";
222
+ if (framework && !fs.existsSync(path.join(built, f, "Modules", `${module}.swiftmodule`))) return [];
223
+ const owner = ownerOf(module);
224
+ return [{
225
+ module,
226
+ dir: built,
227
+ ...framework ? { framework: true } : {},
228
+ package: owner === pin.identity ? pinName(pin) : `${owner}@`
229
+ }];
230
+ });
231
+ fs.writeFileSync(path.join(dir, "modules.json"), `${JSON.stringify({
232
+ products,
233
+ modules
234
+ }, null, 2)}\n`);
235
+ }
236
+ function run(cmd, args, cwd) {
237
+ const r = spawnSync(cmd, args, {
238
+ cwd,
239
+ encoding: "utf8",
240
+ maxBuffer: 1 << 26
241
+ });
242
+ const stdout = r.stdout ?? "";
243
+ return {
244
+ ok: r.status === 0,
245
+ stdout,
246
+ output: `${stdout}${r.stderr ?? r.error?.message ?? ""}`.trim()
247
+ };
248
+ }
249
+ //#endregion
10
250
  //#region ../bindgen/src/vendored.ts
11
251
  function frameworkSearchPath(framework) {
12
252
  if (!framework.endsWith(".xcframework")) return path.dirname(framework);
@@ -65,8 +305,16 @@ function mapLucentPaths(root) {
65
305
  function projectSdk(root, native) {
66
306
  const binaries = native?.binaries ?? packageBinaries(root);
67
307
  const pods = podsSearchPaths(path.join(root, "ios"));
308
+ const app = xcodeApp(path.join(root, "ios"));
309
+ const project = app ? {
310
+ ...app.deploymentTarget ? { deploymentTarget: app.deploymentTarget } : {},
311
+ ...app.packages.length ? { swiftPackages: swiftPackages(app) } : {}
312
+ } : {};
68
313
  const frameworkPaths = [...new Set(binaries.ios.map(frameworkSearchPath).filter((dir) => dir !== void 0))];
69
- const ios = pods || frameworkPaths.length ? pods ?? {} : void 0;
314
+ const ios = pods || frameworkPaths.length || app ? {
315
+ ...pods,
316
+ ...project
317
+ } : void 0;
70
318
  return {
71
319
  android: {
72
320
  classpath: path.join(root, ".lucent/android-classpath.json"),
@@ -280,10 +528,11 @@ function gradleInputsHash(root, native) {
280
528
  return h.digest("hex").slice(0, 16);
281
529
  }
282
530
  const gradlewOf = (root) => path.join(root, "android", process.platform === "win32" ? "gradlew.bat" : "gradlew");
283
- function pendingAndroidModules(root, files, sdk) {
531
+ const hasAndroidProject = (root) => fs.existsSync(gradlewOf(root));
532
+ function pendingAndroidModules(root, files, sdk, withProject = false) {
284
533
  const imports = sdkImports(files).android;
285
534
  const resolved = sdk.android?.classpath && fs.existsSync(sdk.android.classpath);
286
- if (!imports.length || resolved || fs.existsSync(gradlewOf(root))) return [];
535
+ if (!imports.length || resolved || !withProject && hasAndroidProject(root)) return [];
287
536
  const available = sdkModules("android", sdk);
288
537
  if (!Array.isArray(available)) return [];
289
538
  const declared = new Set(available);
@@ -339,4 +588,4 @@ function backgroundPrefetch(root, files, platforms) {
339
588
  }).unref();
340
589
  }
341
590
  //#endregion
342
- export { projectHashes as a, sdkImports as c, pendingAndroidModules as i, staleAutolinking as l, mapLucentPaths as n, projectSdk as o, missingAppEntries as r, resolveAndroidDependencies as s, backgroundPrefetch as t, writeLinkedPackage as u };
591
+ export { pendingAndroidModules as a, resolveAndroidDependencies as c, writeLinkedPackage as d, missingAppEntries as i, sdkImports as l, hasAndroidProject as n, projectHashes as o, mapLucentPaths as r, projectSdk as s, backgroundPrefetch as t, staleAutolinking as u };
@@ -1,7 +1,7 @@
1
- import { N as projectFiles, R as coverage, U as symbolKey, c as sdkModules, et as sdkSourceModule, h as toolkitsFrom, s as sdkModule } from "./src-Bzpv4-ox.js";
2
- import { c as sdkImports, o as projectSdk } from "./project-MN6cwMwO.js";
1
+ import { P as projectFiles, W as symbolKey, c as sdkModules, g as toolkitsFrom, m as viewCoverage, s as sdkModule, tt as sdkSourceModule, z as coverage } from "./src-DMbXwPlK.js";
2
+ import { l as sdkImports, s as projectSdk } from "./project-Bp9I-fLP.js";
3
3
  import { i as table } from "./format-D9fnIMEx.js";
4
- import { n as USAGE_FILE, o as readUsage } from "./sdk-usage-D_ly-uLw.js";
4
+ import { n as USAGE_FILE, o as readUsage } from "./sdk-usage-Cc7u1i0L.js";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
7
  //#region src/cli/commands/sdk-coverage.ts
@@ -34,7 +34,15 @@ function run({ root, flags, out }) {
34
34
  out.error(`${t.error(t.symbols.fail)} ${r.missing}`);
35
35
  return 1;
36
36
  }
37
- reports.push(coverage(r.schema, void 0, evidence));
37
+ const moduleOf = (name) => {
38
+ const found = sdkModule(platform, name, sdk);
39
+ return "missing" in found ? void 0 : found.schema;
40
+ };
41
+ const views = flags.views ? viewCoverage(r.schema, moduleOf) : void 0;
42
+ reports.push({
43
+ ...coverage(r.schema, void 0, evidence),
44
+ ...views ? { views } : {}
45
+ });
38
46
  for (const toolkit of toolkitsFrom(platform, m)) {
39
47
  const source = sdkSourceModule(platform, m, sdk);
40
48
  if ("missing" in source) {
@@ -71,6 +79,27 @@ function run({ root, flags, out }) {
71
79
  "generated",
72
80
  "exercised"
73
81
  ].map((h) => t.dim(h)), ...rows])) out.print(line);
82
+ for (const c of reports) {
83
+ if (!c.views?.length) continue;
84
+ out.print("");
85
+ out.print(t.bold(`${c.module} views`));
86
+ for (const line of table([[
87
+ "view",
88
+ "made",
89
+ "props",
90
+ "events",
91
+ "children",
92
+ "left out"
93
+ ].map((h) => t.dim(h)), ...c.views.map((v) => [
94
+ v.view,
95
+ v.made,
96
+ String(v.props.length),
97
+ String(v.events.length),
98
+ v.children ? "yes" : "-",
99
+ String(v.leftOut.length)
100
+ ])])) out.print(line.trimEnd());
101
+ if (flags.members) for (const v of c.views) for (const a of [...v.props, ...v.events]) out.print(` ${v.view} ${a.name}: ${t.dim(a.explanation)}`);
102
+ }
74
103
  if (flags.members) for (const c of reports) {
75
104
  out.print("");
76
105
  out.print(t.bold(c.module));