@simplysm/sd-cli 13.0.69 → 13.0.71
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 +10 -957
- package/dist/builders/BaseBuilder.d.ts +23 -23
- package/dist/builders/BaseBuilder.d.ts.map +1 -1
- package/dist/builders/BaseBuilder.js +15 -15
- package/dist/builders/DtsBuilder.d.ts +4 -4
- package/dist/builders/DtsBuilder.js +1 -1
- package/dist/builders/LibraryBuilder.d.ts +3 -3
- package/dist/builders/types.d.ts +10 -10
- package/dist/capacitor/capacitor.d.ts +36 -36
- package/dist/capacitor/capacitor.js +63 -63
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/add-client.d.ts +8 -8
- package/dist/commands/add-client.js +15 -15
- package/dist/commands/add-client.js.map +1 -1
- package/dist/commands/add-server.d.ts +9 -9
- package/dist/commands/add-server.js +13 -13
- package/dist/commands/add-server.js.map +1 -1
- package/dist/commands/build.d.ts +9 -9
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/dev.d.ts +9 -9
- package/dist/commands/device.d.ts +9 -9
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +17 -17
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/init.d.ts +6 -6
- package/dist/commands/init.js +12 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +23 -23
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +25 -25
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts +13 -13
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +61 -61
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/replace-deps.d.ts +3 -3
- package/dist/commands/replace-deps.d.ts.map +1 -1
- package/dist/commands/replace-deps.js +1 -1
- package/dist/commands/replace-deps.js.map +1 -1
- package/dist/commands/typecheck.d.ts +20 -20
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +20 -20
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/commands/watch.d.ts +7 -7
- package/dist/electron/electron.d.ts +27 -27
- package/dist/electron/electron.js +32 -32
- package/dist/electron/electron.js.map +1 -1
- package/dist/infra/ResultCollector.d.ts +9 -9
- package/dist/infra/ResultCollector.js +5 -5
- package/dist/infra/SignalHandler.d.ts +7 -7
- package/dist/infra/SignalHandler.js +4 -4
- package/dist/infra/WorkerManager.d.ts +14 -14
- package/dist/infra/WorkerManager.js +11 -11
- package/dist/orchestrators/BuildOrchestrator.d.ts +19 -19
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +26 -26
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevOrchestrator.d.ts +25 -25
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js +30 -30
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/orchestrators/WatchOrchestrator.d.ts +13 -13
- package/dist/orchestrators/WatchOrchestrator.js +17 -17
- package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +2 -2
- package/dist/sd-cli-entry.js +38 -38
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.d.ts +2 -2
- package/dist/sd-cli.js +1 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/sd-config.types.d.ts +84 -84
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/build-env.d.ts +1 -1
- package/dist/utils/config-editor.d.ts +5 -5
- package/dist/utils/config-editor.js +2 -2
- package/dist/utils/config-editor.js.map +1 -1
- package/dist/utils/copy-public.d.ts +9 -9
- package/dist/utils/copy-src.d.ts +9 -9
- package/dist/utils/esbuild-config.d.ts +30 -30
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/output-utils.d.ts +6 -6
- package/dist/utils/package-utils.d.ts +6 -6
- package/dist/utils/package-utils.js +1 -1
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/rebuild-manager.js +3 -3
- package/dist/utils/rebuild-manager.js.map +1 -1
- package/dist/utils/replace-deps.d.ts +25 -25
- package/dist/utils/replace-deps.js +3 -3
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/sd-config.d.ts +3 -3
- package/dist/utils/sd-config.js +3 -3
- package/dist/utils/sd-config.js.map +1 -1
- package/dist/utils/tailwind-config-deps.d.ts +3 -3
- package/dist/utils/template.d.ts +8 -8
- package/dist/utils/tsconfig.d.ts +16 -16
- package/dist/utils/tsconfig.js +2 -2
- package/dist/utils/tsconfig.js.map +1 -1
- package/dist/utils/typecheck-serialization.d.ts +8 -8
- package/dist/utils/vite-config.d.ts +8 -8
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +3 -3
- package/dist/utils/worker-events.d.ts +12 -12
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-utils.d.ts +3 -3
- package/dist/utils/worker-utils.js +2 -2
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts +14 -14
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +1 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/dts.worker.d.ts +13 -13
- package/dist/workers/dts.worker.d.ts.map +1 -1
- package/dist/workers/dts.worker.js +3 -3
- package/dist/workers/dts.worker.js.map +1 -1
- package/dist/workers/library.worker.d.ts +12 -12
- package/dist/workers/library.worker.js +1 -1
- package/dist/workers/library.worker.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +1 -1
- package/dist/workers/server-runtime.worker.d.ts +6 -6
- package/dist/workers/server-runtime.worker.js +6 -6
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/dist/workers/server.worker.d.ts +20 -20
- package/dist/workers/server.worker.d.ts.map +1 -1
- package/dist/workers/server.worker.js +6 -6
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +8 -7
- package/src/builders/BaseBuilder.ts +33 -33
- package/src/builders/DtsBuilder.ts +5 -5
- package/src/builders/LibraryBuilder.ts +9 -9
- package/src/builders/types.ts +10 -10
- package/src/capacitor/capacitor.ts +119 -119
- package/src/commands/add-client.ts +31 -31
- package/src/commands/add-server.ts +34 -34
- package/src/commands/build.ts +9 -9
- package/src/commands/check.ts +5 -5
- package/src/commands/dev.ts +9 -9
- package/src/commands/device.ts +30 -30
- package/src/commands/init.ts +25 -25
- package/src/commands/lint.ts +64 -64
- package/src/commands/publish.ts +139 -139
- package/src/commands/replace-deps.ts +4 -4
- package/src/commands/typecheck.ts +74 -74
- package/src/commands/watch.ts +7 -7
- package/src/electron/electron.ts +51 -51
- package/src/infra/ResultCollector.ts +9 -9
- package/src/infra/SignalHandler.ts +7 -7
- package/src/infra/WorkerManager.ts +14 -14
- package/src/orchestrators/BuildOrchestrator.ts +76 -76
- package/src/orchestrators/DevOrchestrator.ts +88 -88
- package/src/orchestrators/WatchOrchestrator.ts +39 -39
- package/src/sd-cli-entry.ts +43 -43
- package/src/sd-cli.ts +15 -15
- package/src/sd-config.types.ts +85 -85
- package/src/utils/build-env.ts +1 -1
- package/src/utils/config-editor.ts +19 -19
- package/src/utils/copy-public.ts +17 -17
- package/src/utils/copy-src.ts +11 -11
- package/src/utils/esbuild-config.ts +33 -33
- package/src/utils/output-utils.ts +11 -11
- package/src/utils/package-utils.ts +12 -12
- package/src/utils/rebuild-manager.ts +3 -3
- package/src/utils/replace-deps.ts +361 -361
- package/src/utils/sd-config.ts +44 -44
- package/src/utils/tailwind-config-deps.ts +98 -98
- package/src/utils/template.ts +56 -56
- package/src/utils/tsconfig.ts +127 -127
- package/src/utils/typecheck-serialization.ts +86 -86
- package/src/utils/vite-config.ts +341 -341
- package/src/utils/worker-events.ts +16 -16
- package/src/utils/worker-utils.ts +45 -45
- package/src/workers/client.worker.ts +34 -34
- package/src/workers/dts.worker.ts +467 -467
- package/src/workers/library.worker.ts +314 -314
- package/src/workers/lint.worker.ts +16 -16
- package/src/workers/server-runtime.worker.ts +157 -157
- package/src/workers/server.worker.ts +572 -572
- package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
- package/templates/add-server/__SERVER__/package.json.hbs +2 -2
- package/templates/init/package.json.hbs +3 -3
- package/tests/config-editor.spec.ts +160 -0
- package/tests/copy-src.spec.ts +50 -0
- package/tests/get-compiler-options-for-package.spec.ts +139 -0
- package/tests/get-package-source-files.spec.ts +181 -0
- package/tests/get-types-from-package-json.spec.ts +107 -0
- package/tests/infra/ResultCollector.spec.ts +39 -0
- package/tests/infra/SignalHandler.spec.ts +38 -0
- package/tests/infra/WorkerManager.spec.ts +97 -0
- package/tests/load-ignore-patterns.spec.ts +188 -0
- package/tests/load-sd-config.spec.ts +137 -0
- package/tests/package-utils.spec.ts +188 -0
- package/tests/parse-root-tsconfig.spec.ts +89 -0
- package/tests/replace-deps.spec.ts +308 -0
- package/tests/run-lint.spec.ts +415 -0
- package/tests/run-typecheck.spec.ts +653 -0
- package/tests/run-watch.spec.ts +75 -0
- package/tests/sd-cli.spec.ts +330 -0
- package/tests/tailwind-config-deps.spec.ts +30 -0
- package/tests/template.spec.ts +70 -0
- package/tests/utils/rebuild-manager.spec.ts +43 -0
- package/tests/write-changed-output-files.spec.ts +97 -0
|
@@ -17,7 +17,7 @@ import type { SdCapacitorConfig } from "../sd-config.types";
|
|
|
17
17
|
import { execa } from "execa";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* package.json
|
|
20
|
+
* package.json type
|
|
21
21
|
*/
|
|
22
22
|
interface NpmConfig {
|
|
23
23
|
name: string;
|
|
@@ -29,7 +29,7 @@ interface NpmConfig {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* Configuration validation error
|
|
33
33
|
*/
|
|
34
34
|
class CapacitorConfigError extends Error {
|
|
35
35
|
constructor(message: string) {
|
|
@@ -39,11 +39,11 @@ class CapacitorConfigError extends Error {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Capacitor
|
|
42
|
+
* Capacitor project management class
|
|
43
43
|
*
|
|
44
|
-
* - Capacitor
|
|
45
|
-
* - Android APK/AAB
|
|
46
|
-
* -
|
|
44
|
+
* - Initialize Capacitor project
|
|
45
|
+
* - Build Android APK/AAB
|
|
46
|
+
* - Run app on device
|
|
47
47
|
*/
|
|
48
48
|
export class Capacitor {
|
|
49
49
|
private static readonly _ANDROID_KEYSTORE_FILE_NAME = "android.keystore";
|
|
@@ -65,10 +65,10 @@ export class Capacitor {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Capacitor
|
|
68
|
+
* Create Capacitor instance (with configuration validation)
|
|
69
69
|
*/
|
|
70
70
|
static async create(pkgPath: string, config: SdCapacitorConfig): Promise<Capacitor> {
|
|
71
|
-
// F5:
|
|
71
|
+
// F5: validate runtime configuration
|
|
72
72
|
Capacitor._validateConfig(config);
|
|
73
73
|
|
|
74
74
|
const npmConfig = await fsReadJson<NpmConfig>(path.resolve(pkgPath, "package.json"));
|
|
@@ -76,48 +76,48 @@ export class Capacitor {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
* F5:
|
|
79
|
+
* F5: Validate configuration
|
|
80
80
|
*/
|
|
81
81
|
private static _validateConfig(config: SdCapacitorConfig): void {
|
|
82
82
|
if (typeof config.appId !== "string" || config.appId.trim() === "") {
|
|
83
|
-
throw new CapacitorConfigError("capacitor.appId
|
|
83
|
+
throw new CapacitorConfigError("capacitor.appId is required.");
|
|
84
84
|
}
|
|
85
85
|
if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/i.test(config.appId)) {
|
|
86
|
-
throw new CapacitorConfigError(`capacitor.appId
|
|
86
|
+
throw new CapacitorConfigError(`capacitor.appId format is invalid: ${config.appId}`);
|
|
87
87
|
}
|
|
88
88
|
if (typeof config.appName !== "string" || config.appName.trim() === "") {
|
|
89
|
-
throw new CapacitorConfigError("capacitor.appName
|
|
89
|
+
throw new CapacitorConfigError("capacitor.appName is required.");
|
|
90
90
|
}
|
|
91
91
|
if (config.platform != null) {
|
|
92
92
|
const platforms = Object.keys(config.platform);
|
|
93
93
|
for (const p of platforms) {
|
|
94
94
|
if (p !== "android") {
|
|
95
|
-
throw new CapacitorConfigError(
|
|
95
|
+
throw new CapacitorConfigError(`unsupported platform: ${p} (currently only android is supported)`);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
*
|
|
102
|
+
* Execute command (with logging)
|
|
103
103
|
*/
|
|
104
104
|
private async _exec(cmd: string, args: string[], cwd: string): Promise<string> {
|
|
105
|
-
Capacitor._logger.debug(
|
|
105
|
+
Capacitor._logger.debug(`executed command: ${cmd} ${args.join(" ")}`);
|
|
106
106
|
const { stdout: result } = await execa(cmd, args, { cwd });
|
|
107
|
-
Capacitor._logger.debug(
|
|
107
|
+
Capacitor._logger.debug(`execution result: ${result}`);
|
|
108
108
|
return result;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
* F10:
|
|
112
|
+
* F10: Acquire lock to prevent concurrent execution
|
|
113
113
|
*/
|
|
114
114
|
private async _acquireLock(): Promise<void> {
|
|
115
115
|
const lockPath = path.resolve(this._capPath, Capacitor._LOCK_FILE_NAME);
|
|
116
116
|
if (await fsExists(lockPath)) {
|
|
117
117
|
const lockContent = await fsRead(lockPath);
|
|
118
118
|
throw new Error(
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
`Another Capacitor operation is in progress (PID: ${lockContent}). ` +
|
|
120
|
+
`If there's an issue, delete the ${lockPath} file.`,
|
|
121
121
|
);
|
|
122
122
|
}
|
|
123
123
|
await fsMkdir(this._capPath);
|
|
@@ -125,7 +125,7 @@ export class Capacitor {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
* F10:
|
|
128
|
+
* F10: Release lock
|
|
129
129
|
*/
|
|
130
130
|
private async _releaseLock(): Promise<void> {
|
|
131
131
|
const lockPath = path.resolve(this._capPath, Capacitor._LOCK_FILE_NAME);
|
|
@@ -133,65 +133,65 @@ export class Capacitor {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
* F4:
|
|
136
|
+
* F4: Validate external tools
|
|
137
137
|
*/
|
|
138
138
|
private async _validateTools(): Promise<void> {
|
|
139
|
-
// Android SDK
|
|
139
|
+
// Check Android SDK
|
|
140
140
|
const sdkPath = await this._findAndroidSdk();
|
|
141
141
|
if (sdkPath == null) {
|
|
142
142
|
throw new Error(
|
|
143
|
-
"Android SDK
|
|
144
|
-
"1. Android Studio
|
|
145
|
-
"2. ANDROID_HOME
|
|
143
|
+
"Android SDK not found.\n" +
|
|
144
|
+
"1. Install Android Studio or\n" +
|
|
145
|
+
"2. Set ANDROID_HOME or ANDROID_SDK_ROOT environment variable.",
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
// Java
|
|
149
|
+
// Check Java (only for android platform)
|
|
150
150
|
if (this._platforms.includes("android")) {
|
|
151
151
|
const javaPath = await this._findJava21();
|
|
152
152
|
if (javaPath == null) {
|
|
153
153
|
Capacitor._logger.warn(
|
|
154
|
-
"Java 21
|
|
154
|
+
"Java 21 not found. Gradle may use embedded JDK or the build may fail.",
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
|
-
* Capacitor
|
|
161
|
+
* Initialize Capacitor project
|
|
162
162
|
*
|
|
163
|
-
* 1. package.json
|
|
164
|
-
* 2. capacitor.config.ts
|
|
165
|
-
* 3.
|
|
166
|
-
* 4.
|
|
167
|
-
* 5. Android
|
|
168
|
-
* 6. cap sync
|
|
163
|
+
* 1. Create package.json and install dependencies
|
|
164
|
+
* 2. Create capacitor.config.ts
|
|
165
|
+
* 3. Add platform (android)
|
|
166
|
+
* 4. Set up icon
|
|
167
|
+
* 5. Configure Android native settings
|
|
168
|
+
* 6. Run cap sync or cap copy
|
|
169
169
|
*/
|
|
170
170
|
async initialize(): Promise<void> {
|
|
171
171
|
await this._acquireLock();
|
|
172
172
|
|
|
173
173
|
try {
|
|
174
|
-
// F4:
|
|
174
|
+
// F4: Validate external tools
|
|
175
175
|
await this._validateTools();
|
|
176
176
|
|
|
177
|
-
// 1. Capacitor
|
|
177
|
+
// 1. Initialize Capacitor project
|
|
178
178
|
const changed = await this._initCap();
|
|
179
179
|
|
|
180
|
-
// 2. Capacitor
|
|
180
|
+
// 2. Create Capacitor config file
|
|
181
181
|
await this._writeCapConf();
|
|
182
182
|
|
|
183
|
-
// 3.
|
|
183
|
+
// 3. Manage platform (F12: idempotent - skip if already exists)
|
|
184
184
|
await this._addPlatforms();
|
|
185
185
|
|
|
186
|
-
// 4.
|
|
186
|
+
// 4. Set up icon (F6: error recovery)
|
|
187
187
|
await this._setupIcon();
|
|
188
188
|
|
|
189
|
-
// 5. Android
|
|
189
|
+
// 5. Configure Android native settings
|
|
190
190
|
if (this._platforms.includes("android")) {
|
|
191
191
|
await this._configureAndroid();
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
// 6.
|
|
194
|
+
// 6. Synchronize web assets
|
|
195
195
|
if (changed) {
|
|
196
196
|
await this._exec("npx", ["cap", "sync"], this._capPath);
|
|
197
197
|
} else {
|
|
@@ -203,7 +203,7 @@ export class Capacitor {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
|
-
* Android APK/AAB
|
|
206
|
+
* Build Android APK/AAB
|
|
207
207
|
*/
|
|
208
208
|
async build(outPath: string): Promise<void> {
|
|
209
209
|
await this._acquireLock();
|
|
@@ -217,7 +217,7 @@ export class Capacitor {
|
|
|
217
217
|
if (platform === "android") {
|
|
218
218
|
await this._buildAndroid(outPath, buildType);
|
|
219
219
|
} else {
|
|
220
|
-
throw new Error(
|
|
220
|
+
throw new Error(`unsupported platform: ${platform}`);
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
} finally {
|
|
@@ -226,10 +226,10 @@ export class Capacitor {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
/**
|
|
229
|
-
*
|
|
229
|
+
* Run app on device (connect WebView to development server)
|
|
230
230
|
*/
|
|
231
231
|
async runOnDevice(url?: string): Promise<void> {
|
|
232
|
-
// F11: URL
|
|
232
|
+
// F11: Validate URL
|
|
233
233
|
if (url != null) {
|
|
234
234
|
this._validateUrl(url);
|
|
235
235
|
await this._updateServerUrl(url);
|
|
@@ -245,7 +245,7 @@ export class Capacitor {
|
|
|
245
245
|
try {
|
|
246
246
|
await this._exec("adb", ["kill-server"], this._capPath);
|
|
247
247
|
} catch {
|
|
248
|
-
// adb kill-server
|
|
248
|
+
// adb kill-server failure is ignored
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
throw err;
|
|
@@ -254,26 +254,26 @@ export class Capacitor {
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
/**
|
|
257
|
-
* F11: URL
|
|
257
|
+
* F11: Validate URL
|
|
258
258
|
*/
|
|
259
259
|
private _validateUrl(url: string): void {
|
|
260
260
|
try {
|
|
261
261
|
const parsed = new URL(url);
|
|
262
262
|
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
263
|
-
throw new Error(
|
|
263
|
+
throw new Error(`unsupported protocol: ${parsed.protocol}`);
|
|
264
264
|
}
|
|
265
265
|
} catch (err) {
|
|
266
266
|
if (err instanceof TypeError) {
|
|
267
|
-
throw new Error(
|
|
267
|
+
throw new Error(`invalid URL: ${url}`);
|
|
268
268
|
}
|
|
269
269
|
throw err;
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
//#region Private -
|
|
273
|
+
//#region Private - Initialization
|
|
274
274
|
|
|
275
275
|
/**
|
|
276
|
-
* Capacitor
|
|
276
|
+
* Basic Capacitor project initialization (package.json, npm install, cap init)
|
|
277
277
|
*/
|
|
278
278
|
private async _initCap(): Promise<boolean> {
|
|
279
279
|
const depChanged = await this._setupNpmConf();
|
|
@@ -281,9 +281,9 @@ export class Capacitor {
|
|
|
281
281
|
|
|
282
282
|
// pnpm install
|
|
283
283
|
const installResult = await this._exec("pnpm", ["install"], this._capPath);
|
|
284
|
-
Capacitor._logger.debug(`pnpm install
|
|
284
|
+
Capacitor._logger.debug(`pnpm install completed: ${installResult}`);
|
|
285
285
|
|
|
286
|
-
// F12: cap init
|
|
286
|
+
// F12: cap init idempotency - execute only when capacitor.config.ts does not exist
|
|
287
287
|
const configPath = path.resolve(this._capPath, "capacitor.config.ts");
|
|
288
288
|
if (!(await fsExists(configPath))) {
|
|
289
289
|
await this._exec(
|
|
@@ -293,7 +293,7 @@ export class Capacitor {
|
|
|
293
293
|
);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
//
|
|
296
|
+
// Create default www/index.html
|
|
297
297
|
const wwwPath = path.resolve(this._capPath, "www");
|
|
298
298
|
await fsMkdir(wwwPath);
|
|
299
299
|
await fsWrite(
|
|
@@ -305,14 +305,14 @@ export class Capacitor {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
/**
|
|
308
|
-
* package.json
|
|
308
|
+
* Configure package.json
|
|
309
309
|
*/
|
|
310
310
|
private async _setupNpmConf(): Promise<boolean> {
|
|
311
311
|
const projNpmConfigPath = path.resolve(this._pkgPath, "../../package.json");
|
|
312
312
|
|
|
313
|
-
// F3:
|
|
313
|
+
// F3: Check if file exists
|
|
314
314
|
if (!(await fsExists(projNpmConfigPath))) {
|
|
315
|
-
throw new Error(
|
|
315
|
+
throw new Error(`root package.json not found: ${projNpmConfigPath}`);
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
const projNpmConfig = await fsReadJson<NpmConfig>(projNpmConfigPath);
|
|
@@ -329,7 +329,7 @@ export class Capacitor {
|
|
|
329
329
|
capNpmConf.volta = projNpmConfig.volta;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
//
|
|
332
|
+
// Default dependencies
|
|
333
333
|
capNpmConf.dependencies = capNpmConf.dependencies ?? {};
|
|
334
334
|
capNpmConf.dependencies["@capacitor/core"] = "^7.0.0";
|
|
335
335
|
capNpmConf.dependencies["@capacitor/app"] = "^7.0.0";
|
|
@@ -341,7 +341,7 @@ export class Capacitor {
|
|
|
341
341
|
capNpmConf.devDependencies["@capacitor/cli"] = "^7.0.0";
|
|
342
342
|
capNpmConf.devDependencies["@capacitor/assets"] = "^3.0.0";
|
|
343
343
|
|
|
344
|
-
//
|
|
344
|
+
// Configure plugin packages
|
|
345
345
|
const mainDeps = {
|
|
346
346
|
...this._npmConfig.dependencies,
|
|
347
347
|
...this._npmConfig.devDependencies,
|
|
@@ -357,28 +357,28 @@ export class Capacitor {
|
|
|
357
357
|
),
|
|
358
358
|
);
|
|
359
359
|
|
|
360
|
-
//
|
|
360
|
+
// Remove unused plugins
|
|
361
361
|
for (const prevPlugin of prevPlugins) {
|
|
362
362
|
if (!usePlugins.includes(prevPlugin)) {
|
|
363
363
|
delete capNpmConf.dependencies[prevPlugin];
|
|
364
|
-
Capacitor._logger.debug(
|
|
364
|
+
Capacitor._logger.debug(`plugin removed: ${prevPlugin}`);
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
//
|
|
368
|
+
// Add new plugins
|
|
369
369
|
for (const plugin of usePlugins) {
|
|
370
370
|
if (!(plugin in capNpmConf.dependencies)) {
|
|
371
371
|
const version = mainDeps[plugin] ?? "*";
|
|
372
372
|
capNpmConf.dependencies[plugin] = version;
|
|
373
|
-
Capacitor._logger.debug(
|
|
373
|
+
Capacitor._logger.debug(`plugin added: ${plugin}@${version}`);
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
-
//
|
|
377
|
+
// Save
|
|
378
378
|
await fsMkdir(this._capPath);
|
|
379
379
|
await fsWriteJson(capNpmConfPath, capNpmConf, { space: 2 });
|
|
380
380
|
|
|
381
|
-
//
|
|
381
|
+
// Check if dependencies changed
|
|
382
382
|
const isChanged =
|
|
383
383
|
orgCapNpmConf.volta !== capNpmConf.volta ||
|
|
384
384
|
JSON.stringify(orgCapNpmConf.dependencies) !== JSON.stringify(capNpmConf.dependencies) ||
|
|
@@ -388,12 +388,12 @@ export class Capacitor {
|
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
/**
|
|
391
|
-
* capacitor.config.ts
|
|
391
|
+
* Create capacitor.config.ts
|
|
392
392
|
*/
|
|
393
393
|
private async _writeCapConf(): Promise<void> {
|
|
394
394
|
const confPath = path.resolve(this._capPath, "capacitor.config.ts");
|
|
395
395
|
|
|
396
|
-
//
|
|
396
|
+
// Create plugin options
|
|
397
397
|
const pluginOptions: Record<string, Record<string, unknown>> = {};
|
|
398
398
|
for (const [pluginName, options] of Object.entries(this._config.plugins ?? {})) {
|
|
399
399
|
if (options !== true) {
|
|
@@ -427,13 +427,13 @@ export default config;
|
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
/**
|
|
430
|
-
*
|
|
430
|
+
* Add platform (F12: ensure idempotency)
|
|
431
431
|
*/
|
|
432
432
|
private async _addPlatforms(): Promise<void> {
|
|
433
433
|
for (const platform of this._platforms) {
|
|
434
434
|
const platformPath = path.resolve(this._capPath, platform);
|
|
435
435
|
if (await fsExists(platformPath)) {
|
|
436
|
-
Capacitor._logger.debug(
|
|
436
|
+
Capacitor._logger.debug(`platform already exists: ${platform}`);
|
|
437
437
|
continue;
|
|
438
438
|
}
|
|
439
439
|
|
|
@@ -442,7 +442,7 @@ export default config;
|
|
|
442
442
|
}
|
|
443
443
|
|
|
444
444
|
/**
|
|
445
|
-
*
|
|
445
|
+
* Set up icon (F6: error recovery)
|
|
446
446
|
*/
|
|
447
447
|
private async _setupIcon(): Promise<void> {
|
|
448
448
|
const assetsDirPath = path.resolve(this._capPath, "assets");
|
|
@@ -450,10 +450,10 @@ export default config;
|
|
|
450
450
|
if (this._config.icon != null) {
|
|
451
451
|
const iconSource = path.resolve(this._pkgPath, this._config.icon);
|
|
452
452
|
|
|
453
|
-
// F6:
|
|
453
|
+
// F6: Check if source icon exists
|
|
454
454
|
if (!(await fsExists(iconSource))) {
|
|
455
455
|
Capacitor._logger.warn(
|
|
456
|
-
|
|
456
|
+
`icon file not found: ${iconSource}. Using default icon.`,
|
|
457
457
|
);
|
|
458
458
|
return;
|
|
459
459
|
}
|
|
@@ -461,13 +461,13 @@ export default config;
|
|
|
461
461
|
try {
|
|
462
462
|
await fsMkdir(assetsDirPath);
|
|
463
463
|
|
|
464
|
-
//
|
|
464
|
+
// Create icon
|
|
465
465
|
const logoPath = path.resolve(assetsDirPath, "logo.png");
|
|
466
466
|
|
|
467
467
|
const logoSize = Math.floor(1024 * 0.6);
|
|
468
468
|
const padding = Math.floor((1024 - logoSize) / 2);
|
|
469
469
|
|
|
470
|
-
// F6: sharp
|
|
470
|
+
// F6: Handle sharp errors
|
|
471
471
|
await sharp(iconSource)
|
|
472
472
|
.resize(logoSize, logoSize, {
|
|
473
473
|
fit: "contain",
|
|
@@ -496,9 +496,9 @@ export default config;
|
|
|
496
496
|
);
|
|
497
497
|
} catch (err) {
|
|
498
498
|
Capacitor._logger.warn(
|
|
499
|
-
|
|
499
|
+
`icon generation failed: ${err instanceof Error ? err.message : err}. Using default icon.`,
|
|
500
500
|
);
|
|
501
|
-
// F6:
|
|
501
|
+
// F6: Continue even if it fails (use default icon)
|
|
502
502
|
}
|
|
503
503
|
} else {
|
|
504
504
|
await fsRm(assetsDirPath);
|
|
@@ -507,17 +507,17 @@ export default config;
|
|
|
507
507
|
|
|
508
508
|
//#endregion
|
|
509
509
|
|
|
510
|
-
//#region Private - Android
|
|
510
|
+
//#region Private - Android Configuration
|
|
511
511
|
|
|
512
512
|
/**
|
|
513
|
-
* Android
|
|
513
|
+
* Configure Android native settings
|
|
514
514
|
*/
|
|
515
515
|
private async _configureAndroid(): Promise<void> {
|
|
516
516
|
const androidPath = path.resolve(this._capPath, "android");
|
|
517
517
|
|
|
518
|
-
// F3: Android
|
|
518
|
+
// F3: Check if Android directory exists
|
|
519
519
|
if (!(await fsExists(androidPath))) {
|
|
520
|
-
throw new Error(`Android
|
|
520
|
+
throw new Error(`Android project directory not found: ${androidPath}`);
|
|
521
521
|
}
|
|
522
522
|
|
|
523
523
|
await this._configureAndroidJavaHomePath(androidPath);
|
|
@@ -527,14 +527,14 @@ export default config;
|
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
/**
|
|
530
|
-
* JAVA_HOME
|
|
530
|
+
* Set up JAVA_HOME path (gradle.properties)
|
|
531
531
|
*/
|
|
532
532
|
private async _configureAndroidJavaHomePath(androidPath: string): Promise<void> {
|
|
533
533
|
const gradlePropsPath = path.resolve(androidPath, "gradle.properties");
|
|
534
534
|
|
|
535
|
-
// F3:
|
|
535
|
+
// F3: Check if file exists
|
|
536
536
|
if (!(await fsExists(gradlePropsPath))) {
|
|
537
|
-
Capacitor._logger.warn(`gradle.properties
|
|
537
|
+
Capacitor._logger.warn(`gradle.properties file not found: ${gradlePropsPath}`);
|
|
538
538
|
return;
|
|
539
539
|
}
|
|
540
540
|
|
|
@@ -542,7 +542,7 @@ export default config;
|
|
|
542
542
|
|
|
543
543
|
const java21Path = await this._findJava21();
|
|
544
544
|
if (java21Path != null && !content.includes("org.gradle.java.home")) {
|
|
545
|
-
// F9: Windows
|
|
545
|
+
// F9: Improved Windows path escaping
|
|
546
546
|
const escapedPath = java21Path.replace(/\\/g, "\\\\");
|
|
547
547
|
content += `\norg.gradle.java.home=${escapedPath}\n`;
|
|
548
548
|
await fsWrite(gradlePropsPath, content);
|
|
@@ -550,7 +550,7 @@ export default config;
|
|
|
550
550
|
}
|
|
551
551
|
|
|
552
552
|
/**
|
|
553
|
-
* Java 21
|
|
553
|
+
* Auto-detect Java 21 path
|
|
554
554
|
*/
|
|
555
555
|
private async _findJava21(): Promise<string | undefined> {
|
|
556
556
|
const patterns = [
|
|
@@ -573,26 +573,26 @@ export default config;
|
|
|
573
573
|
}
|
|
574
574
|
|
|
575
575
|
/**
|
|
576
|
-
* Android SDK
|
|
576
|
+
* Set up Android SDK path (local.properties)
|
|
577
577
|
*/
|
|
578
578
|
private async _configureAndroidSdkPath(androidPath: string): Promise<void> {
|
|
579
579
|
const localPropsPath = path.resolve(androidPath, "local.properties");
|
|
580
580
|
|
|
581
581
|
const sdkPath = await this._findAndroidSdk();
|
|
582
582
|
if (sdkPath != null) {
|
|
583
|
-
// F9:
|
|
583
|
+
// F9: Always use forward slash (Gradle compatible)
|
|
584
584
|
await fsWrite(localPropsPath, `sdk.dir=${sdkPath.replace(/\\/g, "/")}\n`);
|
|
585
585
|
} else {
|
|
586
586
|
throw new Error(
|
|
587
|
-
"Android SDK
|
|
588
|
-
"1. Android Studio
|
|
589
|
-
"2. ANDROID_HOME
|
|
587
|
+
"Android SDK not found.\n" +
|
|
588
|
+
"1. Install Android Studio or\n" +
|
|
589
|
+
"2. Set ANDROID_HOME or ANDROID_SDK_ROOT environment variable.",
|
|
590
590
|
);
|
|
591
591
|
}
|
|
592
592
|
}
|
|
593
593
|
|
|
594
594
|
/**
|
|
595
|
-
* Android SDK
|
|
595
|
+
* Search for Android SDK path
|
|
596
596
|
*/
|
|
597
597
|
private async _findAndroidSdk(): Promise<string | undefined> {
|
|
598
598
|
const fromEnv = (env["ANDROID_HOME"] ?? env["ANDROID_SDK_ROOT"]) as string | undefined;
|
|
@@ -617,24 +617,24 @@ export default config;
|
|
|
617
617
|
}
|
|
618
618
|
|
|
619
619
|
/**
|
|
620
|
-
* AndroidManifest.xml
|
|
620
|
+
* Modify AndroidManifest.xml (F3: add error handling)
|
|
621
621
|
*/
|
|
622
622
|
private async _configureAndroidManifest(androidPath: string): Promise<void> {
|
|
623
623
|
const manifestPath = path.resolve(androidPath, "app/src/main/AndroidManifest.xml");
|
|
624
624
|
|
|
625
|
-
// F3:
|
|
625
|
+
// F3: Check if file exists
|
|
626
626
|
if (!(await fsExists(manifestPath))) {
|
|
627
|
-
throw new Error(`AndroidManifest.xml
|
|
627
|
+
throw new Error(`AndroidManifest.xml file not found: ${manifestPath}`);
|
|
628
628
|
}
|
|
629
629
|
|
|
630
630
|
let content = await fsRead(manifestPath);
|
|
631
631
|
|
|
632
|
-
// usesCleartextTraffic
|
|
632
|
+
// Configure usesCleartextTraffic
|
|
633
633
|
if (!content.includes("android:usesCleartextTraffic")) {
|
|
634
634
|
content = content.replace("<application", '<application android:usesCleartextTraffic="true"');
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
-
//
|
|
637
|
+
// Configure additional permissions
|
|
638
638
|
const permissions = this._config.platform?.android?.permissions ?? [];
|
|
639
639
|
for (const perm of permissions) {
|
|
640
640
|
const permTag = `<uses-permission android:name="android.permission.${perm.name}"`;
|
|
@@ -655,7 +655,7 @@ export default config;
|
|
|
655
655
|
}
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
-
//
|
|
658
|
+
// Configure additional application settings
|
|
659
659
|
const appConfig = this._config.platform?.android?.config;
|
|
660
660
|
if (appConfig) {
|
|
661
661
|
for (const [key, value] of Object.entries(appConfig)) {
|
|
@@ -666,7 +666,7 @@ export default config;
|
|
|
666
666
|
}
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
-
// intentFilters
|
|
669
|
+
// Configure intentFilters
|
|
670
670
|
const intentFilters = this._config.platform?.android?.intentFilters ?? [];
|
|
671
671
|
for (const filter of intentFilters) {
|
|
672
672
|
const filterKey = filter.action ?? filter.category ?? "";
|
|
@@ -690,19 +690,19 @@ export default config;
|
|
|
690
690
|
}
|
|
691
691
|
|
|
692
692
|
/**
|
|
693
|
-
* build.gradle
|
|
693
|
+
* Modify build.gradle (F3: add error handling)
|
|
694
694
|
*/
|
|
695
695
|
private async _configureAndroidBuildGradle(androidPath: string): Promise<void> {
|
|
696
696
|
const buildGradlePath = path.resolve(androidPath, "app/build.gradle");
|
|
697
697
|
|
|
698
|
-
// F3:
|
|
698
|
+
// F3: Check if file exists
|
|
699
699
|
if (!(await fsExists(buildGradlePath))) {
|
|
700
|
-
throw new Error(`build.gradle
|
|
700
|
+
throw new Error(`build.gradle file not found: ${buildGradlePath}`);
|
|
701
701
|
}
|
|
702
702
|
|
|
703
703
|
let content = await fsRead(buildGradlePath);
|
|
704
704
|
|
|
705
|
-
// versionName
|
|
705
|
+
// Configure versionName and versionCode
|
|
706
706
|
const version = this._npmConfig.version;
|
|
707
707
|
const cleanVersion = version.replace(/-.*$/, "");
|
|
708
708
|
const versionParts = cleanVersion.split(".");
|
|
@@ -714,7 +714,7 @@ export default config;
|
|
|
714
714
|
content = content.replace(/versionCode \d+/, `versionCode ${versionCode}`);
|
|
715
715
|
content = content.replace(/versionName "[^"]+"/, `versionName "${version}"`);
|
|
716
716
|
|
|
717
|
-
// SDK
|
|
717
|
+
// Configure SDK version
|
|
718
718
|
if (this._config.platform?.android?.sdkVersion != null) {
|
|
719
719
|
const sdkVersion = this._config.platform.android.sdkVersion;
|
|
720
720
|
content = content.replace(/minSdkVersion .+/, `minSdkVersion ${sdkVersion}`);
|
|
@@ -727,18 +727,18 @@ export default config;
|
|
|
727
727
|
);
|
|
728
728
|
}
|
|
729
729
|
|
|
730
|
-
//
|
|
730
|
+
// Configure signing
|
|
731
731
|
const keystorePath = path.resolve(this._capPath, Capacitor._ANDROID_KEYSTORE_FILE_NAME);
|
|
732
732
|
const signConfig = this._config.platform?.android?.sign;
|
|
733
733
|
if (signConfig) {
|
|
734
734
|
const keystoreSource = path.resolve(this._pkgPath, signConfig.keystore);
|
|
735
|
-
// F3: keystore
|
|
735
|
+
// F3: Check if keystore file exists
|
|
736
736
|
if (!(await fsExists(keystoreSource))) {
|
|
737
|
-
throw new Error(`keystore
|
|
737
|
+
throw new Error(`keystore file not found: ${keystoreSource}`);
|
|
738
738
|
}
|
|
739
739
|
await fsCopy(keystoreSource, keystorePath);
|
|
740
740
|
|
|
741
|
-
// F9:
|
|
741
|
+
// F9: Convert relative path to forward slash
|
|
742
742
|
const keystoreRelativePath = path
|
|
743
743
|
.relative(path.dirname(buildGradlePath), keystorePath)
|
|
744
744
|
.replace(/\\/g, "/");
|
|
@@ -774,10 +774,10 @@ export default config;
|
|
|
774
774
|
|
|
775
775
|
//#endregion
|
|
776
776
|
|
|
777
|
-
//#region Private -
|
|
777
|
+
//#region Private - Build
|
|
778
778
|
|
|
779
779
|
/**
|
|
780
|
-
* Android
|
|
780
|
+
* Build Android
|
|
781
781
|
*/
|
|
782
782
|
private async _buildAndroid(outPath: string, buildType: string): Promise<void> {
|
|
783
783
|
const androidPath = path.resolve(this._capPath, "android");
|
|
@@ -787,20 +787,20 @@ export default config;
|
|
|
787
787
|
const gradleTask =
|
|
788
788
|
buildType === "release" ? (isBundle ? "bundleRelease" : "assembleRelease") : "assembleDebug";
|
|
789
789
|
|
|
790
|
-
// Gradle
|
|
791
|
-
// F9:
|
|
790
|
+
// Execute Gradle build (cross-platform)
|
|
791
|
+
// F9: Run via cmd.exe on Windows (because shell: false)
|
|
792
792
|
if (process.platform === "win32") {
|
|
793
793
|
await this._exec("cmd", ["/c", "gradlew.bat", gradleTask, "--no-daemon"], androidPath);
|
|
794
794
|
} else {
|
|
795
795
|
await this._exec("sh", ["./gradlew", gradleTask, "--no-daemon"], androidPath);
|
|
796
796
|
}
|
|
797
797
|
|
|
798
|
-
//
|
|
798
|
+
// Copy build output
|
|
799
799
|
await this._copyAndroidBuildOutput(androidPath, targetOutPath, buildType);
|
|
800
800
|
}
|
|
801
801
|
|
|
802
802
|
/**
|
|
803
|
-
* Android
|
|
803
|
+
* Copy Android build output
|
|
804
804
|
*/
|
|
805
805
|
private async _copyAndroidBuildOutput(
|
|
806
806
|
androidPath: string,
|
|
@@ -833,7 +833,7 @@ export default config;
|
|
|
833
833
|
);
|
|
834
834
|
|
|
835
835
|
if (!(await fsExists(actualPath))) {
|
|
836
|
-
Capacitor._logger.warn(
|
|
836
|
+
Capacitor._logger.warn(`build output not found: ${actualPath}`);
|
|
837
837
|
return;
|
|
838
838
|
}
|
|
839
839
|
|
|
@@ -842,7 +842,7 @@ export default config;
|
|
|
842
842
|
await fsMkdir(targetOutPath);
|
|
843
843
|
await fsCopy(actualPath, path.resolve(targetOutPath, outputFileName));
|
|
844
844
|
|
|
845
|
-
//
|
|
845
|
+
// Save per-version
|
|
846
846
|
const updatesPath = path.resolve(targetOutPath, "updates");
|
|
847
847
|
await fsMkdir(updatesPath);
|
|
848
848
|
await fsCopy(actualPath, path.resolve(updatesPath, `${this._npmConfig.version}.${ext}`));
|
|
@@ -850,10 +850,10 @@ export default config;
|
|
|
850
850
|
|
|
851
851
|
//#endregion
|
|
852
852
|
|
|
853
|
-
//#region Private -
|
|
853
|
+
//#region Private - Device Execution
|
|
854
854
|
|
|
855
855
|
/**
|
|
856
|
-
* capacitor.config.ts
|
|
856
|
+
* Update server.url in capacitor.config.ts
|
|
857
857
|
*/
|
|
858
858
|
private async _updateServerUrl(url: string): Promise<void> {
|
|
859
859
|
const configPath = path.resolve(this._capPath, "capacitor.config.ts");
|
|
@@ -873,10 +873,10 @@ export default config;
|
|
|
873
873
|
|
|
874
874
|
//#endregion
|
|
875
875
|
|
|
876
|
-
//#region Private -
|
|
876
|
+
//#region Private - Utilities
|
|
877
877
|
|
|
878
878
|
/**
|
|
879
|
-
*
|
|
879
|
+
* Convert string to PascalCase
|
|
880
880
|
*/
|
|
881
881
|
private _toPascalCase(str: string): string {
|
|
882
882
|
return str
|