@lynxship/cli 0.1.20 → 0.1.21
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 +66 -5
- package/dist/android/signing.d.ts +16 -0
- package/dist/android/signing.d.ts.map +1 -0
- package/dist/android/signing.js +131 -0
- package/dist/android/types.d.ts +12 -0
- package/dist/android/types.d.ts.map +1 -0
- package/dist/android/types.js +1 -0
- package/dist/android-build.d.ts +4 -13
- package/dist/android-build.d.ts.map +1 -1
- package/dist/android-build.js +14 -133
- package/dist/android-toolchain/inspection.d.ts +3 -0
- package/dist/android-toolchain/inspection.d.ts.map +1 -0
- package/dist/android-toolchain/inspection.js +122 -0
- package/dist/android-toolchain/probes.d.ts +23 -0
- package/dist/android-toolchain/probes.d.ts.map +1 -0
- package/dist/android-toolchain/probes.js +183 -0
- package/dist/android-toolchain/types.d.ts +20 -0
- package/dist/android-toolchain/types.d.ts.map +1 -0
- package/dist/android-toolchain/types.js +1 -0
- package/dist/android-toolchain.d.ts +4 -20
- package/dist/android-toolchain.d.ts.map +1 -1
- package/dist/android-toolchain.js +4 -300
- package/dist/app-config.d.ts +25 -0
- package/dist/app-config.d.ts.map +1 -0
- package/dist/app-config.js +106 -0
- package/dist/bundle-build.d.ts.map +1 -1
- package/dist/bundle-build.js +11 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +66 -0
- package/dist/commands/build-execution.d.ts +41 -0
- package/dist/commands/build-execution.d.ts.map +1 -0
- package/dist/commands/build-execution.js +182 -0
- package/dist/commands/configuration.d.ts +19 -0
- package/dist/commands/configuration.d.ts.map +1 -0
- package/dist/commands/configuration.js +174 -0
- package/dist/commands/development.d.ts +22 -0
- package/dist/commands/development.d.ts.map +1 -0
- package/dist/commands/development.js +132 -0
- package/dist/commands/device.d.ts +3 -0
- package/dist/commands/device.d.ts.map +1 -0
- package/dist/commands/device.js +86 -0
- package/dist/commands/doctor.d.ts +18 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +320 -0
- package/dist/commands/help.d.ts +2 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +118 -0
- package/dist/commands/i18n.d.ts +14 -0
- package/dist/commands/i18n.d.ts.map +1 -0
- package/dist/commands/i18n.js +88 -0
- package/dist/commands/logs.d.ts +3 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +51 -0
- package/dist/commands/metadata.d.ts +3 -0
- package/dist/commands/metadata.d.ts.map +1 -0
- package/dist/commands/metadata.js +45 -0
- package/dist/commands/ota.d.ts +28 -0
- package/dist/commands/ota.d.ts.map +1 -0
- package/dist/commands/ota.js +203 -0
- package/dist/commands/project.d.ts +14 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +122 -0
- package/dist/commands/remote-build.d.ts +35 -0
- package/dist/commands/remote-build.d.ts.map +1 -0
- package/dist/commands/remote-build.js +73 -0
- package/dist/commands/submit.d.ts +27 -0
- package/dist/commands/submit.d.ts.map +1 -0
- package/dist/commands/submit.js +92 -0
- package/dist/ecosystem.d.ts +9 -0
- package/dist/ecosystem.d.ts.map +1 -0
- package/dist/ecosystem.js +95 -0
- package/dist/frameworks.d.ts.map +1 -1
- package/dist/frameworks.js +5 -1
- package/dist/guidance/catalog.d.ts +3 -0
- package/dist/guidance/catalog.d.ts.map +1 -0
- package/dist/guidance/catalog.js +504 -0
- package/dist/guidance/types.d.ts +10 -0
- package/dist/guidance/types.d.ts.map +1 -0
- package/dist/guidance/types.js +1 -0
- package/dist/guidance.d.ts +2 -9
- package/dist/guidance.d.ts.map +1 -1
- package/dist/guidance.js +1 -502
- package/dist/i18n/discovery.d.ts +14 -0
- package/dist/i18n/discovery.d.ts.map +1 -0
- package/dist/i18n/discovery.js +106 -0
- package/dist/i18n/plan.d.ts +12 -0
- package/dist/i18n/plan.d.ts.map +1 -0
- package/dist/i18n/plan.js +95 -0
- package/dist/i18n/setup.d.ts +11 -0
- package/dist/i18n/setup.d.ts.map +1 -0
- package/dist/i18n/setup.js +47 -0
- package/dist/i18n/types.d.ts +17 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2069
- package/dist/ios/assets.d.ts +19 -0
- package/dist/ios/assets.d.ts.map +1 -0
- package/dist/ios/assets.js +128 -0
- package/dist/ios/production-build.d.ts +6 -0
- package/dist/ios/production-build.d.ts.map +1 -0
- package/dist/ios/production-build.js +200 -0
- package/dist/ios/project.d.ts +4 -0
- package/dist/ios/project.d.ts.map +1 -0
- package/dist/ios/project.js +43 -0
- package/dist/ios/simulator-build.d.ts +8 -0
- package/dist/ios/simulator-build.d.ts.map +1 -0
- package/dist/ios/simulator-build.js +188 -0
- package/dist/ios/simulator.d.ts +5 -0
- package/dist/ios/simulator.d.ts.map +1 -0
- package/dist/ios/simulator.js +60 -0
- package/dist/ios/types.d.ts +14 -0
- package/dist/ios/types.d.ts.map +1 -0
- package/dist/ios/types.js +1 -0
- package/dist/ios-build.d.ts +4 -37
- package/dist/ios-build.d.ts.map +1 -1
- package/dist/ios-build.js +3 -575
- package/dist/ios-host.d.ts.map +1 -1
- package/dist/ios-toolchain/inspection.d.ts +4 -0
- package/dist/ios-toolchain/inspection.d.ts.map +1 -0
- package/dist/ios-toolchain/inspection.js +211 -0
- package/dist/ios-toolchain/probes.d.ts +24 -0
- package/dist/ios-toolchain/probes.d.ts.map +1 -0
- package/dist/ios-toolchain/probes.js +127 -0
- package/dist/ios-toolchain/types.d.ts +17 -0
- package/dist/ios-toolchain/types.d.ts.map +1 -0
- package/dist/ios-toolchain/types.js +1 -0
- package/dist/ios-toolchain.d.ts +3 -18
- package/dist/ios-toolchain.d.ts.map +1 -1
- package/dist/ios-toolchain.js +1 -333
- package/dist/plugins/contracts.d.ts +28 -0
- package/dist/plugins/contracts.d.ts.map +1 -0
- package/dist/plugins/contracts.js +1 -0
- package/dist/plugins/discovery.d.ts +19 -0
- package/dist/plugins/discovery.d.ts.map +1 -0
- package/dist/plugins/discovery.js +250 -0
- package/dist/plugins/native-operations.d.ts +28 -0
- package/dist/plugins/native-operations.d.ts.map +1 -0
- package/dist/plugins/native-operations.js +217 -0
- package/dist/plugins/operations.d.ts +9 -0
- package/dist/plugins/operations.d.ts.map +1 -0
- package/dist/plugins/operations.js +76 -0
- package/dist/plugins/validation.d.ts +9 -0
- package/dist/plugins/validation.d.ts.map +1 -0
- package/dist/plugins/validation.js +52 -0
- package/dist/plugins.d.ts +3 -34
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +2 -595
- package/dist/process-runner.d.ts +4 -0
- package/dist/process-runner.d.ts.map +1 -1
- package/dist/process-runner.js +14 -0
- package/dist/remote.d.ts +22 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +141 -2
- package/dist/rspeedy-app-config.d.ts +4 -0
- package/dist/rspeedy-app-config.d.ts.map +1 -0
- package/dist/rspeedy-app-config.js +32 -0
- package/dist/runtime/args.d.ts +3 -0
- package/dist/runtime/args.d.ts.map +1 -0
- package/dist/runtime/args.js +11 -0
- package/dist/runtime/build-commands.d.ts +3 -0
- package/dist/runtime/build-commands.d.ts.map +1 -0
- package/dist/runtime/build-commands.js +282 -0
- package/dist/runtime/context.d.ts +63 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/context.js +179 -0
- package/dist/runtime/early-commands.d.ts +4 -0
- package/dist/runtime/early-commands.d.ts.map +1 -0
- package/dist/runtime/early-commands.js +284 -0
- package/dist/runtime/project.d.ts +8 -0
- package/dist/runtime/project.d.ts.map +1 -0
- package/dist/runtime/project.js +38 -0
- package/dist/runtime/state.d.ts +47 -0
- package/dist/runtime/state.d.ts.map +1 -0
- package/dist/runtime/state.js +33 -0
- package/dist/secure-store/backends.d.ts +20 -0
- package/dist/secure-store/backends.d.ts.map +1 -0
- package/dist/secure-store/backends.js +248 -0
- package/dist/secure-store/types.d.ts +27 -0
- package/dist/secure-store/types.d.ts.map +1 -0
- package/dist/secure-store/types.js +1 -0
- package/dist/secure-store.d.ts +2 -26
- package/dist/secure-store.d.ts.map +1 -1
- package/dist/secure-store.js +3 -245
- package/package.json +7 -4
- package/templates/android-host/app/build.gradle +1 -0
- package/templates/android-host/app/src/main/java/template/MainActivity.java +98 -3
- package/templates/ios-host/__IOS_TARGET_NAME__/DemoLynxProvider.swift +26 -16
- package/templates/ios-host/__IOS_TARGET_NAME__/ViewController.swift +107 -19
package/dist/ios-toolchain.js
CHANGED
|
@@ -1,336 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
4
|
-
import { captureProcess, commandExists, } from "./process-runner.js";
|
|
5
|
-
function check(name, status, value, fix) {
|
|
6
|
-
return { name, status, ok: status !== "fail", value, fix };
|
|
7
|
-
}
|
|
8
|
-
function findHost(root, profile) {
|
|
9
|
-
const configured = profile.ios?.workspace ?? profile.ios?.project;
|
|
10
|
-
if (configured) {
|
|
11
|
-
const configuredPath = resolve(root, configured);
|
|
12
|
-
if (configuredPath.endsWith(".xcodeproj")) {
|
|
13
|
-
const workspace = configuredPath.replace(/\.xcodeproj$/, ".xcworkspace");
|
|
14
|
-
if (existsSync(workspace))
|
|
15
|
-
return workspace;
|
|
16
|
-
}
|
|
17
|
-
return configuredPath;
|
|
18
|
-
}
|
|
19
|
-
for (const directory of ["ios", "macos"]) {
|
|
20
|
-
try {
|
|
21
|
-
const candidate = readdirSync(join(root, directory)).find((name) => name.endsWith(".xcworkspace") || name.endsWith(".xcodeproj"));
|
|
22
|
-
if (candidate)
|
|
23
|
-
return join(root, directory, candidate);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
// A native host is optional until an iOS build is requested.
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
function projectFlag(path) {
|
|
32
|
-
return path.endsWith(".xcworkspace") ? "-workspace" : "-project";
|
|
33
|
-
}
|
|
34
|
-
function commandOutput(result) {
|
|
35
|
-
return `${result.stdout}\n${result.stderr}`.trim();
|
|
36
|
-
}
|
|
37
|
-
function setting(output, name) {
|
|
38
|
-
return output.match(new RegExp(`^${name}\\s*=\\s*(.+)$`, "m"))?.[1]?.trim();
|
|
39
|
-
}
|
|
40
|
-
function provisioningProfilesDirectory() {
|
|
41
|
-
return join(homedir(), "Library", "MobileDevice", "Provisioning Profiles");
|
|
42
|
-
}
|
|
43
|
-
function provisioningProfileFiles() {
|
|
44
|
-
try {
|
|
45
|
-
return readdirSync(provisioningProfilesDirectory())
|
|
46
|
-
.filter((name) => /\.(?:mobileprovision|provisionprofile)$/.test(name))
|
|
47
|
-
.map((name) => join(provisioningProfilesDirectory(), name));
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
function plistValue(content, key) {
|
|
54
|
-
return content.match(new RegExp(`<key>${key}</key>\\s*<(?:string|date)>([^<]+)</`))?.[1];
|
|
55
|
-
}
|
|
56
|
-
async function validProvisioningProfile(file, root, bundleIdentifier) {
|
|
57
|
-
if (!commandExists("security"))
|
|
58
|
-
return false;
|
|
59
|
-
try {
|
|
60
|
-
const decoded = await captureProcess("security", ["cms", "-D", "-i", file], { cwd: root });
|
|
61
|
-
if (decoded.code !== 0)
|
|
62
|
-
return false;
|
|
63
|
-
const content = commandOutput(decoded);
|
|
64
|
-
const expiration = plistValue(content, "ExpirationDate");
|
|
65
|
-
if (!expiration || Number.isNaN(Date.parse(expiration)))
|
|
66
|
-
return false;
|
|
67
|
-
if (Date.parse(expiration) <= Date.now())
|
|
68
|
-
return false;
|
|
69
|
-
if (!bundleIdentifier)
|
|
70
|
-
return true;
|
|
71
|
-
const applicationIdentifier = plistValue(content, "application-identifier");
|
|
72
|
-
if (!applicationIdentifier)
|
|
73
|
-
return false;
|
|
74
|
-
const profileBundle = applicationIdentifier.split(".").slice(1).join(".");
|
|
75
|
-
return profileBundle === bundleIdentifier || profileBundle === "*";
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async function hasValidProvisioningProfile(root, bundleIdentifier) {
|
|
82
|
-
const files = provisioningProfileFiles();
|
|
83
|
-
for (const file of files) {
|
|
84
|
-
if (await validProvisioningProfile(file, root, bundleIdentifier))
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
async function probeProject(root, context, profile, target) {
|
|
90
|
-
if (!context.path || !context.scheme || !commandExists("xcodebuild"))
|
|
91
|
-
return context;
|
|
92
|
-
const configuration = profile.ios?.configuration ??
|
|
93
|
-
(target === "simulator" ? "Debug" : "Release");
|
|
94
|
-
const settings = await captureProcess("xcodebuild", [
|
|
95
|
-
context.flag,
|
|
96
|
-
context.path,
|
|
97
|
-
"-scheme",
|
|
98
|
-
context.scheme,
|
|
99
|
-
"-configuration",
|
|
100
|
-
configuration,
|
|
101
|
-
"-showBuildSettings",
|
|
102
|
-
], { cwd: root });
|
|
103
|
-
return { ...context, settings };
|
|
104
|
-
}
|
|
105
|
-
async function probeVersion(command, args, cwd) {
|
|
106
|
-
if (!commandExists(command))
|
|
107
|
-
return undefined;
|
|
108
|
-
try {
|
|
109
|
-
const result = await captureProcess(command, args, { cwd });
|
|
110
|
-
if (result.code !== 0)
|
|
111
|
-
return undefined;
|
|
112
|
-
return commandOutput(result).split(/\r?\n/)[0]?.trim();
|
|
113
|
-
}
|
|
114
|
-
catch {
|
|
115
|
-
return undefined;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
async function probeCommand(command, args, cwd) {
|
|
119
|
-
if (!commandExists(command))
|
|
120
|
-
return false;
|
|
121
|
-
try {
|
|
122
|
-
return (await captureProcess(command, args, { cwd })).code === 0;
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
export async function inspectIosToolchain(root, profile, target = "device") {
|
|
129
|
-
const checks = [];
|
|
130
|
-
const isMac = platform() === "darwin";
|
|
131
|
-
const host = findHost(root, profile);
|
|
132
|
-
const hostExists = Boolean(host && existsSync(host));
|
|
133
|
-
const scheme = profile.ios?.scheme;
|
|
134
|
-
const simulator = target === "simulator";
|
|
135
|
-
const exportOptions = profile.ios?.exportOptionsPlist
|
|
136
|
-
? resolve(root, profile.ios.exportOptionsPlist)
|
|
137
|
-
: undefined;
|
|
138
|
-
checks.push(check("ios-platform", isMac ? "pass" : "fail", isMac
|
|
139
|
-
? "macOS"
|
|
140
|
-
: `${platform()} · macOS is required for an iOS ${simulator ? "Simulator app" : "IPA"}`, isMac
|
|
141
|
-
? undefined
|
|
142
|
-
: "Run the iOS build on a macOS machine or macOS CI runner"));
|
|
143
|
-
if (!isMac) {
|
|
144
|
-
checks.push(check("ios-host", "warn", hostExists
|
|
145
|
-
? "host found but not inspectable on this OS"
|
|
146
|
-
: "not inspected"));
|
|
147
|
-
return {
|
|
148
|
-
ok: checks.every((item) => item.status !== "fail"),
|
|
149
|
-
checks,
|
|
150
|
-
project: host,
|
|
151
|
-
scheme,
|
|
152
|
-
automaticSigning: false,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
const developerDirectory = await probeVersion("xcode-select", ["-p"], root);
|
|
156
|
-
checks.push(check("xcode-select", developerDirectory ? "pass" : "fail", developerDirectory ?? "no active Xcode developer directory", developerDirectory
|
|
157
|
-
? undefined
|
|
158
|
-
: "sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer"));
|
|
159
|
-
const xcodeVersion = await probeVersion("xcodebuild", ["-version"], root);
|
|
160
|
-
checks.push(check("xcode", xcodeVersion ? "pass" : "fail", xcodeVersion ?? "xcodebuild unavailable", xcodeVersion
|
|
161
|
-
? undefined
|
|
162
|
-
: "Install Xcode, open it once, accept its license, then run xcode-select --install if needed"));
|
|
163
|
-
checks.push(check("xcrun", commandExists("xcrun") ? "pass" : "fail", commandExists("xcrun") ? "available" : "missing", commandExists("xcrun")
|
|
164
|
-
? undefined
|
|
165
|
-
: "Install Xcode and select its command-line tools"));
|
|
166
|
-
checks.push(check("codesign", commandExists("codesign") ? "pass" : "fail", commandExists("codesign") ? "available" : "missing", commandExists("codesign")
|
|
167
|
-
? undefined
|
|
168
|
-
: "Install Xcode command-line tools"));
|
|
169
|
-
checks.push(check("unzip", commandExists("unzip") ? "pass" : "fail", commandExists("unzip") ? "available" : "missing", commandExists("unzip")
|
|
170
|
-
? undefined
|
|
171
|
-
: "Install the macOS command-line tools"));
|
|
172
|
-
checks.push(check("ios-host", hostExists ? "pass" : "fail", hostExists ? host : "Xcode project or workspace not found", hostExists
|
|
173
|
-
? undefined
|
|
174
|
-
: "lynxship ios host init --bundle-identifier com.example.myapp"));
|
|
175
|
-
checks.push(check("ios-scheme", scheme ? "pass" : "fail", scheme ?? "missing from the selected build profile", scheme ? undefined : "Set ios.scheme in the selected build profile"));
|
|
176
|
-
const context = await probeProject(root, {
|
|
177
|
-
path: hostExists ? host : undefined,
|
|
178
|
-
flag: host ? projectFlag(host) : "-project",
|
|
179
|
-
scheme,
|
|
180
|
-
}, profile, target);
|
|
181
|
-
const settingsOutput = context.settings
|
|
182
|
-
? commandOutput(context.settings)
|
|
183
|
-
: "";
|
|
184
|
-
checks.push(check("xcode-project-settings", !context.settings
|
|
185
|
-
? "warn"
|
|
186
|
-
: context.settings.code === 0
|
|
187
|
-
? "pass"
|
|
188
|
-
: "fail", !context.settings
|
|
189
|
-
? "not inspected until host and scheme are configured"
|
|
190
|
-
: context.settings.code === 0
|
|
191
|
-
? "scheme and configuration load successfully"
|
|
192
|
-
: "xcodebuild could not load the selected scheme", context.settings?.code === 0 || !context.settings
|
|
193
|
-
? undefined
|
|
194
|
-
: "Open the project in Xcode and verify the scheme is shared and buildable"));
|
|
195
|
-
const automaticSigning = setting(settingsOutput, "CODE_SIGN_STYLE") === "Automatic";
|
|
196
|
-
const team = setting(settingsOutput, "DEVELOPMENT_TEAM");
|
|
197
|
-
checks.push(check("apple-team", simulator
|
|
198
|
-
? "pass"
|
|
199
|
-
: context.settings?.code !== 0 || !context.settings
|
|
200
|
-
? "warn"
|
|
201
|
-
: team
|
|
202
|
-
? "pass"
|
|
203
|
-
: "fail", simulator
|
|
204
|
-
? "not required for iOS Simulator"
|
|
205
|
-
: (team ?? "team not resolved from Xcode build settings"), simulator || team
|
|
206
|
-
? undefined
|
|
207
|
-
: "Select the Apple Developer Team in Xcode Signing & Capabilities"));
|
|
208
|
-
const distribution = profile.distribution ?? profile.ios?.distribution ?? "store";
|
|
209
|
-
const identityResult = simulator
|
|
210
|
-
? undefined
|
|
211
|
-
: await (commandExists("security")
|
|
212
|
-
? captureProcess("security", ["find-identity", "-v", "-p", "codesigning"], {
|
|
213
|
-
cwd: root,
|
|
214
|
-
})
|
|
215
|
-
: undefined);
|
|
216
|
-
const identityOutput = identityResult ? commandOutput(identityResult) : "";
|
|
217
|
-
const identityCount = Number(identityOutput.match(/(\d+) valid identities found/)?.[1] ?? 0);
|
|
218
|
-
const requiredIdentity = distribution === "store" ? "Apple Distribution" : "Apple Development";
|
|
219
|
-
checks.push(check("apple-signing-identity", simulator
|
|
220
|
-
? "pass"
|
|
221
|
-
: identityCount === 0 || !identityOutput.includes(requiredIdentity)
|
|
222
|
-
? "fail"
|
|
223
|
-
: "pass", simulator
|
|
224
|
-
? "not required for iOS Simulator"
|
|
225
|
-
: identityCount === 0
|
|
226
|
-
? "no valid code-signing identity found"
|
|
227
|
-
: identityOutput.includes(requiredIdentity)
|
|
228
|
-
? `${requiredIdentity} identity available`
|
|
229
|
-
: `${requiredIdentity} identity not found`, simulator ||
|
|
230
|
-
(identityCount > 0 && identityOutput.includes(requiredIdentity))
|
|
231
|
-
? undefined
|
|
232
|
-
: "Install or select the Apple certificate in Xcode Settings > Accounts, then rerun doctor"));
|
|
233
|
-
const podfile = join(root, "ios", "Podfile");
|
|
234
|
-
const podRequired = existsSync(podfile);
|
|
235
|
-
const podVersion = podRequired
|
|
236
|
-
? await probeVersion("pod", ["--version"], root)
|
|
237
|
-
: undefined;
|
|
238
|
-
checks.push(check("cocoapods", !podRequired || podVersion ? "pass" : "fail", !podRequired
|
|
239
|
-
? "not required · no ios/Podfile"
|
|
240
|
-
: (podVersion ?? "Podfile found but pod is unavailable"), !podRequired || podVersion
|
|
241
|
-
? undefined
|
|
242
|
-
: "gem install cocoapods, then run pod install in the project"));
|
|
243
|
-
let exportOptionsContent;
|
|
244
|
-
if (exportOptions && existsSync(exportOptions)) {
|
|
245
|
-
try {
|
|
246
|
-
exportOptionsContent = readFileSync(exportOptions, "utf8");
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
249
|
-
exportOptionsContent = undefined;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
const exportMethod = exportOptionsContent?.match(/<key>method<\/key>\s*<string>([^<]+)<\/string>/)?.[1];
|
|
253
|
-
const supportedExportMethods = new Set([
|
|
254
|
-
"app-store",
|
|
255
|
-
"ad-hoc",
|
|
256
|
-
"development",
|
|
257
|
-
"enterprise",
|
|
258
|
-
]);
|
|
259
|
-
checks.push(check("export-options", simulator
|
|
260
|
-
? "pass"
|
|
261
|
-
: !exportOptions
|
|
262
|
-
? "fail"
|
|
263
|
-
: !existsSync(exportOptions)
|
|
264
|
-
? "fail"
|
|
265
|
-
: !exportMethod || !supportedExportMethods.has(exportMethod)
|
|
266
|
-
? "fail"
|
|
267
|
-
: "pass", simulator
|
|
268
|
-
? "not required for iOS Simulator"
|
|
269
|
-
: !exportOptions
|
|
270
|
-
? "missing from the production build profile"
|
|
271
|
-
: !existsSync(exportOptions)
|
|
272
|
-
? `${exportOptions} not found`
|
|
273
|
-
: exportMethod && supportedExportMethods.has(exportMethod)
|
|
274
|
-
? `method: ${exportMethod}`
|
|
275
|
-
: "method missing or invalid for an iOS export", simulator ||
|
|
276
|
-
(exportOptions &&
|
|
277
|
-
existsSync(exportOptions) &&
|
|
278
|
-
exportMethod &&
|
|
279
|
-
supportedExportMethods.has(exportMethod))
|
|
280
|
-
? undefined
|
|
281
|
-
: "Set build.production.ios.exportOptionsPlist to a valid ExportOptions.plist"));
|
|
282
|
-
const manualProvisioning = setting(settingsOutput, "CODE_SIGN_STYLE") === "Manual" ||
|
|
283
|
-
exportOptionsContent?.includes("<key>provisioningProfiles</key>") === true;
|
|
284
|
-
const profilesAvailable = await hasValidProvisioningProfile(root, setting(settingsOutput, "PRODUCT_BUNDLE_IDENTIFIER"));
|
|
285
|
-
checks.push(check("ios-provisioning", simulator
|
|
286
|
-
? "pass"
|
|
287
|
-
: manualProvisioning && !profilesAvailable
|
|
288
|
-
? "fail"
|
|
289
|
-
: automaticSigning && !profilesAvailable
|
|
290
|
-
? "warn"
|
|
291
|
-
: "pass", simulator
|
|
292
|
-
? "not required for iOS Simulator"
|
|
293
|
-
: manualProvisioning && !profilesAvailable
|
|
294
|
-
? "manual signing selected but no local provisioning profile was found"
|
|
295
|
-
: automaticSigning && !profilesAvailable
|
|
296
|
-
? "Xcode-managed provisioning will be requested during the build"
|
|
297
|
-
: profilesAvailable
|
|
298
|
-
? "local provisioning profile available"
|
|
299
|
-
: "not required by the detected signing mode", simulator || !(manualProvisioning && !profilesAvailable)
|
|
300
|
-
? undefined
|
|
301
|
-
: "Download a matching .mobileprovision/.provisionprofile from Apple Developer and install it in Xcode"));
|
|
302
|
-
const simctlAvailable = await probeCommand("xcrun", ["--find", "simctl"], root);
|
|
303
|
-
const devicectlAvailable = await probeCommand("xcrun", ["--find", "devicectl"], root);
|
|
304
|
-
checks.push(check("ios-device-tools", simctlAvailable || devicectlAvailable ? "pass" : "warn", simctlAvailable && devicectlAvailable
|
|
305
|
-
? "simctl and devicectl available"
|
|
306
|
-
: simctlAvailable
|
|
307
|
-
? "simctl available · devicectl unavailable"
|
|
308
|
-
: devicectlAvailable
|
|
309
|
-
? "devicectl available · simctl unavailable"
|
|
310
|
-
: "simctl and devicectl unavailable", simctlAvailable || devicectlAvailable
|
|
311
|
-
? undefined
|
|
312
|
-
: "Install the full Xcode toolchain, then rerun lynxship doctor --platform ios"));
|
|
313
|
-
if (simulator) {
|
|
314
|
-
const runtimeResult = await (commandExists("xcrun")
|
|
315
|
-
? captureProcess("xcrun", ["simctl", "list", "runtimes"], { cwd: root })
|
|
316
|
-
: undefined);
|
|
317
|
-
const runtimeOutput = runtimeResult ? commandOutput(runtimeResult) : "";
|
|
318
|
-
const hasRuntime = /iOS\s+[0-9]/.test(runtimeOutput);
|
|
319
|
-
checks.push(check("ios-simulator-runtime", hasRuntime ? "pass" : "fail", hasRuntime
|
|
320
|
-
? "iOS Simulator runtime available"
|
|
321
|
-
: "no iOS Simulator runtime found", hasRuntime
|
|
322
|
-
? undefined
|
|
323
|
-
: "Open Xcode > Settings > Components and install an iOS Simulator runtime"));
|
|
324
|
-
}
|
|
325
|
-
const failures = checks.filter((item) => item.status === "fail");
|
|
326
|
-
return {
|
|
327
|
-
ok: failures.length === 0,
|
|
328
|
-
checks,
|
|
329
|
-
project: host,
|
|
330
|
-
scheme,
|
|
331
|
-
automaticSigning,
|
|
332
|
-
};
|
|
333
|
-
}
|
|
1
|
+
export { inspectIosToolchain } from "./ios-toolchain/inspection.js";
|
|
334
2
|
export function formatIosToolchainFailure(report) {
|
|
335
3
|
return report.checks
|
|
336
4
|
.filter((item) => item.status === "fail")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AutolinkContribution, BuildContribution, CloudIntegrationContribution, PluginCapability, PluginPermission, PluginPlanChange, TemplateContribution } from "@lynxship/plugin-api";
|
|
2
|
+
import type { LynxShipConfig } from "../config.js";
|
|
3
|
+
export interface ProjectPluginInfo {
|
|
4
|
+
name: string;
|
|
5
|
+
version?: string;
|
|
6
|
+
packagePath?: string;
|
|
7
|
+
entry?: string;
|
|
8
|
+
apiVersion?: number;
|
|
9
|
+
capabilities: PluginCapability[];
|
|
10
|
+
permissions: PluginPermission[];
|
|
11
|
+
status: "ready" | "missing" | "invalid";
|
|
12
|
+
reason: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PluginReport {
|
|
15
|
+
configured: number;
|
|
16
|
+
plugins: ProjectPluginInfo[];
|
|
17
|
+
}
|
|
18
|
+
export interface PluginApplication {
|
|
19
|
+
config: LynxShipConfig;
|
|
20
|
+
report: PluginReport;
|
|
21
|
+
applied: string[];
|
|
22
|
+
templates: TemplateContribution[];
|
|
23
|
+
cloud: CloudIntegrationContribution[];
|
|
24
|
+
build: BuildContribution[];
|
|
25
|
+
changes: PluginPlanChange[];
|
|
26
|
+
autolink: AutolinkContribution[];
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=contracts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../src/plugins/contracts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,EAC5B,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,KAAK,EAAE,4BAA4B,EAAE,CAAC;IACtC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CAClC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Platform } from "@lynxship/contracts";
|
|
2
|
+
import { type JsonObject, type LynxShipPluginDefinition, type PluginPlatform, type PluginReference } from "@lynxship/plugin-api";
|
|
3
|
+
import type { LynxShipConfig } from "../config.js";
|
|
4
|
+
import type { ProjectPluginInfo, PluginReport } from "./contracts.js";
|
|
5
|
+
export interface ResolvedPlugin {
|
|
6
|
+
info: ProjectPluginInfo;
|
|
7
|
+
definition?: LynxShipPluginDefinition;
|
|
8
|
+
}
|
|
9
|
+
export declare function pluginReference(reference: PluginReference): {
|
|
10
|
+
name: string;
|
|
11
|
+
options: JsonObject;
|
|
12
|
+
};
|
|
13
|
+
export declare function resolvePlugin(root: string, reference: PluginReference, load?: boolean): Promise<ResolvedPlugin>;
|
|
14
|
+
export declare function pluginReferences(config: LynxShipConfig): PluginReference[];
|
|
15
|
+
export declare function inspectProjectPlugins(root: string, config: LynxShipConfig): Promise<PluginReport>;
|
|
16
|
+
export declare function appliesToPlatform(platforms: readonly PluginPlatform[] | undefined, platform: Platform): boolean;
|
|
17
|
+
export declare function mergeJson(left: JsonObject, right: JsonObject): JsonObject;
|
|
18
|
+
export declare function safeProjectFile(root: string, file: string): string;
|
|
19
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/plugins/discovery.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,KAAK,UAAU,EACf,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAuBtE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,UAAU,CAAC,EAAE,wBAAwB,CAAC;CACvC;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,eAAe,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,CAAC;CACrB,CAGA;AA8FD,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,eAAe,EAC1B,IAAI,UAAO,GACV,OAAO,CAAC,cAAc,CAAC,CAgIzB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,EAAE,CAI1E;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,YAAY,CAAC,CAQvB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,cAAc,EAAE,GAAG,SAAS,EAChD,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAOT;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,UAAU,CAkBzE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAYlE"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { capabilities, isKnownList, LYNXSHIP_PLUGIN_API_VERSION, KNOWN_CAPABILITIES, KNOWN_PERMISSIONS, permissions, validPluginDefinition, } from "./validation.js";
|
|
6
|
+
export function pluginReference(reference) {
|
|
7
|
+
if (typeof reference === "string")
|
|
8
|
+
return { name: reference, options: {} };
|
|
9
|
+
return { name: reference[0], options: reference[1] };
|
|
10
|
+
}
|
|
11
|
+
function packageDirectory(entry) {
|
|
12
|
+
let current = dirname(entry);
|
|
13
|
+
while (true) {
|
|
14
|
+
if (existsSync(join(current, "package.json")))
|
|
15
|
+
return current;
|
|
16
|
+
const parent = dirname(current);
|
|
17
|
+
if (parent === current)
|
|
18
|
+
return dirname(entry);
|
|
19
|
+
current = parent;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function parsePackageJson(file) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function packageJsonFromEntry(entry) {
|
|
31
|
+
return join(packageDirectory(entry), "package.json");
|
|
32
|
+
}
|
|
33
|
+
function findInstalledPackageJson(root, name) {
|
|
34
|
+
let current = root;
|
|
35
|
+
while (true) {
|
|
36
|
+
const candidate = join(current, "node_modules", ...name.split("/"), "package.json");
|
|
37
|
+
if (existsSync(candidate))
|
|
38
|
+
return candidate;
|
|
39
|
+
const parent = dirname(current);
|
|
40
|
+
if (parent === current)
|
|
41
|
+
return undefined;
|
|
42
|
+
current = parent;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function resolvePackageEntry(root, name) {
|
|
46
|
+
try {
|
|
47
|
+
const requireFromProject = createRequire(join(root, "package.json"));
|
|
48
|
+
let entry;
|
|
49
|
+
let packagePath;
|
|
50
|
+
try {
|
|
51
|
+
entry = requireFromProject.resolve(name);
|
|
52
|
+
packagePath = packageJsonFromEntry(entry);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
packagePath = findInstalledPackageJson(root, name) ?? "";
|
|
56
|
+
if (!packagePath)
|
|
57
|
+
throw new Error("Cannot find module '" + name + "'");
|
|
58
|
+
entry = packagePath;
|
|
59
|
+
}
|
|
60
|
+
const packageJson = parsePackageJson(packagePath);
|
|
61
|
+
const plugin = packageJson.lynxship?.plugin;
|
|
62
|
+
if (typeof plugin !== "string" || !plugin)
|
|
63
|
+
return {
|
|
64
|
+
packagePath,
|
|
65
|
+
reason: `${name} does not declare package.json#lynxship.plugin`,
|
|
66
|
+
};
|
|
67
|
+
const packageRoot = dirname(packagePath);
|
|
68
|
+
const resolved = resolve(packageRoot, plugin);
|
|
69
|
+
const outside = relative(packageRoot, resolved).startsWith("..");
|
|
70
|
+
if (outside)
|
|
71
|
+
return {
|
|
72
|
+
packagePath,
|
|
73
|
+
reason: `${name} points its plugin entry outside the package`,
|
|
74
|
+
};
|
|
75
|
+
if (!existsSync(resolved))
|
|
76
|
+
return {
|
|
77
|
+
packagePath,
|
|
78
|
+
reason: `${name} plugin entry does not exist: ${plugin}`,
|
|
79
|
+
};
|
|
80
|
+
return { packagePath, entry: resolved };
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
return {
|
|
84
|
+
reason: error instanceof Error
|
|
85
|
+
? `Cannot resolve ${name}: ${error.message}`
|
|
86
|
+
: `Cannot resolve ${name}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export async function resolvePlugin(root, reference, load = true) {
|
|
91
|
+
const requested = pluginReference(reference);
|
|
92
|
+
const resolved = resolvePackageEntry(root, requested.name);
|
|
93
|
+
if (!resolved.packagePath || !resolved.entry) {
|
|
94
|
+
return {
|
|
95
|
+
info: {
|
|
96
|
+
name: requested.name,
|
|
97
|
+
capabilities: [],
|
|
98
|
+
permissions: [],
|
|
99
|
+
status: "missing",
|
|
100
|
+
reason: resolved.reason ?? "Plugin package was not found",
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const packageJson = parsePackageJson(resolved.packagePath);
|
|
105
|
+
const metadata = packageJson.lynxship;
|
|
106
|
+
const apiVersion = typeof metadata?.apiVersion === "number" ? metadata.apiVersion : undefined;
|
|
107
|
+
const packageCapabilities = capabilities(metadata?.capabilities);
|
|
108
|
+
const packagePermissions = permissions(metadata?.permissions);
|
|
109
|
+
if (!isKnownList(metadata?.capabilities, KNOWN_CAPABILITIES) ||
|
|
110
|
+
!isKnownList(metadata?.permissions, KNOWN_PERMISSIONS)) {
|
|
111
|
+
return {
|
|
112
|
+
info: {
|
|
113
|
+
name: requested.name,
|
|
114
|
+
version: typeof packageJson.version === "string"
|
|
115
|
+
? packageJson.version
|
|
116
|
+
: undefined,
|
|
117
|
+
packagePath: resolved.packagePath,
|
|
118
|
+
entry: resolved.entry,
|
|
119
|
+
apiVersion,
|
|
120
|
+
capabilities: packageCapabilities,
|
|
121
|
+
permissions: packagePermissions,
|
|
122
|
+
status: "invalid",
|
|
123
|
+
reason: "Plugin package capabilities and permissions must be arrays of known values",
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (apiVersion !== LYNXSHIP_PLUGIN_API_VERSION) {
|
|
128
|
+
return {
|
|
129
|
+
info: {
|
|
130
|
+
name: requested.name,
|
|
131
|
+
version: typeof packageJson.version === "string"
|
|
132
|
+
? packageJson.version
|
|
133
|
+
: undefined,
|
|
134
|
+
packagePath: resolved.packagePath,
|
|
135
|
+
entry: resolved.entry,
|
|
136
|
+
apiVersion,
|
|
137
|
+
capabilities: packageCapabilities,
|
|
138
|
+
permissions: packagePermissions,
|
|
139
|
+
status: "invalid",
|
|
140
|
+
reason: `Plugin API ${String(apiVersion ?? "missing")} is not supported; expected ${LYNXSHIP_PLUGIN_API_VERSION}`,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const info = {
|
|
145
|
+
name: requested.name,
|
|
146
|
+
version: typeof packageJson.version === "string" ? packageJson.version : undefined,
|
|
147
|
+
packagePath: resolved.packagePath,
|
|
148
|
+
entry: resolved.entry,
|
|
149
|
+
apiVersion,
|
|
150
|
+
capabilities: packageCapabilities,
|
|
151
|
+
permissions: packagePermissions,
|
|
152
|
+
status: "ready",
|
|
153
|
+
reason: "Plugin manifest is valid",
|
|
154
|
+
};
|
|
155
|
+
if (!load)
|
|
156
|
+
return { info };
|
|
157
|
+
try {
|
|
158
|
+
const module = (await import(pathToFileURL(resolved.entry).href));
|
|
159
|
+
const definition = module.default ?? module.plugin;
|
|
160
|
+
if (!validPluginDefinition(definition)) {
|
|
161
|
+
return {
|
|
162
|
+
info: {
|
|
163
|
+
...info,
|
|
164
|
+
status: "invalid",
|
|
165
|
+
reason: "Plugin entry must export a LynxShipPluginDefinition",
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (capabilities(definition.capabilities).join(",") !==
|
|
170
|
+
packageCapabilities.join(",") ||
|
|
171
|
+
permissions(definition.permissions).join(",") !==
|
|
172
|
+
packagePermissions.join(",")) {
|
|
173
|
+
return {
|
|
174
|
+
info: {
|
|
175
|
+
...info,
|
|
176
|
+
status: "invalid",
|
|
177
|
+
reason: "Plugin code capabilities/permissions do not match package.json",
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (definition.name !== requested.name &&
|
|
182
|
+
definition.name !== packageJson.name) {
|
|
183
|
+
return {
|
|
184
|
+
info: {
|
|
185
|
+
...info,
|
|
186
|
+
status: "invalid",
|
|
187
|
+
reason: `Plugin entry name '${definition.name}' does not match its package`,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return { info, definition };
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return {
|
|
195
|
+
info: {
|
|
196
|
+
...info,
|
|
197
|
+
status: "invalid",
|
|
198
|
+
reason: error instanceof Error
|
|
199
|
+
? `Plugin entry failed to load: ${error.message}`
|
|
200
|
+
: "Plugin entry failed to load",
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export function pluginReferences(config) {
|
|
206
|
+
const value = config.plugins;
|
|
207
|
+
if (!Array.isArray(value))
|
|
208
|
+
return [];
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
export async function inspectProjectPlugins(root, config) {
|
|
212
|
+
const references = pluginReferences(config);
|
|
213
|
+
const plugins = await Promise.all(references.map(async (reference) => (await resolvePlugin(root, reference, false)).info));
|
|
214
|
+
return { configured: references.length, plugins };
|
|
215
|
+
}
|
|
216
|
+
export function appliesToPlatform(platforms, platform) {
|
|
217
|
+
return (!platforms ||
|
|
218
|
+
platforms.length === 0 ||
|
|
219
|
+
platforms.includes("all") ||
|
|
220
|
+
platforms.includes(platform));
|
|
221
|
+
}
|
|
222
|
+
export function mergeJson(left, right) {
|
|
223
|
+
const result = { ...left };
|
|
224
|
+
for (const [key, value] of Object.entries(right)) {
|
|
225
|
+
const current = result[key];
|
|
226
|
+
if (current &&
|
|
227
|
+
typeof current === "object" &&
|
|
228
|
+
!Array.isArray(current) &&
|
|
229
|
+
value &&
|
|
230
|
+
typeof value === "object" &&
|
|
231
|
+
!Array.isArray(value)) {
|
|
232
|
+
result[key] = mergeJson(current, value);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
result[key] = value;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
export function safeProjectFile(root, file) {
|
|
241
|
+
if (isAbsolute(file))
|
|
242
|
+
throw new Error(`Plugin native file must be relative: ${file}`);
|
|
243
|
+
const target = resolve(root, file);
|
|
244
|
+
const projectRelative = relative(root, target);
|
|
245
|
+
if (!projectRelative ||
|
|
246
|
+
projectRelative.startsWith("..") ||
|
|
247
|
+
isAbsolute(projectRelative))
|
|
248
|
+
throw new Error(`Plugin native file escapes the project: ${file}`);
|
|
249
|
+
return target;
|
|
250
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Platform } from "@lynxship/contracts";
|
|
2
|
+
import { type BuildContribution, type CloudIntegrationContribution, type JsonObject, type LynxShipPluginDefinition, type NativeFileOperation, type AutolinkContribution, type PluginPermission, type PluginPlanChange, type TemplateContribution } from "@lynxship/plugin-api";
|
|
3
|
+
export interface OwnedNativeOperation {
|
|
4
|
+
plugin: string;
|
|
5
|
+
operation: NativeFileOperation;
|
|
6
|
+
}
|
|
7
|
+
declare function permissionForContribution(contribution: {
|
|
8
|
+
config?: JsonObject;
|
|
9
|
+
build?: BuildContribution;
|
|
10
|
+
native?: readonly NativeFileOperation[];
|
|
11
|
+
autolink?: AutolinkContribution;
|
|
12
|
+
templates?: readonly TemplateContribution[];
|
|
13
|
+
cloud?: readonly CloudIntegrationContribution[];
|
|
14
|
+
}): PluginPermission[];
|
|
15
|
+
export declare function assertPluginContribution(plugin: LynxShipPluginDefinition, contribution: unknown): asserts contribution is {
|
|
16
|
+
config?: JsonObject;
|
|
17
|
+
build?: BuildContribution;
|
|
18
|
+
native?: readonly NativeFileOperation[];
|
|
19
|
+
autolink?: AutolinkContribution;
|
|
20
|
+
templates?: readonly TemplateContribution[];
|
|
21
|
+
cloud?: readonly CloudIntegrationContribution[];
|
|
22
|
+
};
|
|
23
|
+
export declare function assertPluginPermissions(plugin: LynxShipPluginDefinition, contribution: Parameters<typeof permissionForContribution>[0]): void;
|
|
24
|
+
export declare function inspectNativeOperation(root: string, owned: OwnedNativeOperation, platform: Platform): Promise<PluginPlanChange | undefined>;
|
|
25
|
+
export declare function assertNativeConflicts(root: string, operations: readonly OwnedNativeOperation[]): void;
|
|
26
|
+
export declare function applyNativeOperationsAtomically(root: string, operations: readonly OwnedNativeOperation[], platform: Platform): Promise<PluginPlanChange[]>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=native-operations.d.ts.map
|