@lynxship/cli 0.1.7 → 0.1.10
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 +175 -19
- package/dist/android-toolchain.d.ts +23 -0
- package/dist/android-toolchain.d.ts.map +1 -0
- package/dist/android-toolchain.js +319 -0
- package/dist/artifact-build.d.ts +17 -0
- package/dist/artifact-build.d.ts.map +1 -0
- package/dist/artifact-build.js +56 -0
- package/dist/artifact-name.d.ts +1 -1
- package/dist/artifact-name.d.ts.map +1 -1
- package/dist/autolink.d.ts +3 -2
- package/dist/autolink.d.ts.map +1 -1
- package/dist/autolink.js +130 -6
- package/dist/bundle-build.d.ts +2 -0
- package/dist/bundle-build.d.ts.map +1 -1
- package/dist/bundle-build.js +11 -3
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/desktop-build.d.ts +22 -0
- package/dist/desktop-build.d.ts.map +1 -0
- package/dist/desktop-build.js +161 -0
- package/dist/desktop-signing.d.ts +14 -0
- package/dist/desktop-signing.d.ts.map +1 -0
- package/dist/desktop-signing.js +200 -0
- package/dist/guidance.d.ts.map +1 -1
- package/dist/guidance.js +113 -4
- package/dist/harmony-build.d.ts +22 -0
- package/dist/harmony-build.d.ts.map +1 -0
- package/dist/harmony-build.js +194 -0
- package/dist/index.js +333 -73
- package/dist/ios-toolchain.d.ts +19 -0
- package/dist/ios-toolchain.d.ts.map +1 -0
- package/dist/ios-toolchain.js +303 -0
- package/dist/lynx-devtool.d.ts +18 -0
- package/dist/lynx-devtool.d.ts.map +1 -0
- package/dist/lynx-devtool.js +125 -0
- package/dist/process-runner.d.ts +11 -0
- package/dist/process-runner.d.ts.map +1 -1
- package/dist/process-runner.js +29 -0
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +6 -0
- package/dist/remote.d.ts +3 -3
- package/dist/remote.d.ts.map +1 -1
- package/dist/target-toolchain.d.ts +17 -0
- package/dist/target-toolchain.d.ts.map +1 -0
- package/dist/target-toolchain.js +122 -0
- package/dist/ui/components.d.ts.map +1 -1
- package/dist/ui/components.js +8 -2
- package/dist/web-build.d.ts +16 -0
- package/dist/web-build.d.ts.map +1 -0
- package/dist/web-build.js +116 -0
- package/package.json +4 -4
package/dist/artifact-name.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type NativeArtifactExtension = "apk" | "aab" | "ipa";
|
|
1
|
+
export type NativeArtifactExtension = "apk" | "aab" | "ipa" | "hap" | "lynx.bundle" | "web.bundle" | "app" | "dmg" | "exe" | "appimage" | "zip";
|
|
2
2
|
export declare function nativeArtifactName(extension: NativeArtifactExtension): string;
|
|
3
3
|
//# sourceMappingURL=artifact-name.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-name.d.ts","sourceRoot":"","sources":["../src/artifact-name.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,uBAAuB,
|
|
1
|
+
{"version":3,"file":"artifact-name.d.ts","sourceRoot":"","sources":["../src/artifact-name.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,uBAAuB,GAC/B,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,aAAa,GACb,YAAY,GACZ,KAAK,GACL,KAAK,GACL,KAAK,GACL,UAAU,GACV,KAAK,CAAC;AAEV,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,uBAAuB,GAAG,MAAM,CAE7E"}
|
package/dist/autolink.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type MobilePlatform } from "@lynxship/contracts";
|
|
2
2
|
export interface AutolinkPlatformStatus {
|
|
3
3
|
required: boolean;
|
|
4
4
|
ready: boolean;
|
|
5
5
|
manifests: string[];
|
|
6
6
|
reason: string;
|
|
7
|
+
issues?: string[];
|
|
7
8
|
}
|
|
8
9
|
export interface AutolinkStatus {
|
|
9
10
|
android: AutolinkPlatformStatus;
|
|
10
11
|
ios: AutolinkPlatformStatus;
|
|
11
12
|
}
|
|
12
13
|
export declare function inspectAutolink(root: string): Promise<AutolinkStatus>;
|
|
13
|
-
export declare function requireAutolinkReady(root: string, platform:
|
|
14
|
+
export declare function requireAutolinkReady(root: string, platform: MobilePlatform): Promise<AutolinkPlatformStatus>;
|
|
14
15
|
//# sourceMappingURL=autolink.d.ts.map
|
package/dist/autolink.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autolink.d.ts","sourceRoot":"","sources":["../src/autolink.ts"],"names":[],"mappings":"AAGA,OAAO,EAAU,KAAK,
|
|
1
|
+
{"version":3,"file":"autolink.d.ts","sourceRoot":"","sources":["../src/autolink.ts"],"names":[],"mappings":"AAGA,OAAO,EAAU,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAiBlE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,sBAAsB,CAAC;IAChC,GAAG,EAAE,sBAAsB,CAAC;CAC7B;AAsQD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAwB3E;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,sBAAsB,CAAC,CAWjC"}
|
package/dist/autolink.js
CHANGED
|
@@ -8,6 +8,99 @@ const emptyStatus = () => ({
|
|
|
8
8
|
manifests: [],
|
|
9
9
|
reason: "No Lynx native-library manifest found",
|
|
10
10
|
});
|
|
11
|
+
function packageRoot(manifestPath) {
|
|
12
|
+
return dirname(manifestPath);
|
|
13
|
+
}
|
|
14
|
+
function platformSourceDirectory(manifestPath, platform, manifest) {
|
|
15
|
+
const definition = manifest.platforms?.[platform];
|
|
16
|
+
const sourceDir = definition && typeof definition.sourceDir === "string"
|
|
17
|
+
? definition.sourceDir
|
|
18
|
+
: platform;
|
|
19
|
+
return join(packageRoot(manifestPath), sourceDir);
|
|
20
|
+
}
|
|
21
|
+
async function findNativeSourceFiles(directory) {
|
|
22
|
+
const files = [];
|
|
23
|
+
async function visit(current) {
|
|
24
|
+
let entries;
|
|
25
|
+
try {
|
|
26
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
if (["build", "Pods", ".gradle", "node_modules"].includes(entry.name))
|
|
33
|
+
continue;
|
|
34
|
+
const file = join(current, entry.name);
|
|
35
|
+
if (entry.isDirectory()) {
|
|
36
|
+
await visit(file);
|
|
37
|
+
}
|
|
38
|
+
else if (entry.isFile() &&
|
|
39
|
+
/\.(?:java|kt|m|mm|h|swift|podspec)$/i.test(entry.name)) {
|
|
40
|
+
files.push(file);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
await visit(directory);
|
|
45
|
+
return files;
|
|
46
|
+
}
|
|
47
|
+
async function manifestIssues(file, platform) {
|
|
48
|
+
const issues = [];
|
|
49
|
+
let manifest;
|
|
50
|
+
try {
|
|
51
|
+
manifest = JSON.parse(await readFile(file, "utf8"));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return ["lynx.lib.json is not valid JSON"];
|
|
55
|
+
}
|
|
56
|
+
const definition = manifest.platforms?.[platform];
|
|
57
|
+
if (!definition || typeof definition !== "object")
|
|
58
|
+
return issues;
|
|
59
|
+
const root = packageRoot(file);
|
|
60
|
+
if (platform === "android") {
|
|
61
|
+
if (typeof definition.packageName !== "string" || !definition.packageName)
|
|
62
|
+
issues.push("platforms.android.packageName is required");
|
|
63
|
+
if (definition.sourceDir !== undefined &&
|
|
64
|
+
(typeof definition.sourceDir !== "string" || !definition.sourceDir))
|
|
65
|
+
issues.push("platforms.android.sourceDir must be a non-empty string");
|
|
66
|
+
}
|
|
67
|
+
if (platform === "ios" && definition.podspecPath !== undefined) {
|
|
68
|
+
if (typeof definition.podspecPath !== "string" || !definition.podspecPath)
|
|
69
|
+
issues.push("platforms.ios.podspecPath must be a non-empty string");
|
|
70
|
+
}
|
|
71
|
+
const sourceDirectory = platformSourceDirectory(file, platform, manifest);
|
|
72
|
+
if (!existsSync(sourceDirectory))
|
|
73
|
+
issues.push(`native source directory is missing: ${sourceDirectory}`);
|
|
74
|
+
if (platform === "ios") {
|
|
75
|
+
const podspec = typeof definition.podspecPath === "string"
|
|
76
|
+
? join(root, definition.podspecPath)
|
|
77
|
+
: (await findNativeSourceFiles(sourceDirectory)).find((candidate) => candidate.endsWith(".podspec"));
|
|
78
|
+
if (definition.podspecPath && !existsSync(podspec ?? ""))
|
|
79
|
+
issues.push(`iOS podspec is missing: ${podspec}`);
|
|
80
|
+
}
|
|
81
|
+
return issues;
|
|
82
|
+
}
|
|
83
|
+
async function capabilityNames(file, platform) {
|
|
84
|
+
let manifest;
|
|
85
|
+
try {
|
|
86
|
+
manifest = JSON.parse(await readFile(file, "utf8"));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
const source = platformSourceDirectory(file, platform, manifest);
|
|
92
|
+
const files = await findNativeSourceFiles(source);
|
|
93
|
+
const names = new Set();
|
|
94
|
+
for (const nativeFile of files) {
|
|
95
|
+
const text = await readFile(nativeFile, "utf8").catch(() => "");
|
|
96
|
+
const expression = /@Lynx(?:NativeModule|Element|Service)\s*\(\s*["']?([A-Za-z0-9_.-]+)/g;
|
|
97
|
+
for (const match of text.matchAll(expression)) {
|
|
98
|
+
if (match[1])
|
|
99
|
+
names.add(match[1]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return [...names];
|
|
103
|
+
}
|
|
11
104
|
function findNodeModuleRoots(root) {
|
|
12
105
|
const roots = [];
|
|
13
106
|
let current = root;
|
|
@@ -89,6 +182,7 @@ async function platformStatus(root, platform, manifests) {
|
|
|
89
182
|
}
|
|
90
183
|
if (relevant.length === 0)
|
|
91
184
|
return emptyStatus();
|
|
185
|
+
const issues = (await Promise.all(relevant.map((file) => manifestIssues(file, platform)))).flat();
|
|
92
186
|
if (platform === "android") {
|
|
93
187
|
const settingsFile = [
|
|
94
188
|
join(root, "android", "settings.gradle"),
|
|
@@ -102,38 +196,68 @@ async function platformStatus(root, platform, manifests) {
|
|
|
102
196
|
? await readFile(settingsFile, "utf8")
|
|
103
197
|
: "";
|
|
104
198
|
const appText = appFile ? await readFile(appFile, "utf8") : "";
|
|
105
|
-
const
|
|
106
|
-
|
|
199
|
+
const settingsPlugin = settingsText.includes("org.lynxsdk.library-settings") ||
|
|
200
|
+
settingsText.includes("org.lynxsdk.lynx.library-settings");
|
|
201
|
+
const buildPlugin = appText.includes("org.lynxsdk.library-build") ||
|
|
202
|
+
appText.includes("org.lynxsdk.lynx.library-build");
|
|
203
|
+
const pluginsReady = settingsPlugin && buildPlugin;
|
|
204
|
+
const ready = pluginsReady && issues.length === 0;
|
|
107
205
|
return {
|
|
108
206
|
required: true,
|
|
109
207
|
ready,
|
|
110
208
|
manifests: relevant,
|
|
209
|
+
...(issues.length > 0 ? { issues } : {}),
|
|
111
210
|
reason: ready
|
|
112
211
|
? "Lynx Android Autolink plugins are enabled"
|
|
113
|
-
:
|
|
212
|
+
: issues.length > 0
|
|
213
|
+
? issues.join("; ")
|
|
214
|
+
: "Enable the Lynx Autolink settings and build plugins in settings.gradle and app/build.gradle (use the IDs matching the project Lynx release)",
|
|
114
215
|
};
|
|
115
216
|
}
|
|
116
217
|
const podfile = join(root, "ios", "Podfile");
|
|
117
218
|
const podfileText = (await hasFile(podfile))
|
|
118
219
|
? await readFile(podfile, "utf8")
|
|
119
220
|
: "";
|
|
120
|
-
const
|
|
221
|
+
const pluginsReady = podfileText.includes("cocoapods-lynx-library") &&
|
|
121
222
|
podfileText.includes("use_lynx_library!");
|
|
223
|
+
const ready = pluginsReady && issues.length === 0;
|
|
122
224
|
return {
|
|
123
225
|
required: true,
|
|
124
226
|
ready,
|
|
125
227
|
manifests: relevant,
|
|
228
|
+
...(issues.length > 0 ? { issues } : {}),
|
|
126
229
|
reason: ready
|
|
127
230
|
? "Lynx iOS Autolink CocoaPods integration is enabled"
|
|
128
|
-
:
|
|
231
|
+
: issues.length > 0
|
|
232
|
+
? issues.join("; ")
|
|
233
|
+
: "Add cocoapods-lynx-library and use_lynx_library! to ios/Podfile",
|
|
129
234
|
};
|
|
130
235
|
}
|
|
131
236
|
export async function inspectAutolink(root) {
|
|
132
237
|
const manifests = await findManifests(root);
|
|
133
|
-
|
|
238
|
+
const status = {
|
|
134
239
|
android: await platformStatus(root, "android", manifests),
|
|
135
240
|
ios: await platformStatus(root, "ios", manifests),
|
|
136
241
|
};
|
|
242
|
+
for (const platform of ["android", "ios"]) {
|
|
243
|
+
const owners = new Map();
|
|
244
|
+
for (const manifest of status[platform].manifests) {
|
|
245
|
+
for (const name of await capabilityNames(manifest, platform)) {
|
|
246
|
+
const owner = owners.get(name);
|
|
247
|
+
if (owner) {
|
|
248
|
+
const issue = `duplicate ${platform} native capability '${name}' in ${owner} and ${manifest}`;
|
|
249
|
+
status[platform].issues ??= [];
|
|
250
|
+
status[platform].issues.push(issue);
|
|
251
|
+
status[platform].ready = false;
|
|
252
|
+
status[platform].reason = status[platform].issues.join("; ");
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
owners.set(name, manifest);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return status;
|
|
137
261
|
}
|
|
138
262
|
export async function requireAutolinkReady(root, platform) {
|
|
139
263
|
const status = (await inspectAutolink(root))[platform];
|
package/dist/bundle-build.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export interface BundleBuildOptions {
|
|
|
2
2
|
env?: NodeJS.ProcessEnv;
|
|
3
3
|
quiet?: boolean;
|
|
4
4
|
onOutput?: (line: string) => void;
|
|
5
|
+
script?: string;
|
|
6
|
+
rspeedyArgs?: string[];
|
|
5
7
|
}
|
|
6
8
|
export declare function buildLynxBundle(root: string, options?: BundleBuildOptions): Promise<void>;
|
|
7
9
|
//# sourceMappingURL=bundle-build.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-build.d.ts","sourceRoot":"","sources":["../src/bundle-build.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/bundle-build.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { packageManagerScriptCommand, runProcess } from "./process-runner.js";
|
|
3
|
+
import { packageManagerScriptCommand, runProcess, runRspeedy, } from "./process-runner.js";
|
|
4
4
|
async function resolveBundleScript(root) {
|
|
5
5
|
try {
|
|
6
6
|
const manifest = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
|
|
@@ -13,8 +13,16 @@ async function resolveBundleScript(root) {
|
|
|
13
13
|
return "build";
|
|
14
14
|
}
|
|
15
15
|
export async function buildLynxBundle(root, options = {}) {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
if (!options.script && options.rspeedyArgs) {
|
|
17
|
+
await runRspeedy(root, "build", options.rspeedyArgs, {
|
|
18
|
+
env: options.env,
|
|
19
|
+
quiet: options.quiet,
|
|
20
|
+
onOutput: options.onOutput,
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const script = options.script ?? (await resolveBundleScript(root));
|
|
25
|
+
const packageManager = packageManagerScriptCommand(root, script, options.rspeedyArgs ?? []);
|
|
18
26
|
await runProcess(packageManager.command, packageManager.args, {
|
|
19
27
|
cwd: root,
|
|
20
28
|
env: options.env,
|
package/dist/config.d.ts
CHANGED
|
@@ -15,6 +15,25 @@ export interface BuildProfile {
|
|
|
15
15
|
exportOptionsPlist?: string;
|
|
16
16
|
bundleScript?: string;
|
|
17
17
|
};
|
|
18
|
+
harmony?: {
|
|
19
|
+
task?: string;
|
|
20
|
+
module?: string;
|
|
21
|
+
mode?: "project" | "module";
|
|
22
|
+
product?: string;
|
|
23
|
+
buildMode?: "debug" | "release";
|
|
24
|
+
bundleDir?: string;
|
|
25
|
+
artifact?: string;
|
|
26
|
+
signTool?: string;
|
|
27
|
+
};
|
|
28
|
+
web?: {
|
|
29
|
+
environment?: string;
|
|
30
|
+
script?: string;
|
|
31
|
+
artifact?: string;
|
|
32
|
+
};
|
|
33
|
+
desktop?: {
|
|
34
|
+
script?: string;
|
|
35
|
+
artifact?: string;
|
|
36
|
+
};
|
|
18
37
|
}
|
|
19
38
|
export interface LynxShipConfig {
|
|
20
39
|
projectId?: string;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,GAAG,CAAC,EAAE;QACJ,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE;QACJ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,cAAc,CAAC,EAAE;QAAE,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QACP,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE;YAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1C,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,cAAc,EAAE,cAU5B,CAAC;AAaF,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,EACf,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,cAAc,CAyChB;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,cAAc,CAAC,CAazB;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,SAAe,GAClB,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAIjC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAOrD"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,GAAG,CAAC,EAAE;QACJ,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAChC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE;QACJ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,cAAc,CAAC,EAAE;QAAE,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QACP,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE;YAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1C,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,cAAc,EAAE,cAU5B,CAAC;AAaF,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,EACf,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,cAAc,CAyChB;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,cAAc,CAAC,CAazB;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,SAAe,GAClB,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAIjC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAOrD"}
|
package/dist/config.js
CHANGED
|
@@ -57,6 +57,6 @@ export function resolveProfile(config, name = "production") {
|
|
|
57
57
|
return { name, ...profile };
|
|
58
58
|
}
|
|
59
59
|
export function platformValue(value) {
|
|
60
|
-
assert(
|
|
60
|
+
assert(["android", "ios", "harmony", "web", "desktop"].includes(value), "PLATFORM_INVALID", "Platform must be android, ios, harmony, web or desktop");
|
|
61
61
|
return value;
|
|
62
62
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type BuildJob } from "@lynxship/contracts";
|
|
2
|
+
import type { BuildProfile } from "./config.js";
|
|
3
|
+
interface DesktopBuildOptions {
|
|
4
|
+
root: string;
|
|
5
|
+
profile: BuildProfile;
|
|
6
|
+
uploadArtifacts?: boolean;
|
|
7
|
+
skipBundleBuild?: boolean;
|
|
8
|
+
allowUnsigned?: boolean;
|
|
9
|
+
quiet?: boolean;
|
|
10
|
+
onEvent?: (message: string) => void;
|
|
11
|
+
onProgress?: (value?: number, label?: string) => void;
|
|
12
|
+
}
|
|
13
|
+
interface PackageJson {
|
|
14
|
+
dependencies?: Record<string, string>;
|
|
15
|
+
devDependencies?: Record<string, string>;
|
|
16
|
+
scripts?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveDesktopPackScript(manifest: PackageJson, profile?: BuildProfile): string | undefined;
|
|
19
|
+
export declare function hasDesktopHost(root: string, profile?: BuildProfile): Promise<boolean>;
|
|
20
|
+
export declare function runRealDesktopBuild(job: BuildJob, options: DesktopBuildOptions): Promise<BuildJob>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=desktop-build.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { extname, join, resolve } from "node:path";
|
|
4
|
+
import { assert } from "@lynxship/contracts";
|
|
5
|
+
import { transitionBuild } from "@lynxship/build-orchestrator";
|
|
6
|
+
import { publishBuiltArtifact } from "./artifact-build.js";
|
|
7
|
+
import { packageManagerCommand, packageManagerScriptCommand, runProcess, } from "./process-runner.js";
|
|
8
|
+
import { buildLynxBundle } from "./bundle-build.js";
|
|
9
|
+
import { inspectDesktopSigning, verifyDesktopArtifactSignature, } from "./desktop-signing.js";
|
|
10
|
+
export function resolveDesktopPackScript(manifest, profile) {
|
|
11
|
+
if (profile?.desktop?.script)
|
|
12
|
+
return profile.desktop.script;
|
|
13
|
+
for (const name of ["pack", "build:desktop", "build:app"]) {
|
|
14
|
+
if (manifest.scripts?.[name])
|
|
15
|
+
return name;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
async function packageJson(root) {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(await readFile(join(root, "package.json"), "utf8"));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function hasDesktopHost(root, profile) {
|
|
28
|
+
const manifest = await packageJson(root);
|
|
29
|
+
const dependencies = {
|
|
30
|
+
...manifest.dependencies,
|
|
31
|
+
...manifest.devDependencies,
|
|
32
|
+
};
|
|
33
|
+
const hasElectronBuilder = Boolean(dependencies["electron-builder"] ||
|
|
34
|
+
existsSync(join(root, "electron-builder.yml")) ||
|
|
35
|
+
existsSync(join(root, "electron-builder.yaml")) ||
|
|
36
|
+
existsSync(join(root, "electron-builder.json")));
|
|
37
|
+
return Boolean(resolveDesktopPackScript(manifest, profile) ||
|
|
38
|
+
dependencies["@lynx-js/lynxtron"] ||
|
|
39
|
+
dependencies["@lynx-js/lynxtron-builder"] ||
|
|
40
|
+
dependencies.lynxtron ||
|
|
41
|
+
hasElectronBuilder);
|
|
42
|
+
}
|
|
43
|
+
function artifactExtension(file) {
|
|
44
|
+
const extension = extname(file).toLowerCase();
|
|
45
|
+
assert(extension === ".dmg" ||
|
|
46
|
+
extension === ".exe" ||
|
|
47
|
+
extension === ".zip" ||
|
|
48
|
+
extension === ".appimage", "DESKTOP_ARTIFACT_INVALID", "Desktop packaging must produce a .dmg, .exe, .zip or AppImage file.");
|
|
49
|
+
return extension.slice(1);
|
|
50
|
+
}
|
|
51
|
+
async function findDesktopArtifact(root, configured) {
|
|
52
|
+
if (configured) {
|
|
53
|
+
const file = resolve(root, configured);
|
|
54
|
+
assert(existsSync(file), "DESKTOP_ARTIFACT_MISSING", `Configured desktop artifact was not found: ${file}`);
|
|
55
|
+
assert((await stat(file)).isFile(), "DESKTOP_ARTIFACT_INVALID", "Configured desktop artifact must be a file, not an .app directory.");
|
|
56
|
+
return file;
|
|
57
|
+
}
|
|
58
|
+
const directories = [
|
|
59
|
+
"dist",
|
|
60
|
+
"release",
|
|
61
|
+
"out",
|
|
62
|
+
"build/electron",
|
|
63
|
+
"build/dist",
|
|
64
|
+
"build",
|
|
65
|
+
];
|
|
66
|
+
const candidates = [];
|
|
67
|
+
for (const directory of directories) {
|
|
68
|
+
const absolute = join(root, directory);
|
|
69
|
+
const entries = await readdir(absolute, { withFileTypes: true }).catch(() => []);
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
if (!entry.isFile())
|
|
72
|
+
continue;
|
|
73
|
+
if (/\.(?:dmg|exe|zip|appimage)$/i.test(entry.name))
|
|
74
|
+
candidates.push(join(absolute, entry.name));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
assert(candidates.length === 1, "DESKTOP_ARTIFACT_AMBIGUOUS", candidates.length === 0
|
|
78
|
+
? "The desktop packager produced no distributable file. Configure a pack script or build.<profile>.desktop.artifact."
|
|
79
|
+
: "The desktop packager produced multiple files. Set build.<profile>.desktop.artifact explicitly.");
|
|
80
|
+
return candidates[0];
|
|
81
|
+
}
|
|
82
|
+
export async function runRealDesktopBuild(job, options) {
|
|
83
|
+
assert(await hasDesktopHost(options.root, options.profile), "DESKTOP_HOST_REQUIRED", "No Lynxtron desktop host was found. Use the official Lynxtron template or configure a project pack script with @lynx-js/lynxtron-builder.");
|
|
84
|
+
const signing = await inspectDesktopSigning(options.root);
|
|
85
|
+
if (signing.status === "missing" || signing.status === "disabled") {
|
|
86
|
+
assert(options.allowUnsigned, "DESKTOP_SIGNING_REQUIRED", `${signing.value}. Configure a real desktop signing identity before a production build, or use --allow-unsigned together with --no-upload for local packaging tests. ${signing.fix}`);
|
|
87
|
+
}
|
|
88
|
+
const manifest = await packageJson(options.root);
|
|
89
|
+
const uploadArtifacts = options.uploadArtifacts ?? true;
|
|
90
|
+
const step = (message, value) => {
|
|
91
|
+
options.onEvent?.(message);
|
|
92
|
+
options.onProgress?.(value, message);
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
transitionBuild(job, "uploading_source", "Desktop source prepared");
|
|
96
|
+
const packScript = resolveDesktopPackScript(manifest, options.profile);
|
|
97
|
+
if (!options.skipBundleBuild && !packScript) {
|
|
98
|
+
step("Building Lynx desktop bundle with Rspeedy…", 10);
|
|
99
|
+
await buildLynxBundle(options.root, {
|
|
100
|
+
quiet: options.quiet,
|
|
101
|
+
onOutput: options.onEvent,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
transitionBuild(job, "queued", "Desktop build queued locally");
|
|
105
|
+
transitionBuild(job, "provisioning", "Lynxtron packaging toolchain selected");
|
|
106
|
+
transitionBuild(job, "installing_dependencies", "Desktop dependencies selected");
|
|
107
|
+
step("Preparing desktop package…", 30);
|
|
108
|
+
if (packScript) {
|
|
109
|
+
const command = packageManagerScriptCommand(options.root, packScript);
|
|
110
|
+
await runProcess(command.command, command.args, {
|
|
111
|
+
cwd: options.root,
|
|
112
|
+
quiet: options.quiet,
|
|
113
|
+
onOutput: options.onEvent,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const manager = packageManagerCommand(options.root);
|
|
118
|
+
await runProcess(manager.command, [...manager.prefix, "lynxtron-builder", "--publish", "never"], {
|
|
119
|
+
cwd: options.root,
|
|
120
|
+
quiet: options.quiet,
|
|
121
|
+
onOutput: options.onEvent,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
transitionBuild(job, "building", "Desktop package created");
|
|
125
|
+
const artifact = await findDesktopArtifact(options.root, options.profile.desktop?.artifact);
|
|
126
|
+
const signature = await verifyDesktopArtifactSignature(artifact);
|
|
127
|
+
if (!signature.signed && signature.status !== "not-required") {
|
|
128
|
+
assert(options.allowUnsigned, "DESKTOP_SIGNING_REQUIRED", `Desktop artifact is not signed: ${signature.detail} Configure a real desktop signing identity, or use --allow-unsigned together with --no-upload for local packaging tests.`);
|
|
129
|
+
step(`Warning: unsigned desktop artifact accepted for local testing`, 75);
|
|
130
|
+
}
|
|
131
|
+
else if (signature.signed) {
|
|
132
|
+
step("Desktop signature verified", 75);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
step("Desktop signature verification not required for this target", 75);
|
|
136
|
+
}
|
|
137
|
+
return publishBuiltArtifact({
|
|
138
|
+
root: options.root,
|
|
139
|
+
job,
|
|
140
|
+
platform: "desktop",
|
|
141
|
+
artifactPath: artifact,
|
|
142
|
+
extension: artifactExtension(artifact),
|
|
143
|
+
contentType: "application/octet-stream",
|
|
144
|
+
uploadArtifacts,
|
|
145
|
+
verificationMessage: "Desktop distributable output verified",
|
|
146
|
+
quiet: options.quiet,
|
|
147
|
+
onEvent: options.onEvent,
|
|
148
|
+
onProgress: options.onProgress,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (!["success", "failed", "canceled", "timed_out"].includes(job.state))
|
|
153
|
+
transitionBuild(job, "failed", error instanceof Error ? error.message : "Desktop build failed");
|
|
154
|
+
job.logs.push({
|
|
155
|
+
level: "error",
|
|
156
|
+
message: error instanceof Error ? error.message : "Desktop build failed",
|
|
157
|
+
at: new Date().toISOString(),
|
|
158
|
+
});
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type DesktopSigningStatus = "configured" | "missing" | "disabled" | "unknown" | "not-required";
|
|
2
|
+
export interface DesktopSigningCheck {
|
|
3
|
+
status: DesktopSigningStatus;
|
|
4
|
+
value: string;
|
|
5
|
+
fix: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DesktopArtifactSignature {
|
|
8
|
+
signed: boolean;
|
|
9
|
+
status: "signed" | "unsigned" | "unavailable" | "not-required";
|
|
10
|
+
detail: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function inspectDesktopSigning(root: string): Promise<DesktopSigningCheck>;
|
|
13
|
+
export declare function verifyDesktopArtifactSignature(artifactPath: string): Promise<DesktopArtifactSignature>;
|
|
14
|
+
//# sourceMappingURL=desktop-signing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desktop-signing.d.ts","sourceRoot":"","sources":["../src/desktop-signing.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,oBAAoB,GAC5B,YAAY,GACZ,SAAS,GACT,UAAU,GACV,SAAS,GACT,cAAc,CAAC;AAEnB,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,aAAa,GAAG,cAAc,CAAC;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAyCD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mBAAmB,CAAC,CAyC9B;AAuKD,wBAAsB,8BAA8B,CAClD,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,wBAAwB,CAAC,CAcnC"}
|