@simplysm/sd-cli 7.0.236 → 7.0.239

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.
@@ -64,4 +64,4 @@ else {
64
64
  process.exit(1);
65
65
  });
66
66
  }
67
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbGQtd29ya2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dvcmtlci9idWlsZC13b3JrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDakUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDakUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDbkUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDeEUsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQ3hCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUl2RCxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsd0JBQXdCLENBQUMsS0FBSyxPQUFPLEVBQUU7SUFDckQsTUFBTSxDQUFDLFNBQVMsQ0FBQztRQUNmLE9BQU8sRUFBRTtZQUNQLEtBQUssRUFBRSxjQUFjLENBQUMsS0FBSztTQUM1QjtLQUNGLENBQUMsQ0FBQztDQUNKO0tBQ0k7SUFDSCxNQUFNLENBQUMsU0FBUyxDQUFDO1FBQ2YsR0FBRyxFQUFFLElBQUk7S0FDVixDQUFDLENBQUM7Q0FDSjtBQUVELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUF3QixDQUFDO0FBQ3BELE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDakMsTUFBTSxNQUFNLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUF3QixDQUFDO0FBQ3pFLE1BQU0saUJBQWlCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FB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
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbGQtd29ya2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dvcmtlci9idWlsZC13b3JrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDakUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDakUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDbkUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDeEUsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQ3hCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUd2RCxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsd0JBQXdCLENBQUMsS0FBSyxPQUFPLEVBQUU7SUFDckQsTUFBTSxDQUFDLFNBQVMsQ0FBQztRQUNmLE9BQU8sRUFBRTtZQUNQLEtBQUssRUFBRSxjQUFjLENBQUMsS0FBSztTQUM1QjtLQUNGLENBQUMsQ0FBQztDQUNKO0tBQ0k7SUFDSCxNQUFNLENBQUMsU0FBUyxDQUFDO1FBQ2YsR0FBRyxFQUFFLElBQUk7S0FDVixDQUFDLENBQUM7Q0FDSjtBQUVELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUF3QixDQUFDO0FBQ3BELE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDakMsTUFBTSxNQUFNLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUF3QixDQUFDO0FBQ3pFLE1BQU0saUJBQWlCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FB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
@@ -0,0 +1,18 @@
1
+ /* eslint-disable */
2
+
3
+ const script = document.createElement("script");
4
+ script.defer = true;
5
+
6
+ if (process.env["NODE_ENV"] === "production") {
7
+ script.src = "cordova.js";
8
+ }
9
+ else {
10
+ if (navigator.userAgent.toLowerCase().indexOf("android") !== -1) {
11
+ script.src = "cordova-android/cordova.js";
12
+ }
13
+ else {
14
+ script.src = "cordova-browser/cordova.js";
15
+ }
16
+ }
17
+
18
+ document.body.appendChild(script);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-cli",
3
- "version": "7.0.236",
3
+ "version": "7.0.239",
4
4
  "description": "심플리즘 패키지 - CLI",
5
5
  "author": "김석래",
6
6
  "repository": {
@@ -23,16 +23,16 @@
23
23
  "@babel/parser": "^7.17.0",
24
24
  "@babel/types": "^7.17.0",
25
25
  "@ngtools/webpack": "^13.2.2",
26
- "@simplysm/sd-core-common": "7.0.236",
27
- "@simplysm/sd-core-node": "7.0.236",
28
- "@simplysm/sd-service-common": "7.0.236",
29
- "@simplysm/sd-service-server": "7.0.236",
30
- "@simplysm/sd-storage": "7.0.236",
26
+ "@simplysm/sd-core-common": "7.0.239",
27
+ "@simplysm/sd-core-node": "7.0.239",
28
+ "@simplysm/sd-service-common": "7.0.239",
29
+ "@simplysm/sd-service-server": "7.0.239",
30
+ "@simplysm/sd-storage": "7.0.239",
31
31
  "browserslist": "^4.19.1",
32
32
  "chokidar": "^3.5.3",
33
33
  "connect": "^3.7.0",
34
34
  "copy-webpack-plugin": "^10.2.4",
35
- "cordova": "^11.0.0",
35
+ "cordova": "^10.0.0",
36
36
  "css-loader": "^6.6.0",
37
37
  "electron": "^18.0.1",
38
38
  "electron-builder": "^22.14.13",
package/src/bin/sd-cli.ts CHANGED
@@ -93,7 +93,7 @@ const argv = yargs(hideBin(process.argv))
93
93
  })
94
94
  )
95
95
  .command(
96
- "run-cordova <platform> <package> <url>",
96
+ "run-cordova <platform> <package> [url]",
97
97
  "변경감지중인 플랫폼을 코도바 디바이스에 앱 형태로 띄웁니다.",
98
98
  (cmd) => cmd
99
99
  .positional("platform", {
@@ -1,234 +1,259 @@
1
- import { INpmConfig, ISdCliClientBuilderCordovaConfig } from "../commons";
2
- import * as path from "path";
3
- import { FsUtil, Logger, SdProcess } from "@simplysm/sd-core-node";
4
- import JSZip from "jszip";
5
- import xml2js from "xml2js";
6
-
7
- export class SdCliCordova {
8
- protected readonly _logger: Logger;
9
-
10
- private readonly _npmConfig: INpmConfig;
11
-
12
- public readonly cordovaPath = path.resolve(this._rootPath, ".cordova");
13
- private readonly _binPath = path.resolve(process.cwd(), "node_modules/.bin/cordova.cmd");
14
-
15
- public get platforms(): ("browser" | "android")[] {
16
- return [
17
- ...this._config.target?.browser ? ["browser" as const] : [],
18
- ...this._config.target?.android ? ["android" as const] : []
19
- ];
20
- }
21
-
22
- public constructor(private readonly _rootPath: string, private readonly _config: ISdCliClientBuilderCordovaConfig) {
23
- this._npmConfig = FsUtil.readJson(path.resolve(this._rootPath, "package.json"));
24
- this._logger = Logger.get(["simplysm", "sd-cli", this.constructor.name, this._npmConfig.name]);
25
- }
26
-
27
- private async _execAsync(cmd: string, cwd: string): Promise<void> {
28
- this._logger.debug(cmd);
29
- const msg = await SdProcess.spawnAsync(cmd, { cwd });
30
- this._logger.debug(msg);
31
- }
32
-
33
- public async initializeAsync(): Promise<void> {
34
- if (FsUtil.exists(this.cordovaPath)) {
35
- this._logger.log("이미 생성되어있는 '.cordova'를 사용합니다.");
36
- }
37
- else {
38
- await this._execAsync(`${this._binPath} telemetry on`, this._rootPath);
39
-
40
- // 프로젝트 생성
41
- await this._execAsync(`${this._binPath} create "${this.cordovaPath}" "${this._config.appId}" "${this._config.appName}"`, process.cwd());
42
- }
43
-
44
- // platforms 폴더 혹시 없으면 생성
45
- await FsUtil.mkdirsAsync(path.resolve(this.cordovaPath, "platforms"));
46
-
47
- // www 폴더 혹시 없으면 생성
48
- await FsUtil.mkdirsAsync(path.resolve(this.cordovaPath, "www"));
49
-
50
- // 미설치 빌드 플랫폼 신규 생성
51
- const alreadyPlatforms = await FsUtil.readdirAsync(path.resolve(this.cordovaPath, "platforms"));
52
- for (const platform of this.platforms) {
53
- if (!alreadyPlatforms.includes(platform)) {
54
- await this._execAsync(`${this._binPath} platform add ${platform}`, this.cordovaPath);
55
- }
56
- }
57
-
58
- // 설치 미빌드 플랫폼 삭제
59
- for (const alreadyPlatform of alreadyPlatforms) {
60
- if (this._config.target?.[alreadyPlatform] == null) {
61
- await this._execAsync(`${this._binPath} platform remove ${alreadyPlatform}`, this.cordovaPath);
62
- }
63
- }
64
-
65
- // 미설치 플러그인들 설치
66
- const pluginsFetch = FsUtil.exists(path.resolve(this.cordovaPath, "plugins/fetch.json"))
67
- ? await FsUtil.readJsonAsync(path.resolve(this.cordovaPath, "plugins/fetch.json"))
68
- : undefined;
69
- const alreadyPlugins = pluginsFetch != undefined
70
- ? Object.values(pluginsFetch)
71
- .map((item: any) => (item.source.id !== undefined ? item.source.id.replace(/@.*$/, "") : item.source.url))
72
- : [];
73
-
74
- for (const plugin of this._config.plugins?.distinct() ?? []) {
75
- if (!alreadyPlugins.includes(plugin)) {
76
- await this._execAsync(`${this._binPath} plugin add ${plugin}`, this.cordovaPath);
77
- }
78
- }
79
-
80
- // 설치된 미사용 플러그인 삭제
81
- for (const alreadyPlugin of alreadyPlugins) {
82
- if (!(this._config.plugins?.distinct() ?? []).includes(alreadyPlugin)) {
83
- await this._execAsync(`${this._binPath} plugin remove ${alreadyPlugin}`, this.cordovaPath);
84
- }
85
- }
86
-
87
- // ANDROID SIGN 파일 복사
88
- if (this._config.target?.android?.sign) {
89
- await FsUtil.copyAsync(
90
- path.resolve(this._rootPath, this._config.target.android.sign.keystore),
91
- path.resolve(this.cordovaPath, "android.keystore")
92
- );
93
- }
94
- else {
95
- await FsUtil.removeAsync(path.resolve(this.cordovaPath, "android.keystore"));
96
- }
97
-
98
- // 빌드 옵션 파일 생성
99
- await FsUtil.writeJsonAsync(
100
- path.resolve(this.cordovaPath, "build.json"),
101
- {
102
- ...this._config.target?.android ? {
103
- android: {
104
- release: {
105
- packageType: this._config.target.android.bundle ? "bundle" : "apk",
106
- ...this._config.target.android.sign ? {
107
- keystore: path.resolve(this.cordovaPath, "android.keystore"),
108
- storePassword: this._config.target.android.sign.storePassword,
109
- alias: this._config.target.android.sign.alias,
110
- password: this._config.target.android.sign.password,
111
- keystoreType: this._config.target.android.sign.keystoreType
112
- } : {}
113
- }
114
- }
115
- } : {}
116
- }
117
- );
118
-
119
- // ICON 파일 복사
120
- if (this._config.icon !== undefined) {
121
- await FsUtil.copyAsync(path.resolve(this._rootPath, this._config.icon), path.resolve(this.cordovaPath, "res", "icon.png"));
122
- }
123
- else {
124
- await FsUtil.removeAsync(path.resolve(this.cordovaPath, "res", "icon.png"));
125
- }
126
-
127
- // CONFIG: 초기값 백업
128
- const configFilePath = path.resolve(this.cordovaPath, "config.xml");
129
- const configBackFilePath = path.resolve(this.cordovaPath, "config.xml.bak");
130
- if (!FsUtil.exists(configBackFilePath)) {
131
- await FsUtil.copyAsync(configFilePath, configBackFilePath);
132
- }
133
-
134
- // CONFIG: 초기값 읽기
135
- const configFileContent = await FsUtil.readFileAsync(configBackFilePath);
136
- const configXml = await xml2js.parseStringPromise(configFileContent);
137
-
138
-
139
- // CONFIG: 버전 설정
140
- configXml.widget.$.version = this._npmConfig.version;
141
-
142
- // CONFIG: ICON 설정
143
- if (this._config.icon !== undefined) {
144
- configXml["widget"]["icon"] = [{ "$": { "src": "res/icon.png" } }];
145
- }
146
-
147
- // CONFIG: 접근허용 세팅
148
- configXml["widget"]["allow-navigation"] = [{ "$": { "href": "*://*/*" } }];
149
-
150
- // CONFIG: ANDROID usesCleartextTraffic 설정
151
- if (this._config.target?.android) {
152
- configXml.widget.$["xmlns:android"] = "http://schemas.android.com/apk/res/android";
153
-
154
- configXml["widget"]["platform"] = configXml["widget"]["platform"] ?? [];
155
- configXml["widget"]["platform"].push({
156
- "$": {
157
- "name": "android"
158
- },
159
- "edit-config": [{
160
- "$": {
161
- "file": "app/src/main/AndroidManifest.xml",
162
- "mode": "merge",
163
- "target": "/manifest/application"
164
- },
165
- "application": [{
166
- "android:usesCleartextTraffic": "true"
167
- }]
168
- }]
169
- });
170
- }
171
-
172
- // CONFIG: 파일 새로 쓰기
173
- const configResultContent = new xml2js.Builder().buildObject(configXml);
174
- await FsUtil.writeFileAsync(configFilePath, configResultContent);
175
-
176
- // 각 플랫폼 www 준비
177
- await this._execAsync(`${this._binPath} prepare`, this.cordovaPath);
178
- }
179
-
180
- public async buildAsync(outPath: string): Promise<void> {
181
- // 실행
182
- const buildType = this._config.debug ? "debug" : "release";
183
- for (const platform of this.platforms) {
184
- await this._execAsync(`${this._binPath} build ${platform} --${buildType}`, this.cordovaPath);
185
- }
186
-
187
- // 결과물 복사: ANDROID
188
- if (this._config.target?.android) {
189
- const targetOutPath = path.resolve(outPath, "android");
190
- const apkFileName = this._config.target.android.sign ? `app-${buildType}.apk` : `app-${buildType}-unsigned.apk`;
191
- // const distApkFileName = path.basename(`${this._config.appName}${this._config.target.android.sign ? "" : "-unsigned"}-v${this._npmConfig.version}.apk`);
192
- const latestDistApkFileName = path.basename(`${this._config.appName}${this._config.target.android.sign ? "" : "-unsigned"}-latest.apk`);
193
- await FsUtil.mkdirsAsync(targetOutPath);
194
- // await FsUtil.copyAsync(
195
- // path.resolve(this.cordovaPath, "platforms/android/app/build/outputs/apk", buildType, apkFileName),
196
- // path.resolve(targetOutPath, distApkFileName)
197
- // );
198
- await FsUtil.copyAsync(
199
- path.resolve(this.cordovaPath, "platforms/android/app/build/outputs/apk", buildType, apkFileName),
200
- path.resolve(targetOutPath, latestDistApkFileName)
201
- );
202
- }
203
-
204
- if (this._config.target?.android) {
205
- // 자동업데이트를 위한 zip 파일 쓰기
206
- const zip = new JSZip();
207
- const resultFiles = await FsUtil.globAsync(path.resolve(this.cordovaPath, "platforms", "android", "app", "src", "main", "assets", "www", "**/*"), {
208
- dot: true,
209
- nodir: true
210
- });
211
- for (const resultFile of resultFiles) {
212
- const contentBuffer = await FsUtil.readFileBufferAsync(resultFile);
213
- const relativePath = path.relative(path.resolve(this.cordovaPath, "www"), resultFile);
214
- zip.file("/" + relativePath, contentBuffer);
215
- }
216
-
217
- const zipFileName = path.basename(`${this._npmConfig.version}.zip`);
218
- const resultBuffer = await zip.generateAsync({ type: "nodebuffer" });
219
-
220
- await FsUtil.writeFileAsync(path.resolve(outPath, "android/updates/", zipFileName), resultBuffer);
221
- }
222
- }
223
-
224
- public static async runWebviewOnDeviceAsync(rootPath: string, platform: "browser" | "android", pkgName: string, url: string): Promise<void> {
225
- const cordovaPath = path.resolve(rootPath, `packages/${pkgName}/.cordova/`);
226
-
227
- await FsUtil.removeAsync(path.resolve(cordovaPath, "www"));
228
- await FsUtil.mkdirsAsync(path.resolve(cordovaPath, "www"));
229
- await FsUtil.writeFileAsync(path.resolve(cordovaPath, "www/index.html"), `'${url}'로 이동중... <script>setTimeout(function () {window.location.href = "${url.replace(/\/$/, "")}/${pkgName}/cordova/"}, 3000);</script>`.trim());
230
-
231
- const binPath = path.resolve(process.cwd(), "node_modules/.bin/cordova.cmd");
232
- await SdProcess.spawnAsync(`${binPath} run ${platform} --device`, { cwd: cordovaPath }, true);
233
- }
234
- }
1
+ import { INpmConfig, ISdCliClientBuilderCordovaConfig } from "../commons";
2
+ import * as path from "path";
3
+ import { FsUtil, Logger, SdProcess } from "@simplysm/sd-core-node";
4
+ import JSZip from "jszip";
5
+ import xml2js from "xml2js";
6
+
7
+ export class SdCliCordova {
8
+ protected readonly _logger: Logger;
9
+
10
+ private readonly _npmConfig: INpmConfig;
11
+
12
+ public readonly cordovaPath = path.resolve(this._rootPath, ".cordova");
13
+ private readonly _binPath = path.resolve(process.cwd(), "node_modules/.bin/cordova.cmd");
14
+
15
+ public get platforms(): ("browser" | "android")[] {
16
+ return [
17
+ ...this._config.target?.browser ? ["browser" as const] : [],
18
+ ...this._config.target?.android ? ["android" as const] : []
19
+ ];
20
+ }
21
+
22
+ public constructor(private readonly _rootPath: string, private readonly _config: ISdCliClientBuilderCordovaConfig) {
23
+ this._npmConfig = FsUtil.readJson(path.resolve(this._rootPath, "package.json"));
24
+ this._logger = Logger.get(["simplysm", "sd-cli", this.constructor.name, this._npmConfig.name]);
25
+ }
26
+
27
+ private async _execAsync(cmd: string, cwd: string): Promise<void> {
28
+ this._logger.debug(cmd);
29
+ const msg = await SdProcess.spawnAsync(cmd, { cwd });
30
+ this._logger.debug(msg);
31
+ }
32
+
33
+ public async initializeAsync(): Promise<void> {
34
+ if (FsUtil.exists(this.cordovaPath)) {
35
+ this._logger.log("이미 생성되어있는 '.cordova'를 사용합니다.");
36
+ }
37
+ else {
38
+ await this._execAsync(`${this._binPath} telemetry on`, this._rootPath);
39
+
40
+ // 프로젝트 생성
41
+ await this._execAsync(`${this._binPath} create "${this.cordovaPath}" "${this._config.appId}" "${this._config.appName}"`, process.cwd());
42
+ }
43
+
44
+ // platforms 폴더 혹시 없으면 생성
45
+ await FsUtil.mkdirsAsync(path.resolve(this.cordovaPath, "platforms"));
46
+
47
+ // www 폴더 혹시 없으면 생성
48
+ await FsUtil.mkdirsAsync(path.resolve(this.cordovaPath, "www"));
49
+
50
+ // 미설치 빌드 플랫폼 신규 생성
51
+ const alreadyPlatforms = await FsUtil.readdirAsync(path.resolve(this.cordovaPath, "platforms"));
52
+ for (const platform of this.platforms) {
53
+ if (!alreadyPlatforms.includes(platform)) {
54
+ await this._execAsync(`${this._binPath} platform add ${platform}`, this.cordovaPath);
55
+ }
56
+ }
57
+
58
+ // 설치 미빌드 플랫폼 삭제
59
+ for (const alreadyPlatform of alreadyPlatforms) {
60
+ if (this._config.target?.[alreadyPlatform] == null) {
61
+ await this._execAsync(`${this._binPath} platform remove ${alreadyPlatform}`, this.cordovaPath);
62
+ }
63
+ }
64
+
65
+ // 설치된 미사용 플러그인 삭제
66
+ const pluginsFetch = FsUtil.exists(path.resolve(this.cordovaPath, "plugins/fetch.json"))
67
+ ? await FsUtil.readJsonAsync(path.resolve(this.cordovaPath, "plugins/fetch.json"))
68
+ : undefined;
69
+ const alreadyPluginIds = pluginsFetch != undefined
70
+ ? Object.values(pluginsFetch)
71
+ .map((item: any) => (item.source.id !== undefined ? item.source.id : item.source.url))
72
+ : [];
73
+ const usePlugins = ["cordova-plugin-ionic-webview", ...this._config.plugins ?? []].distinct();
74
+
75
+ for (const alreadyPluginId of alreadyPluginIds) {
76
+ let hasPlugin = false;
77
+ for (const usePlugin of usePlugins) {
78
+ if (
79
+ (usePlugin.includes("@") && alreadyPluginId === usePlugin) ||
80
+ (!usePlugin.includes("@") && alreadyPluginId.replace(/@.*$/, "") === usePlugin)
81
+ ) {
82
+ hasPlugin = true;
83
+ break;
84
+ }
85
+ }
86
+
87
+ if (!hasPlugin) {
88
+ await this._execAsync(`${this._binPath} plugin remove ${alreadyPluginId.replace(/@.*$/, "")}`, this.cordovaPath);
89
+ }
90
+ }
91
+
92
+ // 미설치 플러그인들 설치
93
+ for (const usePlugin of usePlugins) {
94
+ if (
95
+ (usePlugin.includes("@") && !alreadyPluginIds.includes(usePlugin)) ||
96
+ (!usePlugin.includes("@") && !alreadyPluginIds.map((alreadyPluginId) => alreadyPluginId.replace(/@.*$/, "")).includes(usePlugin))
97
+ ) {
98
+ await this._execAsync(`${this._binPath} plugin add ${usePlugin}`, this.cordovaPath);
99
+ }
100
+ }
101
+
102
+ // ANDROID SIGN 파일 복사
103
+ if (this._config.target?.android?.sign) {
104
+ await FsUtil.copyAsync(
105
+ path.resolve(this._rootPath, this._config.target.android.sign.keystore),
106
+ path.resolve(this.cordovaPath, "android.keystore")
107
+ );
108
+ }
109
+ else {
110
+ await FsUtil.removeAsync(path.resolve(this.cordovaPath, "android.keystore"));
111
+ // SIGN을 안쓸경우 아래 파일이 생성되어 있으면 오류남
112
+ await FsUtil.removeAsync(path.resolve(this.cordovaPath, "platforms/android/release-signing.properties"));
113
+ }
114
+
115
+ // 빌드 옵션 파일 생성
116
+ await FsUtil.writeJsonAsync(
117
+ path.resolve(this.cordovaPath, "build.json"),
118
+ {
119
+ ...this._config.target?.android ? {
120
+ android: {
121
+ release: {
122
+ packageType: this._config.target.android.bundle ? "bundle" : "apk",
123
+ ...this._config.target.android.sign ? {
124
+ keystore: path.resolve(this.cordovaPath, "android.keystore"),
125
+ storePassword: this._config.target.android.sign.storePassword,
126
+ alias: this._config.target.android.sign.alias,
127
+ password: this._config.target.android.sign.password,
128
+ keystoreType: this._config.target.android.sign.keystoreType
129
+ } : {}
130
+ }
131
+ }
132
+ } : {}
133
+ }
134
+ );
135
+
136
+ // ICON 파일 복사
137
+ if (this._config.icon !== undefined) {
138
+ await FsUtil.copyAsync(path.resolve(this._rootPath, this._config.icon), path.resolve(this.cordovaPath, "res", "icon.png"));
139
+ }
140
+ else {
141
+ await FsUtil.removeAsync(path.resolve(this.cordovaPath, "res", "icon.png"));
142
+ }
143
+
144
+ // CONFIG: 초기값 백업
145
+ const configFilePath = path.resolve(this.cordovaPath, "config.xml");
146
+ const configBackFilePath = path.resolve(this.cordovaPath, "config.xml.bak");
147
+ if (!FsUtil.exists(configBackFilePath)) {
148
+ await FsUtil.copyAsync(configFilePath, configBackFilePath);
149
+ }
150
+
151
+ // CONFIG: 초기값 읽기
152
+ const configFileContent = await FsUtil.readFileAsync(configBackFilePath);
153
+ const configXml = await xml2js.parseStringPromise(configFileContent);
154
+
155
+
156
+ // CONFIG: 버전 설정
157
+ configXml.widget.$.version = this._npmConfig.version;
158
+
159
+ // CONFIG: ICON 설정
160
+ if (this._config.icon !== undefined) {
161
+ configXml["widget"]["icon"] = [{ "$": { "src": "res/icon.png" } }];
162
+ }
163
+
164
+ // CONFIG: 접근허용 세팅
165
+ configXml["widget"]["access"] = [{ "$": { "origin": "*" } }];
166
+ configXml["widget"]["allow-navigation"] = [{ "$": { "href": "*" } }];
167
+ configXml["widget"]["allow-intent"] = [{ "$": { "href": "*" } }];
168
+ configXml["widget"]["preference"] = [{ "$": { "name": "MixedContentMode", "value": "0" } }];
169
+
170
+ // CONFIG: ANDROID usesCleartextTraffic 설정
171
+ if (this._config.target?.android) {
172
+ configXml.widget.$["xmlns:android"] = "http://schemas.android.com/apk/res/android";
173
+
174
+ configXml["widget"]["platform"] = configXml["widget"]["platform"] ?? [];
175
+ configXml["widget"]["platform"].push({
176
+ "$": {
177
+ "name": "android"
178
+ },
179
+ "edit-config": [{
180
+ "$": {
181
+ "file": "app/src/main/AndroidManifest.xml",
182
+ "mode": "merge",
183
+ "target": "/manifest/application"
184
+ },
185
+ "application": [{
186
+ "$": {
187
+ "android:usesCleartextTraffic": "true"
188
+ }
189
+ }]
190
+ }]
191
+ });
192
+ }
193
+
194
+ // CONFIG: 파일 새로 쓰기
195
+ const configResultContent = new xml2js.Builder().buildObject(configXml);
196
+ await FsUtil.writeFileAsync(configFilePath, configResultContent);
197
+
198
+ // 각 플랫폼 www 준비
199
+ await this._execAsync(`${this._binPath} prepare`, this.cordovaPath);
200
+ }
201
+
202
+ public async buildAsync(outPath: string): Promise<void> {
203
+ // 실행
204
+ const buildType = this._config.debug ? "debug" : "release";
205
+ for (const platform of this.platforms) {
206
+ await this._execAsync(`${this._binPath} build ${platform} --${buildType}`, this.cordovaPath);
207
+ }
208
+
209
+ // 결과물 복사: ANDROID
210
+ if (this._config.target?.android) {
211
+ const targetOutPath = path.resolve(outPath, "android");
212
+ const apkFileName = this._config.target.android.sign ? `app-${buildType}.apk` : `app-${buildType}-unsigned.apk`;
213
+ // const distApkFileName = path.basename(`${this._config.appName}${this._config.target.android.sign ? "" : "-unsigned"}-v${this._npmConfig.version}.apk`);
214
+ const latestDistApkFileName = path.basename(`${this._config.appName}${this._config.target.android.sign ? "" : "-unsigned"}-latest.apk`);
215
+ await FsUtil.mkdirsAsync(targetOutPath);
216
+ // await FsUtil.copyAsync(
217
+ // path.resolve(this.cordovaPath, "platforms/android/app/build/outputs/apk", buildType, apkFileName),
218
+ // path.resolve(targetOutPath, distApkFileName)
219
+ // );
220
+ await FsUtil.copyAsync(
221
+ path.resolve(this.cordovaPath, "platforms/android/app/build/outputs/apk", buildType, apkFileName),
222
+ path.resolve(targetOutPath, latestDistApkFileName)
223
+ );
224
+ }
225
+
226
+ if (this._config.target?.android) {
227
+ // 자동업데이트를 위한 zip 파일 쓰기
228
+ const zip = new JSZip();
229
+ const resultFiles = await FsUtil.globAsync(path.resolve(this.cordovaPath, "platforms", "android", "app", "src", "main", "assets", "www", "**/*"), {
230
+ dot: true,
231
+ nodir: true
232
+ });
233
+ for (const resultFile of resultFiles) {
234
+ const contentBuffer = await FsUtil.readFileBufferAsync(resultFile);
235
+ const relativePath = path.relative(path.resolve(this.cordovaPath, "platforms", "android", "app", "src", "main", "assets", "www"), resultFile);
236
+ zip.file("/" + relativePath, contentBuffer);
237
+ }
238
+
239
+ const zipFileName = path.basename(`${this._npmConfig.version}.zip`);
240
+ const resultBuffer = await zip.generateAsync({ type: "nodebuffer" });
241
+
242
+ await FsUtil.writeFileAsync(path.resolve(outPath, "android/updates/", zipFileName), resultBuffer);
243
+ }
244
+ }
245
+
246
+ public static async runWebviewOnDeviceAsync(rootPath: string, platform: "browser" | "android", pkgName: string, url?: string): Promise<void> {
247
+ const cordovaPath = path.resolve(rootPath, `packages/${pkgName}/.cordova/`);
248
+
249
+ if (url !== undefined) {
250
+ await FsUtil.removeAsync(path.resolve(cordovaPath, "www"));
251
+ await FsUtil.mkdirsAsync(path.resolve(cordovaPath, "www"));
252
+ await FsUtil.writeFileAsync(path.resolve(cordovaPath, "www/index.html"), `'${url}'로 이동중... <script>setTimeout(function () {window.location.href = "${url.replace(/\/$/, "")}/${pkgName}/cordova/"}, 3000);</script>`.trim());
253
+ }
254
+
255
+ const binPath = path.resolve(process.cwd(), "node_modules/.bin/cordova.cmd");
256
+ // await SdProcess.spawnAsync(`${binPath} build ${platform}`, { cwd: cordovaPath }, true);
257
+ await SdProcess.spawnAsync(`${binPath} run ${platform} --device`, { cwd: cordovaPath }, true);
258
+ }
259
+ }