@simplysm/sd-cli 14.0.18 → 14.0.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/dist/angular/vite-angular-plugin.d.ts +2 -0
- package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
- package/dist/angular/vite-angular-plugin.js +57 -28
- package/dist/angular/vite-angular-plugin.js.map +1 -1
- package/dist/angular/vite-postcss-inline-plugin.d.ts.map +1 -1
- package/dist/angular/vite-postcss-inline-plugin.js +4 -1
- package/dist/angular/vite-postcss-inline-plugin.js.map +1 -1
- package/dist/capacitor/capacitor-android.d.ts +16 -0
- package/dist/capacitor/capacitor-android.d.ts.map +1 -0
- package/dist/capacitor/capacitor-android.js +289 -0
- package/dist/capacitor/capacitor-android.js.map +1 -0
- package/dist/capacitor/capacitor.d.ts +0 -50
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +16 -281
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/check.js +2 -2
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +3 -2
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/lint.d.ts +1 -42
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +1 -151
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +2 -1
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/typecheck.d.ts +3 -40
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +3 -232
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +20 -8
- package/dist/electron/electron.js.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.d.ts +1 -0
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.js +16 -0
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
- package/dist/orchestrators/TypecheckOrchestrator.d.ts +74 -0
- package/dist/orchestrators/TypecheckOrchestrator.d.ts.map +1 -0
- package/dist/orchestrators/TypecheckOrchestrator.js +285 -0
- package/dist/orchestrators/TypecheckOrchestrator.js.map +1 -0
- package/dist/sd-cli.js +6 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/utils/lint-core.d.ts +43 -0
- package/dist/utils/lint-core.d.ts.map +1 -0
- package/dist/utils/lint-core.js +154 -0
- package/dist/utils/lint-core.js.map +1 -0
- package/dist/utils/lint-utils.d.ts +1 -1
- package/dist/utils/lint-utils.d.ts.map +1 -1
- package/dist/utils/server-production-files.d.ts +22 -0
- package/dist/utils/server-production-files.d.ts.map +1 -0
- package/dist/utils/server-production-files.js +162 -0
- package/dist/utils/server-production-files.js.map +1 -0
- package/dist/utils/vite-config.d.ts +1 -1
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +76 -26
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
- package/dist/utils/vite-scope-watch-plugin.js +7 -1
- package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +1 -1
- package/dist/workers/lint.worker.d.ts.map +1 -1
- package/dist/workers/lint.worker.js +1 -1
- package/dist/workers/lint.worker.js.map +1 -1
- package/dist/workers/server-build.worker.d.ts.map +1 -1
- package/dist/workers/server-build.worker.js +11 -161
- package/dist/workers/server-build.worker.js.map +1 -1
- package/dist/workers/server-runtime.worker.d.ts.map +1 -1
- package/dist/workers/server-runtime.worker.js +15 -0
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/package.json +9 -7
- package/src/angular/vite-angular-plugin.ts +88 -34
- package/src/angular/vite-postcss-inline-plugin.ts +5 -1
- package/src/capacitor/capacitor-android.ts +368 -0
- package/src/capacitor/capacitor.ts +18 -363
- package/src/commands/check.ts +2 -2
- package/src/commands/device.ts +3 -2
- package/src/commands/lint.ts +1 -201
- package/src/commands/publish.ts +2 -1
- package/src/commands/typecheck.ts +7 -292
- package/src/electron/electron.ts +15 -8
- package/src/orchestrators/DevWatchOrchestrator.ts +18 -0
- package/src/orchestrators/TypecheckOrchestrator.ts +364 -0
- package/src/sd-cli.ts +6 -1
- package/src/utils/lint-core.ts +205 -0
- package/src/utils/lint-utils.ts +1 -1
- package/src/utils/server-production-files.ts +186 -0
- package/src/utils/vite-config.ts +83 -27
- package/src/utils/vite-scope-watch-plugin.ts +6 -1
- package/src/workers/lint.worker.ts +1 -1
- package/src/workers/server-build.worker.ts +10 -185
- package/src/workers/server-runtime.worker.ts +15 -0
- package/tests/angular/linker-disk-cache.spec.ts +31 -25
- package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +15 -15
- package/tests/angular/vite-angular-plugin-hmr.spec.ts +9 -9
- package/tests/angular/vite-angular-plugin-legacy-watch.spec.ts +108 -0
- package/tests/angular/vite-angular-plugin-lint.spec.ts +4 -4
- package/tests/angular/vite-angular-plugin-scss-hmr.spec.ts +10 -15
- package/tests/angular/vite-angular-plugin.spec.ts +80 -15
- package/tests/angular/vite-postcss-inline-plugin.spec.ts +10 -0
- package/tests/capacitor/capacitor-android-exports.verify.md +11 -0
- package/tests/capacitor/capacitor-android.spec.ts +219 -0
- package/tests/capacitor/capacitor-build.spec.ts +17 -21
- package/tests/capacitor/capacitor-icon.spec.ts +17 -19
- package/tests/capacitor/capacitor-init.spec.ts +18 -14
- package/tests/capacitor/capacitor-run.spec.ts +10 -24
- package/tests/capacitor/capacitor-workspace.spec.ts +30 -25
- package/tests/commands/check.spec.ts +2 -2
- package/tests/commands/device.spec.ts +12 -7
- package/tests/commands/lint.spec.ts +33 -194
- package/tests/commands/publish-set.verify.md +7 -0
- package/tests/electron/electron-symlink-cleanup.verify.md +8 -0
- package/tests/electron/electron.spec.ts +27 -2
- package/tests/orchestrators/dist-delete-watcher.verify.md +10 -0
- package/tests/orchestrators/typecheck-orchestrator.spec.ts +180 -0
- package/tests/sd-cli-catch-all.verify.md +7 -0
- package/tests/utils/lint-core-import-paths.verify.md +10 -0
- package/tests/utils/lint-core.spec.ts +188 -0
- package/tests/utils/server-production-files-import-paths.verify.md +14 -0
- package/tests/utils/vite-config.spec.ts +255 -133
- package/tests/utils/vite-scope-watch-plugin.spec.ts +22 -0
- package/tests/workers/server-build-context-dispose.verify.md +8 -0
- package/tests/workers/server-runtime-worker.spec.ts +48 -4
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { symlink } from "fs/promises";
|
|
5
4
|
import { createRequire } from "module";
|
|
6
5
|
import { cpx, fsx, pathx } from "@simplysm/core-node";
|
|
7
|
-
import { env } from "@simplysm/core-common";
|
|
8
6
|
import { consola, LogLevels } from "consola";
|
|
9
7
|
import type { NpmConfig, SdCapacitorConfig } from "../sd-config.types.js";
|
|
8
|
+
import { configureAndroid, findAndroidSdk, findJava21 } from "./capacitor-android.js";
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* 설정 검증 에러
|
|
@@ -127,7 +126,7 @@ export class Capacitor {
|
|
|
127
126
|
// 5. Android 네이티브 설정 구성
|
|
128
127
|
if (this._platforms.includes("android")) {
|
|
129
128
|
Capacitor._logger.debug("Android 네이티브 설정 시작");
|
|
130
|
-
await this.
|
|
129
|
+
await configureAndroid(this._capPath, this._config, this._npmConfig);
|
|
131
130
|
Capacitor._logger.debug("Android 네이티브 설정 완료");
|
|
132
131
|
}
|
|
133
132
|
|
|
@@ -215,7 +214,7 @@ export class Capacitor {
|
|
|
215
214
|
*/
|
|
216
215
|
private async _validateTools(): Promise<void> {
|
|
217
216
|
// Android SDK 확인
|
|
218
|
-
const sdkPath = await
|
|
217
|
+
const sdkPath = await findAndroidSdk();
|
|
219
218
|
if (sdkPath == null) {
|
|
220
219
|
throw new Error(
|
|
221
220
|
"Android SDK를 찾을 수 없습니다.\n" +
|
|
@@ -227,7 +226,7 @@ export class Capacitor {
|
|
|
227
226
|
|
|
228
227
|
// Java 확인 (android 플랫폼인 경우에만)
|
|
229
228
|
if (this._platforms.includes("android")) {
|
|
230
|
-
const javaPath = await
|
|
229
|
+
const javaPath = await findJava21();
|
|
231
230
|
if (javaPath == null) {
|
|
232
231
|
Capacitor._logger.warn(
|
|
233
232
|
"Java 21을 찾을 수 없습니다. Gradle이 내장 JDK를 사용하거나 빌드가 실패할 수 있습니다.",
|
|
@@ -247,14 +246,12 @@ export class Capacitor {
|
|
|
247
246
|
*/
|
|
248
247
|
private async _initCap(): Promise<boolean> {
|
|
249
248
|
Capacitor._logger.debug("package.json 설정 시작");
|
|
250
|
-
const
|
|
249
|
+
const depChanged = await this._setupNpmConf();
|
|
251
250
|
const nodeModulesExists = await fsx.exists(pathx.posixResolve(this._capPath, "node_modules"));
|
|
252
251
|
Capacitor._logger.debug(`depChanged: ${depChanged}, nodeModulesExists: ${nodeModulesExists}`);
|
|
253
252
|
|
|
254
253
|
if (!depChanged && nodeModulesExists) {
|
|
255
|
-
|
|
256
|
-
Capacitor._logger.debug("의존성 변경 없음, workspace 플러그인 symlink만 갱신");
|
|
257
|
-
await this._linkWorkspacePlugins(workspacePlugins);
|
|
254
|
+
Capacitor._logger.debug("의존성 변경 없음");
|
|
258
255
|
return false;
|
|
259
256
|
}
|
|
260
257
|
|
|
@@ -270,11 +267,6 @@ export class Capacitor {
|
|
|
270
267
|
await this._exec("pnpm", ["approve-builds", "--all"], this._capPath);
|
|
271
268
|
Capacitor._logger.debug("pnpm install 완료");
|
|
272
269
|
|
|
273
|
-
// workspace 플러그인 symlink
|
|
274
|
-
Capacitor._logger.debug("workspace 플러그인 symlink 시작");
|
|
275
|
-
await this._linkWorkspacePlugins(workspacePlugins);
|
|
276
|
-
Capacitor._logger.debug("workspace 플러그인 symlink 완료");
|
|
277
|
-
|
|
278
270
|
// 멱등성: capacitor.config.ts가 없을 때만 cap init 실행
|
|
279
271
|
const configPath = pathx.posixResolve(this._capPath, "capacitor.config.ts");
|
|
280
272
|
if (!(await fsx.exists(configPath))) {
|
|
@@ -300,7 +292,7 @@ export class Capacitor {
|
|
|
300
292
|
/**
|
|
301
293
|
* package.json 설정
|
|
302
294
|
*/
|
|
303
|
-
private async _setupNpmConf(): Promise<
|
|
295
|
+
private async _setupNpmConf(): Promise<boolean> {
|
|
304
296
|
const projNpmConfigPath = pathx.posixResolve(this._findWorkspaceRoot(), "package.json");
|
|
305
297
|
|
|
306
298
|
// 루트 package.json 존재 확인
|
|
@@ -358,16 +350,17 @@ export class Capacitor {
|
|
|
358
350
|
}
|
|
359
351
|
}
|
|
360
352
|
|
|
361
|
-
// 새 플러그인 추가
|
|
362
|
-
const
|
|
353
|
+
// 새 플러그인 추가
|
|
354
|
+
const pkgRequire = createRequire(pathx.posixResolve(this._pkgPath, "package.json"));
|
|
363
355
|
for (const plugin of usePlugins) {
|
|
364
356
|
const version = mainDeps[plugin] ?? "*";
|
|
365
357
|
if (typeof version === "string" && version.startsWith("workspace:")) {
|
|
366
|
-
// workspace 플러그인은
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
358
|
+
// workspace 플러그인은 link: 프로토콜로 실제 경로를 지정
|
|
359
|
+
const pluginPkgJsonPath = pkgRequire.resolve(`${plugin}/package.json`);
|
|
360
|
+
const pluginDir = path.dirname(pluginPkgJsonPath);
|
|
361
|
+
const relativePath = path.relative(this._capPath, pluginDir).replace(/\\/g, "/");
|
|
362
|
+
capNpmConf.dependencies[plugin] = `link:${relativePath}`;
|
|
363
|
+
Capacitor._logger.debug(`workspace 플러그인 (link): ${plugin} → ${relativePath}`);
|
|
371
364
|
} else if (!(plugin in capNpmConf.dependencies)) {
|
|
372
365
|
capNpmConf.dependencies[plugin] = version;
|
|
373
366
|
Capacitor._logger.debug(`플러그인 추가: ${plugin}@${version}`);
|
|
@@ -388,12 +381,11 @@ export class Capacitor {
|
|
|
388
381
|
await fsx.writeJson(capNpmConfPath, capNpmConf, { space: 2 });
|
|
389
382
|
|
|
390
383
|
// 의존성 변경 여부 확인
|
|
391
|
-
|
|
384
|
+
return (
|
|
392
385
|
orgCapNpmConf.volta !== capNpmConf.volta ||
|
|
393
386
|
JSON.stringify(orgCapNpmConf.dependencies) !== JSON.stringify(capNpmConf.dependencies) ||
|
|
394
|
-
JSON.stringify(orgCapNpmConf.devDependencies) !== JSON.stringify(capNpmConf.devDependencies)
|
|
395
|
-
|
|
396
|
-
return { depChanged: isChanged, workspacePlugins };
|
|
387
|
+
JSON.stringify(orgCapNpmConf.devDependencies) !== JSON.stringify(capNpmConf.devDependencies)
|
|
388
|
+
);
|
|
397
389
|
}
|
|
398
390
|
|
|
399
391
|
/**
|
|
@@ -517,319 +509,6 @@ export default config;
|
|
|
517
509
|
|
|
518
510
|
//#endregion
|
|
519
511
|
|
|
520
|
-
//#region Private - Android 설정
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Android 네이티브 설정 구성
|
|
524
|
-
*/
|
|
525
|
-
private async _configureAndroid(): Promise<void> {
|
|
526
|
-
const androidPath = pathx.posixResolve(this._capPath, "android");
|
|
527
|
-
|
|
528
|
-
// Android 디렉토리 존재 확인
|
|
529
|
-
if (!(await fsx.exists(androidPath))) {
|
|
530
|
-
throw new Error(`Android 프로젝트 디렉토리를 찾을 수 없습니다: ${androidPath}`);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
Capacitor._logger.debug("JAVA_HOME 설정 시작");
|
|
534
|
-
await this._configureAndroidJavaHomePath(androidPath);
|
|
535
|
-
Capacitor._logger.debug("JAVA_HOME 설정 완료");
|
|
536
|
-
|
|
537
|
-
Capacitor._logger.debug("Android SDK 경로 설정 시작");
|
|
538
|
-
await this._configureAndroidSdkPath(androidPath);
|
|
539
|
-
Capacitor._logger.debug("Android SDK 경로 설정 완료");
|
|
540
|
-
|
|
541
|
-
Capacitor._logger.debug("AndroidManifest.xml 설정 시작");
|
|
542
|
-
await this._configureAndroidManifest(androidPath);
|
|
543
|
-
Capacitor._logger.debug("AndroidManifest.xml 설정 완료");
|
|
544
|
-
|
|
545
|
-
Capacitor._logger.debug("루트 build.gradle Kotlin 플러그인 설정 시작");
|
|
546
|
-
await this._configureAndroidRootBuildGradle(androidPath);
|
|
547
|
-
Capacitor._logger.debug("루트 build.gradle Kotlin 플러그인 설정 완료");
|
|
548
|
-
|
|
549
|
-
Capacitor._logger.debug("build.gradle 설정 시작");
|
|
550
|
-
await this._configureAndroidBuildGradle(androidPath);
|
|
551
|
-
Capacitor._logger.debug("build.gradle 설정 완료");
|
|
552
|
-
|
|
553
|
-
Capacitor._logger.debug("styles.xml 설정 시작");
|
|
554
|
-
await this._configureAndroidStyles(androidPath);
|
|
555
|
-
Capacitor._logger.debug("styles.xml 설정 완료");
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* JAVA_HOME 경로 설정 (gradle.properties)
|
|
560
|
-
*/
|
|
561
|
-
private async _configureAndroidJavaHomePath(androidPath: string): Promise<void> {
|
|
562
|
-
const gradlePropsPath = pathx.posixResolve(androidPath, "gradle.properties");
|
|
563
|
-
|
|
564
|
-
if (!(await fsx.exists(gradlePropsPath))) {
|
|
565
|
-
Capacitor._logger.warn(`gradle.properties 파일을 찾을 수 없습니다: ${gradlePropsPath}`);
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
let content = await fsx.read(gradlePropsPath);
|
|
570
|
-
|
|
571
|
-
const java21Path = await this._findJava21();
|
|
572
|
-
if (java21Path != null && !content.includes("org.gradle.java.home")) {
|
|
573
|
-
// Windows 경로 이스케이프
|
|
574
|
-
const escapedPath = java21Path.replace(/\\/g, "\\\\");
|
|
575
|
-
content += `\norg.gradle.java.home=${escapedPath}\n`;
|
|
576
|
-
await fsx.write(gradlePropsPath, content);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Java 21 경로 자동 탐색
|
|
582
|
-
*/
|
|
583
|
-
private async _findJava21(): Promise<string | undefined> {
|
|
584
|
-
const patterns = [
|
|
585
|
-
"C:/Program Files/Amazon Corretto/jdk21*",
|
|
586
|
-
"C:/Program Files/Eclipse Adoptium/jdk-21*",
|
|
587
|
-
"C:/Program Files/Java/jdk-21*",
|
|
588
|
-
"C:/Program Files/Microsoft/jdk-21*",
|
|
589
|
-
"/usr/lib/jvm/java-21*",
|
|
590
|
-
"/usr/lib/jvm/temurin-21*",
|
|
591
|
-
];
|
|
592
|
-
|
|
593
|
-
for (const pattern of patterns) {
|
|
594
|
-
const matches = await fsx.glob(pattern);
|
|
595
|
-
if (matches.length > 0) {
|
|
596
|
-
return matches.sort().at(-1);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
return undefined;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* Android SDK 경로 설정 (local.properties)
|
|
605
|
-
*/
|
|
606
|
-
private async _configureAndroidSdkPath(androidPath: string): Promise<void> {
|
|
607
|
-
const localPropsPath = pathx.posixResolve(androidPath, "local.properties");
|
|
608
|
-
|
|
609
|
-
const sdkPath = await this._findAndroidSdk();
|
|
610
|
-
if (sdkPath != null) {
|
|
611
|
-
// Gradle 호환: 항상 forward slash 사용
|
|
612
|
-
await fsx.write(localPropsPath, `sdk.dir=${pathx.posix(sdkPath)}\n`);
|
|
613
|
-
} else {
|
|
614
|
-
throw new Error(
|
|
615
|
-
"Android SDK를 찾을 수 없습니다.\n" +
|
|
616
|
-
"1. Android Studio를 설치하거나\n" +
|
|
617
|
-
"2. ANDROID_HOME 또는 ANDROID_SDK_ROOT 환경 변수를 설정하세요.",
|
|
618
|
-
);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* Android SDK 경로 탐색
|
|
624
|
-
*/
|
|
625
|
-
private async _findAndroidSdk(): Promise<string | undefined> {
|
|
626
|
-
const androidHome =
|
|
627
|
-
env("ANDROID_HOME") ??
|
|
628
|
-
env("ANDROID_SDK_ROOT");
|
|
629
|
-
if (androidHome != null && (await fsx.exists(androidHome))) {
|
|
630
|
-
return androidHome;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
const candidates = [
|
|
634
|
-
pathx.posixResolve(env("LOCALAPPDATA") ?? "", "Android/Sdk"),
|
|
635
|
-
pathx.posixResolve(env("HOME") ?? "", "Android/Sdk"),
|
|
636
|
-
"C:/Program Files/Android/Sdk",
|
|
637
|
-
"C:/Android/Sdk",
|
|
638
|
-
];
|
|
639
|
-
|
|
640
|
-
for (const candidate of candidates) {
|
|
641
|
-
if (await fsx.exists(candidate)) {
|
|
642
|
-
return candidate;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
return undefined;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
/**
|
|
650
|
-
* AndroidManifest.xml 설정
|
|
651
|
-
*
|
|
652
|
-
* 주의: Capacitor가 생성하는 초기 XML 포맷에 의존하는 정규식 기반 수정.
|
|
653
|
-
* XML 구조가 변경되면 정규식이 실패할 수 있음.
|
|
654
|
-
*/
|
|
655
|
-
private async _configureAndroidManifest(androidPath: string): Promise<void> {
|
|
656
|
-
const manifestPath = pathx.posixResolve(androidPath, "app/src/main/AndroidManifest.xml");
|
|
657
|
-
|
|
658
|
-
if (!(await fsx.exists(manifestPath))) {
|
|
659
|
-
throw new Error(`AndroidManifest.xml 파일을 찾을 수 없습니다: ${manifestPath}`);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
let content = await fsx.read(manifestPath);
|
|
663
|
-
|
|
664
|
-
// usesCleartextTraffic 설정
|
|
665
|
-
if (!content.includes("android:usesCleartextTraffic")) {
|
|
666
|
-
content = content.replace("<application", '<application android:usesCleartextTraffic="true"');
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
// 추가 권한 설정
|
|
670
|
-
const permissions = this._config.platform?.android?.permissions ?? [];
|
|
671
|
-
for (const perm of permissions) {
|
|
672
|
-
const permTag = `<uses-permission android:name="android.permission.${perm.name}"`;
|
|
673
|
-
if (!content.includes(permTag)) {
|
|
674
|
-
const maxSdkAttr =
|
|
675
|
-
perm.maxSdkVersion != null ? ` android:maxSdkVersion="${perm.maxSdkVersion}"` : "";
|
|
676
|
-
const ignoreAttr = perm.ignore != null ? ` tools:ignore="${perm.ignore}"` : "";
|
|
677
|
-
const permLine = ` ${permTag}${maxSdkAttr}${ignoreAttr} />\n`;
|
|
678
|
-
|
|
679
|
-
if (perm.ignore != null && !content.includes("xmlns:tools=")) {
|
|
680
|
-
content = content.replace(
|
|
681
|
-
"<manifest xmlns:android",
|
|
682
|
-
'<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android',
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
content = content.replace("</manifest>", `${permLine}</manifest>`);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// 추가 application 속성 설정
|
|
691
|
-
const appConfig = this._config.platform?.android?.config;
|
|
692
|
-
if (appConfig) {
|
|
693
|
-
for (const [key, value] of Object.entries(appConfig)) {
|
|
694
|
-
const attr = `android:${key}="${value}"`;
|
|
695
|
-
if (!content.includes(`android:${key}=`)) {
|
|
696
|
-
content = content.replace("<application", `<application ${attr}`);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// intentFilters 설정
|
|
702
|
-
const intentFilters = this._config.platform?.android?.intentFilters ?? [];
|
|
703
|
-
for (const filter of intentFilters) {
|
|
704
|
-
const filterKey = filter.action ?? filter.category ?? "";
|
|
705
|
-
if (filterKey && !content.includes(filterKey)) {
|
|
706
|
-
const actionLine = filter.action != null ? `<action android:name="${filter.action}"/>` : "";
|
|
707
|
-
const categoryLine =
|
|
708
|
-
filter.category != null ? `<category android:name="${filter.category}"/>` : "";
|
|
709
|
-
|
|
710
|
-
content = content.replace(
|
|
711
|
-
/(<activity[\s\S]*?android:name="\.MainActivity"[\s\S]*?>)/,
|
|
712
|
-
`$1
|
|
713
|
-
<intent-filter>
|
|
714
|
-
${actionLine}
|
|
715
|
-
${categoryLine}
|
|
716
|
-
</intent-filter>`,
|
|
717
|
-
);
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
await fsx.write(manifestPath, content);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
/**
|
|
725
|
-
* 루트 build.gradle에 Kotlin Gradle 플러그인 classpath 추가
|
|
726
|
-
*/
|
|
727
|
-
private async _configureAndroidRootBuildGradle(androidPath: string): Promise<void> {
|
|
728
|
-
const rootBuildGradlePath = pathx.posixResolve(androidPath, "build.gradle");
|
|
729
|
-
|
|
730
|
-
if (!(await fsx.exists(rootBuildGradlePath))) {
|
|
731
|
-
Capacitor._logger.warn(`루트 build.gradle 파일을 찾을 수 없습니다: ${rootBuildGradlePath}`);
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
let content = await fsx.read(rootBuildGradlePath);
|
|
736
|
-
|
|
737
|
-
if (!content.includes("kotlin-gradle-plugin")) {
|
|
738
|
-
content = content.replace(
|
|
739
|
-
/classpath 'com\.android\.tools\.build:gradle:[^']+'/,
|
|
740
|
-
`$&\n classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.20'`,
|
|
741
|
-
);
|
|
742
|
-
await fsx.write(rootBuildGradlePath, content);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/**
|
|
747
|
-
* build.gradle 수정 (서명 설정 제외)
|
|
748
|
-
*/
|
|
749
|
-
private async _configureAndroidBuildGradle(androidPath: string): Promise<void> {
|
|
750
|
-
const buildGradlePath = pathx.posixResolve(androidPath, "app/build.gradle");
|
|
751
|
-
|
|
752
|
-
if (!(await fsx.exists(buildGradlePath))) {
|
|
753
|
-
throw new Error(`build.gradle 파일을 찾을 수 없습니다: ${buildGradlePath}`);
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
let content = await fsx.read(buildGradlePath);
|
|
757
|
-
|
|
758
|
-
// versionName, versionCode 설정
|
|
759
|
-
const version = this._npmConfig.version;
|
|
760
|
-
const cleanVersion = version.replace(/-.*$/, "");
|
|
761
|
-
const versionParts = cleanVersion.split(".");
|
|
762
|
-
const versionCode =
|
|
763
|
-
parseInt(versionParts[0] ?? "0") * 10000 +
|
|
764
|
-
parseInt(versionParts[1] ?? "0") * 100 +
|
|
765
|
-
parseInt(versionParts[2] ?? "0");
|
|
766
|
-
|
|
767
|
-
content = content.replace(/versionCode \d+/, `versionCode ${versionCode}`);
|
|
768
|
-
content = content.replace(/versionName "[^"]+"/, `versionName "${version}"`);
|
|
769
|
-
|
|
770
|
-
// SDK 버전 설정
|
|
771
|
-
if (this._config.platform?.android?.sdkVersion != null) {
|
|
772
|
-
const sdkVersion = this._config.platform.android.sdkVersion;
|
|
773
|
-
content = content.replace(/minSdkVersion .+/, `minSdkVersion ${sdkVersion}`);
|
|
774
|
-
content = content.replace(/targetSdkVersion .+/, `targetSdkVersion ${sdkVersion}`);
|
|
775
|
-
} else {
|
|
776
|
-
content = content.replace(/minSdkVersion .+/, `minSdkVersion rootProject.ext.minSdkVersion`);
|
|
777
|
-
content = content.replace(
|
|
778
|
-
/targetSdkVersion .+/,
|
|
779
|
-
`targetSdkVersion rootProject.ext.targetSdkVersion`,
|
|
780
|
-
);
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
await fsx.write(buildGradlePath, content);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/**
|
|
787
|
-
* styles.xml의 스플래시 테마 수정
|
|
788
|
-
*
|
|
789
|
-
* 1. Theme.SplashScreen parent → Theme.AppCompat.DayNight.NoActionBar
|
|
790
|
-
* Theme.SplashScreen은 android:windowBackground에 compat_splash_screen을 설정하여
|
|
791
|
-
* android:background(@drawable/splash)와 이중 표시를 발생시킨다.
|
|
792
|
-
* installSplashScreen()을 호출하지 않으므로 Theme.SplashScreen 기능이 불필요하다.
|
|
793
|
-
*
|
|
794
|
-
* 2. android:background → android:windowBackground
|
|
795
|
-
* android:background는 View 레벨 속성으로 AppCompat 뷰 계층의 여러 View에 상속되어
|
|
796
|
-
* 동일한 splash 로고가 다중 레이어에 중복 렌더링된다.
|
|
797
|
-
* android:windowBackground는 Window의 DecorView에만 적용되어 단일 렌더링을 보장한다.
|
|
798
|
-
*/
|
|
799
|
-
private async _configureAndroidStyles(androidPath: string): Promise<void> {
|
|
800
|
-
const stylesPath = pathx.posixResolve(androidPath, "app/src/main/res/values/styles.xml");
|
|
801
|
-
|
|
802
|
-
if (!(await fsx.exists(stylesPath))) {
|
|
803
|
-
Capacitor._logger.warn(`styles.xml 파일을 찾을 수 없습니다: ${stylesPath}`);
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
let content = await fsx.read(stylesPath);
|
|
808
|
-
let changed = false;
|
|
809
|
-
|
|
810
|
-
if (content.includes('parent="Theme.SplashScreen"')) {
|
|
811
|
-
content = content.replace(
|
|
812
|
-
'parent="Theme.SplashScreen"',
|
|
813
|
-
'parent="Theme.AppCompat.DayNight.NoActionBar"',
|
|
814
|
-
);
|
|
815
|
-
changed = true;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
if (content.includes('"android:background">@drawable/splash')) {
|
|
819
|
-
content = content.replace(
|
|
820
|
-
'"android:background">@drawable/splash',
|
|
821
|
-
'"android:windowBackground">@drawable/splash',
|
|
822
|
-
);
|
|
823
|
-
changed = true;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
if (changed) {
|
|
827
|
-
await fsx.write(stylesPath, content);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
//#endregion
|
|
832
|
-
|
|
833
512
|
//#region Public — 기기 실행
|
|
834
513
|
|
|
835
514
|
/**
|
|
@@ -1087,30 +766,6 @@ export default config;
|
|
|
1087
766
|
* workspace:* 플러그인을 .capacitor/node_modules/에 symlink로 연결한다.
|
|
1088
767
|
* cap sync는 플러그인의 android/ 네이티브 코드만 필요하므로 JS 의존성 resolve 불필요.
|
|
1089
768
|
*/
|
|
1090
|
-
private async _linkWorkspacePlugins(plugins: string[]): Promise<void> {
|
|
1091
|
-
if (plugins.length === 0) return;
|
|
1092
|
-
|
|
1093
|
-
const require = createRequire(pathx.posixResolve(this._pkgPath, "package.json"));
|
|
1094
|
-
|
|
1095
|
-
for (const plugin of plugins) {
|
|
1096
|
-
const pluginPkgJsonPath = require.resolve(`${plugin}/package.json`);
|
|
1097
|
-
const pluginDir = path.dirname(pluginPkgJsonPath);
|
|
1098
|
-
|
|
1099
|
-
const linkPath = pathx.posixResolve(this._capPath, "node_modules", ...plugin.split("/"));
|
|
1100
|
-
|
|
1101
|
-
// scope 디렉토리 생성 (예: @simplysm/)
|
|
1102
|
-
await fsx.mkdir(path.dirname(linkPath));
|
|
1103
|
-
|
|
1104
|
-
// 기존 symlink가 있으면 삭제
|
|
1105
|
-
if (await fsx.exists(linkPath)) {
|
|
1106
|
-
await fsx.rm(linkPath);
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
await symlink(pluginDir, linkPath, "junction");
|
|
1110
|
-
Capacitor._logger.debug(`workspace 플러그인 symlink: ${plugin} → ${pluginDir}`);
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
769
|
/**
|
|
1115
770
|
* pnpm-workspace.yaml이 있는 워크스페이스 루트 디렉토리를 찾는다.
|
|
1116
771
|
*/
|
package/src/commands/check.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cpx } from "@simplysm/core-node";
|
|
2
2
|
import { err as errNs } from "@simplysm/core-common";
|
|
3
|
-
import { executeTypecheck, type TypecheckResult } from "
|
|
4
|
-
import { executeLint, type LintResult } from "
|
|
3
|
+
import { executeTypecheck, type TypecheckResult } from "../orchestrators/TypecheckOrchestrator";
|
|
4
|
+
import { executeLint, type LintResult } from "../utils/lint-core";
|
|
5
5
|
import { consola } from "consola";
|
|
6
6
|
import { validateTargets, discoverWorkspacePackages } from "../utils/package-utils";
|
|
7
7
|
import { runLintInWorker } from "../utils/lint-utils";
|
package/src/commands/device.ts
CHANGED
|
@@ -64,8 +64,9 @@ export async function runDevice(options: DeviceOptions): Promise<void> {
|
|
|
64
64
|
if (typeof clientConfig.server === "number") {
|
|
65
65
|
serverUrl = `http://localhost:${clientConfig.server}/${targetName}/`;
|
|
66
66
|
} else {
|
|
67
|
-
// server가 패키지명(string)인 경우: .dev-port 파일에서 포트 자동 탐지
|
|
68
|
-
const
|
|
67
|
+
// server가 패키지명(string)인 경우: 서버 패키지의 .dev-port 파일에서 포트 자동 탐지
|
|
68
|
+
const serverPkgDir = pathx.posixResolve(cwd, "packages", clientConfig.server);
|
|
69
|
+
const portFile = path.join(serverPkgDir, "dist", ".dev-port");
|
|
69
70
|
let portStr: string;
|
|
70
71
|
try {
|
|
71
72
|
portStr = fs.readFileSync(portFile, "utf-8").trim();
|