@lynxship/cli 0.1.15 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,17 +14,37 @@ expiring download URL with a compact terminal QR code.
14
14
  ## Install
15
15
 
16
16
  ```bash
17
- npm install --global @lynxship/cli
17
+ npm install --global @lynxship/cli@latest
18
18
  ```
19
19
 
20
20
  Or run it without a global install:
21
21
 
22
22
  ```bash
23
- npx @lynxship/cli doctor --project-dir ./my-lynx-app
23
+ npx @lynxship/cli@latest doctor --project-dir ./my-lynx-app
24
24
  ```
25
25
 
26
26
  The package exposes the `lynxship` executable.
27
27
 
28
+ ## Plugin ecosystem
29
+
30
+ Install a community plugin as a normal project dependency, list it in
31
+ `lynxship.json`, then validate it before building:
32
+
33
+ ```bash
34
+ npm install --save-dev @example/lynx-plugin
35
+ npx lynxship plugin doctor
36
+ npx lynxship plugin list
37
+ npx lynxship plugin apply --platform android --profile production
38
+ ```
39
+
40
+ The public contract is exported by `@lynxship/plugin-api`. Plugins can make
41
+ JSON configuration contributions and bounded, idempotent Android/iOS/native
42
+ file changes. Autolink remains the official Lynx `lynx.lib.json` and codegen
43
+ workflow. Template and cloud contributions are discoverable metadata until a
44
+ reviewed provider or template service consumes them.
45
+
46
+ See the full contract in the repository `docs/plugin-ecosystem.md`.
47
+
28
48
  ## Vue Lynx projects
29
49
 
30
50
  LynxShip supports projects created with the official Vue Lynx scaffold. Vue
@@ -52,6 +72,34 @@ The CLI generator itself creates ReactLynx templates by default. Use the
52
72
  official `create-vue-lynx` scaffold when Vue is required, then add LynxShip to
53
73
  that existing project with `init`.
54
74
 
75
+ ## Octane and Miso
76
+
77
+ LynxShip detects Octane and Miso projects without replacing their
78
+ project-owned configuration.
79
+
80
+ Octane uses the official `@octanejs/rspeedy-plugin`. The Lynx integration is
81
+ still early access and its Lynx packages are not currently published on npm,
82
+ so development validation must use the official Octane repository and its
83
+ pnpm workspace:
84
+
85
+ ```bash
86
+ git clone https://github.com/octanejs/octane.git
87
+ cd octane
88
+ pnpm install --filter @octanejs/rspeedy-plugin... --frozen-lockfile
89
+ pnpm --filter @octanejs/rspeedy-plugin exec rspeedy dev \
90
+ --root examples/gallery --environment lynx
91
+ ```
92
+
93
+ Miso uses the official Haskell/Nix flake rather than an npm build script.
94
+ Install Nix and the project's Haskell toolchain, then let LynxShip use the
95
+ flake's `default`/`bundle` output or configure a project-specific output in
96
+ `lynxship.json`. Miso remains experimental; a successful bundle does not
97
+ prove a signed Android or iOS production build.
98
+
99
+ See the source-verified fixture notes in the repository's
100
+ `examples/lynx-octane-fixture` and `examples/miso-lynx-fixture` directories,
101
+ plus `docs/compatibility.md`.
102
+
55
103
  ## Requirements
56
104
 
57
105
  - Node.js 22 or 24 LTS, or Node.js 26 Current; Node.js 24 LTS is the
@@ -486,9 +534,11 @@ actual recording and analysis.
486
534
 
487
535
  ## Package layout
488
536
 
489
- The CLI is backed by the public `@lynxship/*` runtime packages in this
490
- workspace. They are published with the same version and must be available in
491
- the configured npm scope before installing the CLI package.
537
+ The CLI is backed by public `@lynxship/*` runtime packages in this workspace,
538
+ including the public `@lynxship/plugin-api` contract.
539
+ Their published versions may differ from the CLI version; workspace ranges are
540
+ rewritten to released versions when the package is packed. Publish a changed
541
+ runtime package before publishing the CLI when its source or version changed.
492
542
 
493
543
  ## License
494
544
 
@@ -1 +1 @@
1
- {"version":3,"file":"android-build.d.ts","sourceRoot":"","sources":["../src/android-build.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWhD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AA8KD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa7D;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsCpE;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CA+JnB"}
1
+ {"version":3,"file":"android-build.d.ts","sourceRoot":"","sources":["../src/android-build.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWhD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AA8KD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa7D;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsCpE;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAgKnB"}
@@ -218,6 +218,7 @@ export async function runRealAndroidBuild(job, options) {
218
218
  await buildLynxBundle(options.root, {
219
219
  env: environment,
220
220
  ...processOptions,
221
+ miso: options.profile.miso,
221
222
  });
222
223
  }
223
224
  step("Rspeedy bundle ready", 20);
@@ -4,6 +4,11 @@ export interface BundleBuildOptions {
4
4
  onOutput?: (line: string) => void;
5
5
  script?: string;
6
6
  rspeedyArgs?: string[];
7
+ miso?: {
8
+ attribute?: string;
9
+ artifact?: string;
10
+ };
7
11
  }
12
+ export declare function resolveMisoBuildTarget(flake: string, configuredAttribute?: string): string | undefined;
8
13
  export declare function buildLynxBundle(root: string, options?: BundleBuildOptions): Promise<void>;
9
14
  //# sourceMappingURL=bundle-build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundle-build.d.ts","sourceRoot":"","sources":["../src/bundle-build.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAcD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAqBf"}
1
+ {"version":3,"file":"bundle-build.d.ts","sourceRoot":"","sources":["../src/bundle-build.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,GAC3B,MAAM,GAAG,SAAS,CAWpB;AAcD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA8Df"}
@@ -1,6 +1,23 @@
1
- import { readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { packageManagerScriptCommand, runProcess, runRspeedy, } from "./process-runner.js";
1
+ import { copyFile, mkdir, readFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { basename, join, resolve } from "node:path";
4
+ import { assert } from "@lynxship/contracts";
5
+ import { commandExists, packageManagerScriptCommand, runProcess, runRspeedy, } from "./process-runner.js";
6
+ import { detectLynxFramework } from "./frameworks.js";
7
+ export function resolveMisoBuildTarget(flake, configuredAttribute) {
8
+ const explicitAttribute = configuredAttribute?.trim();
9
+ if (explicitAttribute)
10
+ return ".#" + explicitAttribute;
11
+ if (flake.includes("counter-bundle"))
12
+ return ".#counter-bundle";
13
+ if (/\binherit\s+bundle\b|\bdefault\s*=\s*bundle\b/.test(flake)) {
14
+ return ".#bundle";
15
+ }
16
+ if (/\bdefault\s*=/.test(flake) && flake.includes("mkLynxBundle")) {
17
+ return ".";
18
+ }
19
+ return undefined;
20
+ }
4
21
  async function resolveBundleScript(root) {
5
22
  try {
6
23
  const manifest = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
@@ -13,6 +30,28 @@ async function resolveBundleScript(root) {
13
30
  return "build";
14
31
  }
15
32
  export async function buildLynxBundle(root, options = {}) {
33
+ const framework = await detectLynxFramework(root);
34
+ if (framework.buildSystem === "miso-nix" &&
35
+ !options.script &&
36
+ !options.rspeedyArgs) {
37
+ assert(commandExists("nix"), "BUILD_MISO_NIX_REQUIRED", "Miso requires Nix. Install Nix, then rerun lynxship doctor or provide a project build script.");
38
+ const flake = await readFile(join(root, "flake.nix"), "utf8").catch(() => "");
39
+ const target = resolveMisoBuildTarget(flake, options.miso?.attribute);
40
+ assert(target, "BUILD_MISO_ATTRIBUTE_REQUIRED", "Miso bundle output is not discoverable. Set build.<profile>.miso.attribute in lynxship.json, or expose a default/mkLynxBundle output in flake.nix.");
41
+ await runProcess("nix", ["build", target], {
42
+ cwd: root,
43
+ env: options.env,
44
+ quiet: options.quiet,
45
+ onOutput: options.onOutput,
46
+ });
47
+ const artifact = resolve(root, options.miso?.artifact ?? "result/main.lynx.bundle");
48
+ assert(existsSync(artifact), "BUILD_MISO_BUNDLE_MISSING", "Miso did not produce the configured Lynx bundle: " + artifact);
49
+ const output = join(root, "dist", basename(artifact));
50
+ await mkdir(join(root, "dist"), { recursive: true });
51
+ await copyFile(artifact, output);
52
+ options.onOutput?.("Miso bundle ready: " + output);
53
+ return;
54
+ }
16
55
  if (!options.script && options.rspeedyArgs) {
17
56
  await runRspeedy(root, "build", options.rspeedyArgs, {
18
57
  env: options.env,
package/dist/config.d.ts CHANGED
@@ -1,8 +1,13 @@
1
1
  import { type Platform } from "@lynxship/contracts";
2
+ import type { PluginReference } from "@lynxship/plugin-api";
2
3
  export interface BuildProfile {
3
4
  distribution?: string;
4
5
  channel?: string;
5
6
  environment?: string;
7
+ miso?: {
8
+ attribute?: string;
9
+ artifact?: string;
10
+ };
6
11
  android?: {
7
12
  artifact?: string;
8
13
  };
@@ -39,6 +44,7 @@ export interface BuildProfile {
39
44
  }
40
45
  export interface LynxShipConfig {
41
46
  projectId?: string;
47
+ plugins?: PluginReference[];
42
48
  cli?: {
43
49
  apiUrl?: string;
44
50
  organizationId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -33,6 +33,7 @@ const allowedRootKeys = new Set([
33
33
  "submit",
34
34
  "update",
35
35
  "artifacts",
36
+ "plugins",
36
37
  ]);
37
38
  export function validateConfig(config, options = {}) {
38
39
  assert(config && typeof config === "object" && !Array.isArray(config), "CONFIG_INVALID", "lynxship.json must contain an object");
@@ -41,6 +42,20 @@ export function validateConfig(config, options = {}) {
41
42
  assert(!options.ci || unknown.length === 0, "CONFIG_UNKNOWN_KEY", `Unknown configuration key(s): ${unknown.join(", ")}`, { unknown });
42
43
  if (value.projectId !== undefined)
43
44
  assert(typeof value.projectId === "string" && value.projectId.length > 0, "CONFIG_PROJECT_ID", "projectId must be a non-empty string");
45
+ if (value.plugins !== undefined) {
46
+ assert(Array.isArray(value.plugins), "CONFIG_PLUGINS", "plugins must be an array of package names or [package, options] tuples");
47
+ for (const plugin of value.plugins) {
48
+ const validString = typeof plugin === "string" && plugin.length > 0;
49
+ const validTuple = Array.isArray(plugin) &&
50
+ plugin.length === 2 &&
51
+ typeof plugin[0] === "string" &&
52
+ plugin[0].length > 0 &&
53
+ plugin[1] !== null &&
54
+ typeof plugin[1] === "object" &&
55
+ !Array.isArray(plugin[1]);
56
+ assert(validString || validTuple, "CONFIG_PLUGINS", "Each plugin must be a package name or [package name, JSON options]");
57
+ }
58
+ }
44
59
  assert(value.runtimeVersion?.policy === undefined ||
45
60
  ["fingerprint", "manual"].includes(value.runtimeVersion.policy), "CONFIG_RUNTIME_POLICY", "runtimeVersion.policy must be fingerprint or manual");
46
61
  if (value.runtimeVersion?.policy === "manual")
@@ -1 +1 @@
1
- {"version":3,"file":"desktop-build.d.ts","sourceRoot":"","sources":["../src/desktop-build.ts"],"names":[],"mappings":"AAGA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED,UAAU,WAAW;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,WAAW,EACrB,OAAO,CAAC,EAAE,YAAY,GACrB,MAAM,GAAG,SAAS,CAMpB;AAYD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAmBlB;AA+DD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CA0GnB"}
1
+ {"version":3,"file":"desktop-build.d.ts","sourceRoot":"","sources":["../src/desktop-build.ts"],"names":[],"mappings":"AAGA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED,UAAU,WAAW;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,WAAW,EACrB,OAAO,CAAC,EAAE,YAAY,GACrB,MAAM,GAAG,SAAS,CAMpB;AAYD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAmBlB;AA+DD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CA2GnB"}
@@ -99,6 +99,7 @@ export async function runRealDesktopBuild(job, options) {
99
99
  await buildLynxBundle(options.root, {
100
100
  quiet: options.quiet,
101
101
  onOutput: options.onEvent,
102
+ miso: options.profile.miso,
102
103
  });
103
104
  }
104
105
  transitionBuild(job, "queued", "Desktop build queued locally");
@@ -0,0 +1,10 @@
1
+ export type LynxFramework = "react" | "vue" | "octane" | "miso" | "vanilla" | "unknown";
2
+ export interface FrameworkDetection {
3
+ framework: LynxFramework;
4
+ label: string;
5
+ evidence: string;
6
+ buildSystem: "rspeedy" | "miso-nix" | "unknown";
7
+ experimental: boolean;
8
+ }
9
+ export declare function detectLynxFramework(root: string): Promise<FrameworkDetection>;
10
+ //# sourceMappingURL=frameworks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frameworks.d.ts","sourceRoot":"","sources":["../src/frameworks.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GACrB,OAAO,GACP,KAAK,GACL,QAAQ,GACR,MAAM,GACN,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAChD,YAAY,EAAE,OAAO,CAAC;CACvB;AAiCD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,kBAAkB,CAAC,CA+F7B"}
@@ -0,0 +1,104 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ async function readText(file) {
5
+ return readFile(file, "utf8").catch(() => "");
6
+ }
7
+ async function readManifest(root) {
8
+ try {
9
+ return JSON.parse(await readText(join(root, "package.json")));
10
+ }
11
+ catch {
12
+ return {};
13
+ }
14
+ }
15
+ function dependencyNames(manifest) {
16
+ return new Set([
17
+ ...Object.keys(manifest.dependencies ?? {}),
18
+ ...Object.keys(manifest.devDependencies ?? {}),
19
+ ]);
20
+ }
21
+ function hasAny(names, candidates) {
22
+ return candidates.find((name) => names.has(name));
23
+ }
24
+ export async function detectLynxFramework(root) {
25
+ const manifest = await readManifest(root);
26
+ const names = dependencyNames(manifest);
27
+ const scripts = Object.values(manifest.scripts ?? {}).join(" ");
28
+ const configFiles = [
29
+ "lynx.config.ts",
30
+ "lynx.config.js",
31
+ "lynx.config.mjs",
32
+ "lynx.config.cjs",
33
+ "octane.config.ts",
34
+ "octane.config.js",
35
+ ];
36
+ const config = (await Promise.all(configFiles
37
+ .filter((file) => existsSync(join(root, file)))
38
+ .map((file) => readText(join(root, file))))).join("\n");
39
+ const flake = await readText(join(root, "flake.nix"));
40
+ const cabal = await readText(join(root, "cabal.project"));
41
+ const hasCabalFile = existsSync(join(root, "cabal.project"));
42
+ const hasMiso = hasAny(names, ["miso", "miso-lynx"]) ||
43
+ (existsSync(join(root, "flake.nix")) &&
44
+ /miso|mkLynxBundle|Miso\\.Native|lynx/i.test(flake + "\n" + cabal)) ||
45
+ (hasCabalFile && /miso|lynx/i.test(cabal));
46
+ if (hasMiso) {
47
+ return {
48
+ framework: "miso",
49
+ label: "Miso (Haskell)",
50
+ evidence: "Miso.Native/Haskell project detected",
51
+ buildSystem: "miso-nix",
52
+ experimental: true,
53
+ };
54
+ }
55
+ const hasOctane = hasAny(names, ["octane", "@octanejs/lynx", "@octanejs/rspeedy-plugin"]) ||
56
+ existsSync(join(root, "octane.config.ts")) ||
57
+ existsSync(join(root, "octane.config.js")) ||
58
+ /pluginOctane|@jsxImportSource\\s+octane|\\.tsrx\\b/.test(config + scripts);
59
+ if (hasOctane) {
60
+ return {
61
+ framework: "octane",
62
+ label: "Octane",
63
+ evidence: "Octane compiler/Rspeedy integration detected",
64
+ buildSystem: "rspeedy",
65
+ experimental: true,
66
+ };
67
+ }
68
+ const vueDependency = hasAny(names, ["vue-lynx", "@lynx-js/vue"]);
69
+ if (vueDependency || /pluginVueLynx|vue-lynx/i.test(config)) {
70
+ return {
71
+ framework: "vue",
72
+ label: "Vue Lynx",
73
+ evidence: "Vue Lynx integration detected",
74
+ buildSystem: "rspeedy",
75
+ experimental: false,
76
+ };
77
+ }
78
+ if (hasAny(names, ["@lynx-js/react", "@lynx-js/react-rsbuild-plugin"])) {
79
+ return {
80
+ framework: "react",
81
+ label: "ReactLynx",
82
+ evidence: "official ReactLynx package detected",
83
+ buildSystem: "rspeedy",
84
+ experimental: false,
85
+ };
86
+ }
87
+ if (names.has("@lynx-js/rspeedy") ||
88
+ /rspeedy|environments\\s*:/i.test(config + scripts)) {
89
+ return {
90
+ framework: "vanilla",
91
+ label: "Vanilla Lynx",
92
+ evidence: "Rspeedy/Lynx project detected",
93
+ buildSystem: "rspeedy",
94
+ experimental: false,
95
+ };
96
+ }
97
+ return {
98
+ framework: "unknown",
99
+ label: "Unknown",
100
+ evidence: "No supported Lynx framework marker found",
101
+ buildSystem: "unknown",
102
+ experimental: false,
103
+ };
104
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"guidance.d.ts","sourceRoot":"","sources":["../src/guidance.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAChC;AA6mBD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,eAAoB,GAC5B,WAAW,CAEb"}
1
+ {"version":3,"file":"guidance.d.ts","sourceRoot":"","sources":["../src/guidance.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAChC;AAmoBD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,eAAoB,GAC5B,WAAW,CAEb"}
package/dist/guidance.js CHANGED
@@ -105,6 +105,28 @@ const guidance = {
105
105
  ],
106
106
  note: "Configure environments.web in lynx.config.* or provide the project's build:web script.",
107
107
  },
108
+ BUILD_MISO_NIX_REQUIRED: {
109
+ commands: [
110
+ "lynxship doctor --platform android",
111
+ "nix --version",
112
+ "lynxship build --platform android --profile production",
113
+ ],
114
+ note: "Install Nix and use the project's flake to build the Miso Lynx bundle.",
115
+ },
116
+ BUILD_MISO_ATTRIBUTE_REQUIRED: {
117
+ commands: [
118
+ "lynxship init",
119
+ "lynxship build --platform android --profile production",
120
+ ],
121
+ note: "Expose a default/mkLynxBundle output or set build.production.miso.attribute to the Nix flake output that creates main.lynx.bundle.",
122
+ },
123
+ BUILD_MISO_BUNDLE_MISSING: {
124
+ commands: [
125
+ "nix build",
126
+ "lynxship build --platform android --profile production",
127
+ ],
128
+ note: "Check build.production.miso.artifact and the output produced by the Miso flake.",
129
+ },
108
130
  WEB_BUNDLE_MISSING: {
109
131
  commands: [
110
132
  "lynxship doctor --platform web",
@@ -1 +1 @@
1
- {"version":3,"file":"harmony-build.d.ts","sourceRoot":"","sources":["../src/harmony-build.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAeD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAcA;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BnB;AA2GD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAoGnB"}
1
+ {"version":3,"file":"harmony-build.d.ts","sourceRoot":"","sources":["../src/harmony-build.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAeD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAcA;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BnB;AA2GD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAqGnB"}
@@ -141,6 +141,7 @@ export async function runRealHarmonyBuild(job, options) {
141
141
  await buildLynxBundle(options.root, {
142
142
  quiet: options.quiet,
143
143
  onOutput: options.onEvent,
144
+ miso: options.profile.miso,
144
145
  });
145
146
  }
146
147
  step("Syncing bundle into the HarmonyOS HAP resources…", 20);