@simplysm/sd-cli 7.0.80 → 7.0.145
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/bin/sd-cli.mjs +36 -6
- package/dist/build-tool/SdCliCordova.d.ts +4 -4
- package/dist/build-tool/SdCliCordova.mjs +108 -107
- package/dist/build-tool/SdCliElectron.d.ts +9 -0
- package/dist/build-tool/SdCliElectron.mjs +66 -0
- package/dist/build-tool/SdCliGithubApi.d.ts +14 -0
- package/dist/build-tool/SdCliGithubApi.mjs +96 -0
- package/dist/builder/SdCliClientBuilder.mjs +151 -65
- package/dist/builder/SdCliJsLibBuilder.mjs +4 -4
- package/dist/builder/SdCliServerBuilder.mjs +6 -7
- package/dist/builder/SdCliTsLibBuilder.mjs +4 -4
- package/dist/commons.d.ts +38 -11
- package/dist/entry-points/SdCliLocalUpdate.mjs +3 -3
- package/dist/entry-points/SdCliWorkspace.mjs +48 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +3 -1
- package/dist/ng-tools/babel/SdCliBbFileMetadata.mjs +2 -2
- package/dist/ng-tools/babel/SdCliBbRootMetadata.d.ts +1 -0
- package/dist/ng-tools/babel/SdCliBbRootMetadata.mjs +29 -3
- package/dist/packages/SdCliPackage.mjs +13 -2
- package/package.json +13 -7
- package/src/bin/sd-cli.ts +46 -7
- package/src/build-tool/SdCliCordova.ts +133 -141
- package/src/build-tool/SdCliElectron.ts +76 -0
- package/src/build-tool/SdCliGithubApi.ts +117 -0
- package/src/builder/SdCliClientBuilder.ts +165 -65
- package/src/builder/SdCliJsLibBuilder.ts +3 -3
- package/src/builder/SdCliServerBuilder.ts +6 -7
- package/src/builder/SdCliTsLibBuilder.ts +3 -3
- package/src/commons.ts +35 -11
- package/src/entry-points/SdCliLocalUpdate.ts +2 -2
- package/src/entry-points/SdCliWorkspace.ts +47 -6
- package/src/index.ts +2 -0
- package/src/ng-tools/babel/SdCliBbFileMetadata.ts +1 -1
- package/src/ng-tools/babel/SdCliBbRootMetadata.ts +30 -2
- package/src/packages/SdCliPackage.ts +18 -1
- package/tsconfig.json +3 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { INpmConfig,
|
|
1
|
+
import { INpmConfig, ISdCliClientBuilderCordovaConfig } from "../commons";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { FsUtil, Logger, SdProcess } from "@simplysm/sd-core-node";
|
|
4
|
-
import { NotImplementError } from "@simplysm/sd-core-common";
|
|
5
4
|
import JSZip from "jszip";
|
|
5
|
+
import xml2js from "xml2js";
|
|
6
6
|
|
|
7
7
|
export class SdCliCordova {
|
|
8
8
|
protected readonly _logger: Logger;
|
|
@@ -12,11 +12,14 @@ export class SdCliCordova {
|
|
|
12
12
|
public readonly cordovaPath = path.resolve(this._rootPath, ".cordova");
|
|
13
13
|
private readonly _binPath = path.resolve(process.cwd(), "node_modules/.bin/cordova.cmd");
|
|
14
14
|
|
|
15
|
-
public get platforms():
|
|
16
|
-
return
|
|
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
|
+
];
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
public constructor(private readonly _rootPath: string, private readonly _config:
|
|
22
|
+
public constructor(private readonly _rootPath: string, private readonly _config: ISdCliClientBuilderCordovaConfig) {
|
|
20
23
|
this._npmConfig = FsUtil.readJson(path.resolve(this._rootPath, "package.json"));
|
|
21
24
|
this._logger = Logger.get(["simplysm", "sd-cli", this.constructor.name, this._npmConfig.name]);
|
|
22
25
|
}
|
|
@@ -52,156 +55,156 @@ export class SdCliCordova {
|
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
: undefined;
|
|
60
|
-
const alreadyPlugins = pluginsFetch != undefined
|
|
61
|
-
? Object.values(pluginsFetch)
|
|
62
|
-
.map((item: any) => (item.source.id !== undefined ? item.source.id.replace(/@.*$/, "") : item.source.url))
|
|
63
|
-
: [];
|
|
64
|
-
|
|
65
|
-
for (const plugin of this._config.plugins.distinct()) {
|
|
66
|
-
if (!alreadyPlugins.includes(plugin)) {
|
|
67
|
-
await this._execAsync(`${this._binPath} plugin add ${plugin}`, this.cordovaPath);
|
|
68
|
-
}
|
|
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);
|
|
69
62
|
}
|
|
70
63
|
}
|
|
71
64
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
await FsUtil.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (this._config.icon !== undefined && !configFileContent.includes("<icon")) {
|
|
86
|
-
configFileContent = configFileContent.replace("</widget>", " <icon src=\"res/icon/icon.png\" />\r\n</widget>");
|
|
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
|
+
}
|
|
87
78
|
}
|
|
88
79
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
if (!
|
|
92
|
-
|
|
93
|
-
"xmlns=\"http://www.w3.org/ns/widgets\"",
|
|
94
|
-
`xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android"`
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
if (!configFileContent.includes("application android:usesCleartextTraffic=\"true\" />")) {
|
|
98
|
-
if (configFileContent.includes("<platform name=\"android\">")) {
|
|
99
|
-
configFileContent = configFileContent.replace("<platform name=\"android\">", `<platform name="android">
|
|
100
|
-
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
|
|
101
|
-
<application android:usesCleartextTraffic="true" />
|
|
102
|
-
</edit-config>`);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
configFileContent = configFileContent.replace("<content src=\"index.html\" />", `<content src="index.html" />
|
|
106
|
-
<platform name="android">
|
|
107
|
-
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
|
|
108
|
-
<application android:usesCleartextTraffic="true" />
|
|
109
|
-
</edit-config>
|
|
110
|
-
</platform>`);
|
|
111
|
-
}
|
|
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);
|
|
112
84
|
}
|
|
113
85
|
}
|
|
114
86
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public async buildAsync(outPath: string): Promise<void> {
|
|
120
|
-
const packageType = this._config.buildOption?.bundle ? "bundle" : "apk";
|
|
121
|
-
|
|
122
|
-
// ANDROID 서명 처리
|
|
123
|
-
if (this._config.buildOption?.sign?.android) {
|
|
87
|
+
// ANDROID SIGN 파일 복사
|
|
88
|
+
if (this._config.target?.android?.sign) {
|
|
124
89
|
await FsUtil.copyAsync(
|
|
125
|
-
path.resolve(this._rootPath, this._config.
|
|
126
|
-
path.resolve(this.cordovaPath,
|
|
90
|
+
path.resolve(this._rootPath, this._config.target.android.sign.keystore),
|
|
91
|
+
path.resolve(this.cordovaPath, "android.keystore")
|
|
127
92
|
);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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 ? {
|
|
131
103
|
android: {
|
|
132
104
|
release: {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
+
} : {}
|
|
139
113
|
}
|
|
140
114
|
}
|
|
141
|
-
}
|
|
142
|
-
|
|
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"));
|
|
143
125
|
}
|
|
144
126
|
|
|
145
|
-
// CONFIG
|
|
127
|
+
// CONFIG: 초기값 백업
|
|
146
128
|
const configFilePath = path.resolve(this.cordovaPath, "config.xml");
|
|
147
|
-
|
|
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
|
+
}
|
|
148
146
|
|
|
149
|
-
// CONFIG: 접근허용
|
|
150
|
-
|
|
147
|
+
// CONFIG: 접근허용 세팅
|
|
148
|
+
configXml["widget"]["allow-navigation"] = [{ "$": { "href": "*://*/*" } }];
|
|
151
149
|
|
|
152
|
-
// CONFIG:
|
|
153
|
-
|
|
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);
|
|
154
175
|
|
|
155
|
-
//
|
|
156
|
-
await
|
|
176
|
+
// 각 플랫폼 www 준비
|
|
177
|
+
await this._execAsync(`${this._binPath} prepare`, this.cordovaPath);
|
|
178
|
+
}
|
|
157
179
|
|
|
180
|
+
public async buildAsync(outPath: string): Promise<void> {
|
|
158
181
|
// 실행
|
|
159
|
-
const buildType = this._config.
|
|
182
|
+
const buildType = this._config.debug ? "debug" : "release";
|
|
160
183
|
for (const platform of this.platforms) {
|
|
161
|
-
await this._execAsync(`${this._binPath} build ${platform} --${buildType}
|
|
184
|
+
await this._execAsync(`${this._binPath} build ${platform} --${buildType}`, this.cordovaPath);
|
|
162
185
|
}
|
|
163
186
|
|
|
164
|
-
// 결과물
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
else if (platform === "electron") {
|
|
182
|
-
const targetOutPath = path.resolve(outPath, platform);
|
|
183
|
-
const outFileName = `${this._config.appName} Setup ${this._npmConfig.version}.exe`;
|
|
184
|
-
const distFileName = outFileName;
|
|
185
|
-
const latestDistFileName = `${this._config.appName} Setup latest.exe`;
|
|
186
|
-
await FsUtil.mkdirsAsync(targetOutPath);
|
|
187
|
-
await FsUtil.copyAsync(
|
|
188
|
-
path.resolve(this.cordovaPath, "platforms/electron/build/", outFileName),
|
|
189
|
-
path.resolve(targetOutPath, distFileName)
|
|
190
|
-
);
|
|
191
|
-
await FsUtil.copyAsync(
|
|
192
|
-
path.resolve(this.cordovaPath, "platforms/electron/build/", outFileName),
|
|
193
|
-
path.resolve(targetOutPath, latestDistFileName)
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
throw new NotImplementError();
|
|
198
|
-
}
|
|
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
|
+
);
|
|
199
202
|
}
|
|
200
203
|
|
|
201
|
-
if (this.
|
|
204
|
+
if (this._config.target?.android) {
|
|
202
205
|
// 자동업데이트를 위한 zip 파일 쓰기
|
|
203
206
|
const zip = new JSZip();
|
|
204
|
-
const resultFiles = await FsUtil.globAsync(path.resolve(this.cordovaPath, "www", "**/*"), {
|
|
207
|
+
const resultFiles = await FsUtil.globAsync(path.resolve(this.cordovaPath, "platforms", "android", "app", "src", "main", "assets", "www", "**/*"), {
|
|
205
208
|
dot: true,
|
|
206
209
|
nodir: true
|
|
207
210
|
});
|
|
@@ -218,23 +221,12 @@ export class SdCliCordova {
|
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
223
|
|
|
221
|
-
public static async runWebviewOnDeviceAsync(
|
|
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
|
+
|
|
222
227
|
await FsUtil.removeAsync(path.resolve(cordovaPath, "www"));
|
|
223
228
|
await FsUtil.mkdirsAsync(path.resolve(cordovaPath, "www"));
|
|
224
|
-
await FsUtil.writeFileAsync(path.resolve(cordovaPath, "www/index.html"), `'${url}'로 이동중... <script>setTimeout(function () {window.location.href = "${url}"}, 3000);</script>`.trim());
|
|
225
|
-
|
|
226
|
-
// CONFIG
|
|
227
|
-
const configFilePath = path.resolve(cordovaPath, "config.xml");
|
|
228
|
-
let configFileContent = await FsUtil.readFileAsync(configFilePath);
|
|
229
|
-
|
|
230
|
-
// CONFIG: 접근허용 일단 모두 지우기
|
|
231
|
-
configFileContent = configFileContent.replace(/ {4}<allow-navigation href="[^"]*" \/>\n/g, "");
|
|
232
|
-
|
|
233
|
-
// CONFIG: 접근허용 등록
|
|
234
|
-
configFileContent = configFileContent.replace("</widget>", ` <allow-navigation href="*://*/*" />\n</widget>`);
|
|
235
|
-
|
|
236
|
-
// CONFIG: 파일쓰기
|
|
237
|
-
await FsUtil.writeFileAsync(configFilePath, configFileContent);
|
|
229
|
+
await FsUtil.writeFileAsync(path.resolve(cordovaPath, "www/index.html"), `'${url}'로 이동중... <script>setTimeout(function () {window.location.href = "${url.replace(/\/$/, "")}/${pkgName}/cordova/"}, 3000);</script>`.trim());
|
|
238
230
|
|
|
239
231
|
const binPath = path.resolve(process.cwd(), "node_modules/.bin/cordova.cmd");
|
|
240
232
|
await SdProcess.spawnAsync(`${binPath} run ${platform} --device`, { cwd: cordovaPath }, true);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { FsUtil, Logger, SdProcess } from "@simplysm/sd-core-node";
|
|
2
|
+
import { INpmConfig, ISdCliClientPackageConfig } from "../commons";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { SdCliConfigUtil } from "../utils/SdCliConfigUtil";
|
|
5
|
+
import ts from "typescript";
|
|
6
|
+
|
|
7
|
+
export class SdCliElectron {
|
|
8
|
+
private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
|
|
9
|
+
|
|
10
|
+
public constructor(private readonly _rootPath: string) {
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public async runWebviewOnDeviceAsync(pkgName: string, url: string, opt: { confFileRelPath: string; optNames: string[] }): Promise<void> {
|
|
14
|
+
this._logger.debug("프로젝트 설정 가져오기...");
|
|
15
|
+
const config = await SdCliConfigUtil.loadConfigAsync(path.resolve(this._rootPath, opt.confFileRelPath), true, opt.optNames);
|
|
16
|
+
const pkgConfig = config.packages[pkgName] as ISdCliClientPackageConfig | undefined;
|
|
17
|
+
if (!pkgConfig) throw new Error("패키지 설정을 찾을 수 없습니다.");
|
|
18
|
+
|
|
19
|
+
const electronConfig = pkgConfig.builder?.electron;
|
|
20
|
+
if (!electronConfig) throw new Error("ELECTRON 설정을 찾을 수 없습니다.");
|
|
21
|
+
|
|
22
|
+
const pkgRootPath = path.resolve(this._rootPath, `packages/${pkgName}`);
|
|
23
|
+
const electronSrcPath = path.resolve(pkgRootPath, `.electron/src`);
|
|
24
|
+
|
|
25
|
+
await FsUtil.removeAsync(electronSrcPath);
|
|
26
|
+
|
|
27
|
+
const npmConfig = (await FsUtil.readJsonAsync(path.resolve(pkgRootPath, `package.json`))) as INpmConfig;
|
|
28
|
+
const electronVersion = npmConfig.dependencies?.["electron"];
|
|
29
|
+
if (electronVersion === undefined) {
|
|
30
|
+
throw new Error("ELECTRON 빌드 패키지의 'dependencies'에는 'electron'이 반드시 포함되어야 합니다.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const dotenvVersion = npmConfig.dependencies?.["dotenv"];
|
|
34
|
+
if (dotenvVersion === undefined) {
|
|
35
|
+
throw new Error("ELECTRON 빌드 패키지의 'dependencies'에는 'dotenv'가 반드시 포함되어야 합니다.");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await FsUtil.writeJsonAsync(path.resolve(electronSrcPath, `package.json`), {
|
|
39
|
+
name: npmConfig.name,
|
|
40
|
+
version: npmConfig.version,
|
|
41
|
+
description: npmConfig.description,
|
|
42
|
+
main: "electron.js",
|
|
43
|
+
author: npmConfig.author,
|
|
44
|
+
license: npmConfig.license,
|
|
45
|
+
devDependencies: {
|
|
46
|
+
"electron": electronVersion.replace("^", "")
|
|
47
|
+
},
|
|
48
|
+
dependencies: {
|
|
49
|
+
"dotenv": dotenvVersion
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (FsUtil.exists(path.resolve(pkgRootPath, "src/favicon.ico"))) {
|
|
54
|
+
await FsUtil.copyAsync(path.resolve(pkgRootPath, "src/favicon.ico"), path.resolve(electronSrcPath, "favicon.ico"));
|
|
55
|
+
}
|
|
56
|
+
if (FsUtil.exists(path.resolve(pkgRootPath, "src/assets"))) {
|
|
57
|
+
await FsUtil.copyAsync(path.resolve(pkgRootPath, "src/assets"), path.resolve(electronSrcPath, "assets"));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await FsUtil.writeFileAsync(path.resolve(electronSrcPath, `.env`), [
|
|
61
|
+
"NODE_ENV=development",
|
|
62
|
+
`SD_TITLE=${npmConfig.description}`,
|
|
63
|
+
`SD_VERSION=${npmConfig.version}`,
|
|
64
|
+
`SD_ELECTRON_DEV_URL=${url.replace(/\/$/, "")}/${pkgName}/electron/`,
|
|
65
|
+
(electronConfig.icon !== undefined) ? `SD_ELECTRON_ICON=${electronConfig.icon}` : `SD_ELECTRON_ICON=favicon.ico`,
|
|
66
|
+
...(pkgConfig.env !== undefined) ? Object.keys(pkgConfig.env).map((key) => `${key}=${pkgConfig.env![key]}`) : []
|
|
67
|
+
].filterExists().join("\n"));
|
|
68
|
+
|
|
69
|
+
let electronTsFileContent = await FsUtil.readFileAsync(path.resolve(pkgRootPath, `src/electron.ts`));
|
|
70
|
+
electronTsFileContent = "require(\"dotenv\").config({ path: `${__dirname}\\\\.env` });\n" + electronTsFileContent;
|
|
71
|
+
const result = ts.transpileModule(electronTsFileContent, { compilerOptions: { module: ts.ModuleKind.CommonJS } });
|
|
72
|
+
await FsUtil.writeFileAsync(path.resolve(electronSrcPath, "electron.js"), result.outputText);
|
|
73
|
+
|
|
74
|
+
await SdProcess.spawnAsync(`${path.resolve(this._rootPath, "node_modules/.bin/electron.cmd")} ${electronSrcPath}`, { cwd: electronSrcPath }, true);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import https from "https";
|
|
2
|
+
import { SdProcess } from "@simplysm/sd-core-node";
|
|
3
|
+
import mime from "mime";
|
|
4
|
+
|
|
5
|
+
export class SdCliGithubApi {
|
|
6
|
+
public constructor(private readonly _apiKey: string,
|
|
7
|
+
private readonly _repoOwner: string,
|
|
8
|
+
private readonly _repoName: string) {
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public async uploadAsync(version: string, files: { name: string; buffer: Buffer }[]): Promise<void> {
|
|
12
|
+
await this._pushAllAsync();
|
|
13
|
+
|
|
14
|
+
const releaseId = await this._createReleaseTagAsync(version);
|
|
15
|
+
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
await this._uploadFileAsync(releaseId, file.name, file.buffer);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private async _uploadFileAsync(releaseId: number, fileName: string, buffer: Buffer): Promise<void> {
|
|
22
|
+
const contentLength = buffer.length;
|
|
23
|
+
|
|
24
|
+
await new Promise<void>((resolve, reject) => {
|
|
25
|
+
const req = https.request(
|
|
26
|
+
`https://uploads.github.com/repos/${this._repoOwner}/${this._repoName}/releases/${releaseId}/assets?name=${fileName}&label=${fileName}`,
|
|
27
|
+
{
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: {
|
|
30
|
+
"Authorization": `token ${this._apiKey}`,
|
|
31
|
+
"Accept": "application/vnd.github.v3+json",
|
|
32
|
+
"User-Agent": "@simplysm/sd-cli:publish",
|
|
33
|
+
"Content-Length": contentLength,
|
|
34
|
+
"Content-Type": mime.getType(fileName)!
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
(res) => {
|
|
38
|
+
let dataBuffer = Buffer.from([]);
|
|
39
|
+
res.on("data", data => {
|
|
40
|
+
dataBuffer = Buffer.concat([dataBuffer, data]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
res.on("end", () => {
|
|
44
|
+
if (res.statusCode !== 201) {
|
|
45
|
+
const errObj = JSON.parse(dataBuffer.toString());
|
|
46
|
+
throw new Error(errObj.message + "(" + errObj.documentation_url + ")");
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
resolve();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
req.on("error", (error) => {
|
|
56
|
+
reject(error);
|
|
57
|
+
});
|
|
58
|
+
req.write(buffer);
|
|
59
|
+
req.end();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async _createReleaseTagAsync(ver: string): Promise<number> {
|
|
64
|
+
const currentBranch = (await SdProcess.spawnAsync("git branch --show-current")).trim();
|
|
65
|
+
|
|
66
|
+
return await new Promise<number>((resolve, reject) => {
|
|
67
|
+
const req = https.request(
|
|
68
|
+
`https://api.github.com/repos/${this._repoOwner}/${this._repoName}/releases`,
|
|
69
|
+
{
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: {
|
|
72
|
+
"Authorization": `token ${this._apiKey}`,
|
|
73
|
+
"Accept": "application/vnd.github.v3+json",
|
|
74
|
+
"User-Agent": "@simplysm/sd-cli:publish"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
(res) => {
|
|
78
|
+
let dataBuffer = Buffer.from([]);
|
|
79
|
+
res.on("data", data => {
|
|
80
|
+
dataBuffer = Buffer.concat([dataBuffer, data]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
res.on("end", () => {
|
|
84
|
+
if (res.statusCode !== 201) {
|
|
85
|
+
const errObj = JSON.parse(dataBuffer.toString());
|
|
86
|
+
throw new Error(errObj.message + "(" + errObj.documentation_url + ")");
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// console.log(JSON.parse(dataBuffer.toString()));
|
|
90
|
+
const resData = JSON.parse(dataBuffer.toString());
|
|
91
|
+
resolve(resData.id);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
req.on("error", (error) => {
|
|
98
|
+
reject(error);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
req.write(JSON.stringify({
|
|
102
|
+
tag_name: `v${ver}`,
|
|
103
|
+
target_commitish: currentBranch,
|
|
104
|
+
name: `v${ver}`,
|
|
105
|
+
body: `v${ver}`,
|
|
106
|
+
draft: false,
|
|
107
|
+
prerelease: false
|
|
108
|
+
}));
|
|
109
|
+
|
|
110
|
+
req.end();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private async _pushAllAsync(): Promise<void> {
|
|
115
|
+
await SdProcess.spawnAsync("git push --tags", undefined, true);
|
|
116
|
+
}
|
|
117
|
+
}
|