@simplysm/sd-cli 11.0.6 → 11.0.9
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/build-cluster.js +2 -2
- package/dist/build-cluster.js.map +1 -1
- package/dist/build-tools/SdCliCordova.d.ts +21 -0
- package/dist/build-tools/SdCliCordova.js +182 -0
- package/dist/build-tools/SdCliCordova.js.map +1 -0
- package/dist/build-tools/SdNgBundler.d.ts +5 -4
- package/dist/build-tools/SdNgBundler.js +49 -19
- package/dist/build-tools/SdNgBundler.js.map +1 -1
- package/dist/build-tools/SdTsBundler.d.ts +0 -1
- package/dist/build-tools/SdTsBundler.js +5 -4
- package/dist/build-tools/SdTsBundler.js.map +1 -1
- package/dist/builders/SdCliClientBuilder.d.ts +1 -1
- package/dist/builders/SdCliClientBuilder.js +22 -2
- package/dist/builders/SdCliClientBuilder.js.map +1 -1
- package/dist/builders/SdCliServerBuilder.d.ts +0 -1
- package/dist/builders/SdCliServerBuilder.js +98 -79
- package/dist/builders/SdCliServerBuilder.js.map +1 -1
- package/dist/builders/SdCliTsLibBuilder.d.ts +2 -3
- package/dist/builders/SdCliTsLibBuilder.js +1 -2
- package/dist/builders/SdCliTsLibBuilder.js.map +1 -1
- package/dist/commons.d.ts +22 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/sd-cli.js +24 -0
- package/dist/sd-cli.js.map +1 -1
- package/lib/cordova-entry.js +22 -0
- package/package.json +14 -11
- package/src/build-cluster.ts +2 -2
- package/src/build-tools/SdCliCordova.ts +240 -0
- package/src/build-tools/SdNgBundler.ts +64 -20
- package/src/build-tools/SdTsBundler.ts +6 -5
- package/src/builders/SdCliClientBuilder.ts +25 -4
- package/src/builders/SdCliServerBuilder.ts +16 -16
- package/src/builders/SdCliTsLibBuilder.ts +2 -3
- package/src/commons.ts +23 -0
- package/src/index.ts +1 -0
- package/src/sd-cli.ts +28 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {INpmConfig, ISdCliClientBuilderCordovaConfig} from "../commons";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import {FsUtil, Logger, SdProcess} from "@simplysm/sd-core-node";
|
|
4
|
+
import xml2js from "xml2js";
|
|
5
|
+
|
|
6
|
+
const BIN_PATH = path.resolve(process.cwd(), "node_modules/.bin/cordova.cmd");
|
|
7
|
+
|
|
8
|
+
export class SdCliCordova {
|
|
9
|
+
protected readonly _logger = Logger.get(["simplysm", "sd-cli", "SdCliCordova"]);
|
|
10
|
+
|
|
11
|
+
private readonly _platforms: string[];
|
|
12
|
+
private readonly _npmConfig: INpmConfig;
|
|
13
|
+
|
|
14
|
+
public constructor(private readonly _opt: {
|
|
15
|
+
pkgPath: string;
|
|
16
|
+
config: ISdCliClientBuilderCordovaConfig;
|
|
17
|
+
cordovaPath: string;
|
|
18
|
+
}) {
|
|
19
|
+
this._platforms = Object.keys(this._opt.config.platform ?? {browser: {}});
|
|
20
|
+
this._npmConfig = FsUtil.readJson(path.resolve(this._opt.pkgPath, "package.json"));
|
|
21
|
+
// this._logger = Logger.get(["simplysm", "sd-cli", this.constructor.name, this._npmConfig.name]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private async _execAsync(cmd: string, cwd: string): Promise<void> {
|
|
25
|
+
this._logger.debug(cmd);
|
|
26
|
+
const msg = await SdProcess.spawnAsync(cmd, {cwd});
|
|
27
|
+
this._logger.debug(msg);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public async initializeAsync(): Promise<void> {
|
|
31
|
+
if (FsUtil.exists(this._opt.cordovaPath)) {
|
|
32
|
+
this._logger.log("이미 생성되어있는 '.cordova'를 사용합니다.");
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
await this._execAsync(`${BIN_PATH} telemetry on`, this._opt.pkgPath);
|
|
36
|
+
|
|
37
|
+
// 프로젝트 생성
|
|
38
|
+
await this._execAsync(`${BIN_PATH} create "${this._opt.cordovaPath}" "${this._opt.config.appId}" "${this._opt.config.appName}"`, process.cwd());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// platforms 폴더 혹시 없으면 생성
|
|
42
|
+
await FsUtil.mkdirsAsync(path.resolve(this._opt.cordovaPath, "platforms"));
|
|
43
|
+
|
|
44
|
+
// www 폴더 혹시 없으면 생성
|
|
45
|
+
await FsUtil.mkdirsAsync(path.resolve(this._opt.cordovaPath, "www"));
|
|
46
|
+
|
|
47
|
+
// 미설치 빌드 플랫폼 신규 생성
|
|
48
|
+
const alreadyPlatforms = await FsUtil.readdirAsync(path.resolve(this._opt.cordovaPath, "platforms"));
|
|
49
|
+
for (const platform of this._platforms) {
|
|
50
|
+
if (!alreadyPlatforms.includes(platform)) {
|
|
51
|
+
await this._execAsync(`${BIN_PATH} platform add ${platform}`, this._opt.cordovaPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 설치 미빌드 플랫폼 삭제
|
|
56
|
+
for (const alreadyPlatform of alreadyPlatforms) {
|
|
57
|
+
if (!this._platforms.includes(alreadyPlatform)) {
|
|
58
|
+
await this._execAsync(`${BIN_PATH} platform remove ${alreadyPlatform}`, this._opt.cordovaPath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 설치된 미사용 플러그인 삭제
|
|
63
|
+
const pluginsFetch = FsUtil.exists(path.resolve(this._opt.cordovaPath, "plugins/fetch.json"))
|
|
64
|
+
? await FsUtil.readJsonAsync(path.resolve(this._opt.cordovaPath, "plugins/fetch.json"))
|
|
65
|
+
: undefined;
|
|
66
|
+
const alreadyPluginIds = pluginsFetch != undefined
|
|
67
|
+
? Object.values(pluginsFetch)
|
|
68
|
+
.map((item: any) => (item.source.id !== undefined ? item.source.id : item.source.url))
|
|
69
|
+
: [];
|
|
70
|
+
const usePlugins = ["cordova-plugin-ionic-webview", ...this._opt.config.plugins ?? []].distinct();
|
|
71
|
+
|
|
72
|
+
for (const alreadyPluginId of alreadyPluginIds) {
|
|
73
|
+
let hasPlugin = false;
|
|
74
|
+
for (const usePlugin of usePlugins) {
|
|
75
|
+
if (
|
|
76
|
+
(usePlugin.includes("@") && alreadyPluginId === usePlugin) ||
|
|
77
|
+
(!usePlugin.includes("@") && alreadyPluginId.replace(/@.*$/, "") === usePlugin)
|
|
78
|
+
) {
|
|
79
|
+
hasPlugin = true;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!hasPlugin) {
|
|
85
|
+
await this._execAsync(`${BIN_PATH} plugin remove ${alreadyPluginId.replace(/@.*$/, "")}`, this._opt.cordovaPath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 미설치 플러그인들 설치
|
|
90
|
+
for (const usePlugin of usePlugins) {
|
|
91
|
+
if (
|
|
92
|
+
(usePlugin.includes("@") && !alreadyPluginIds.includes(usePlugin)) ||
|
|
93
|
+
(!usePlugin.includes("@") && !alreadyPluginIds.map((alreadyPluginId) => alreadyPluginId.replace(/@.*$/, "")).includes(usePlugin))
|
|
94
|
+
) {
|
|
95
|
+
await this._execAsync(`${BIN_PATH} plugin add ${usePlugin}`, this._opt.cordovaPath);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ANDROID SIGN 파일 복사
|
|
100
|
+
if (this._opt.config.platform?.android?.sign) {
|
|
101
|
+
await FsUtil.copyAsync(
|
|
102
|
+
path.resolve(this._opt.pkgPath, this._opt.config.platform.android.sign.keystore),
|
|
103
|
+
path.resolve(this._opt.cordovaPath, "android.keystore")
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
await FsUtil.removeAsync(path.resolve(this._opt.cordovaPath, "android.keystore"));
|
|
108
|
+
// SIGN을 안쓸경우 아래 파일이 생성되어 있으면 오류남
|
|
109
|
+
await FsUtil.removeAsync(path.resolve(this._opt.cordovaPath, "platforms/android/release-signing.properties"));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 빌드 옵션 파일 생성
|
|
113
|
+
await FsUtil.writeJsonAsync(
|
|
114
|
+
path.resolve(this._opt.cordovaPath, "build.json"),
|
|
115
|
+
{
|
|
116
|
+
...this._opt.config.platform?.android ? {
|
|
117
|
+
android: {
|
|
118
|
+
release: {
|
|
119
|
+
packageType: this._opt.config.platform.android.bundle ? "bundle" : "apk",
|
|
120
|
+
...this._opt.config.platform.android.sign ? {
|
|
121
|
+
keystore: path.resolve(this._opt.cordovaPath, "android.keystore"),
|
|
122
|
+
storePassword: this._opt.config.platform.android.sign.storePassword,
|
|
123
|
+
alias: this._opt.config.platform.android.sign.alias,
|
|
124
|
+
password: this._opt.config.platform.android.sign.password,
|
|
125
|
+
keystoreType: this._opt.config.platform.android.sign.keystoreType
|
|
126
|
+
} : {}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} : {}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// ICON 파일 복사
|
|
134
|
+
if (this._opt.config.icon !== undefined) {
|
|
135
|
+
await FsUtil.copyAsync(path.resolve(this._opt.cordovaPath, this._opt.config.icon), path.resolve(this._opt.cordovaPath, "res", "icon.png"));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
await FsUtil.removeAsync(path.resolve(this._opt.cordovaPath, "res", "icon.png"));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// CONFIG: 초기값 백업
|
|
142
|
+
const configFilePath = path.resolve(this._opt.cordovaPath, "config.xml");
|
|
143
|
+
const configBackFilePath = path.resolve(this._opt.cordovaPath, "config.xml.bak");
|
|
144
|
+
if (!FsUtil.exists(configBackFilePath)) {
|
|
145
|
+
await FsUtil.copyAsync(configFilePath, configBackFilePath);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// CONFIG: 초기값 읽기
|
|
149
|
+
const configFileContent = await FsUtil.readFileAsync(configBackFilePath);
|
|
150
|
+
const configXml = await xml2js.parseStringPromise(configFileContent);
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
// CONFIG: 버전 설정
|
|
154
|
+
configXml.widget.$.version = this._npmConfig.version;
|
|
155
|
+
|
|
156
|
+
// CONFIG: ICON 설정
|
|
157
|
+
if (this._opt.config.icon !== undefined) {
|
|
158
|
+
configXml["widget"]["icon"] = [{"$": {"src": "res/icon.png"}}];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// CONFIG: 접근허용 세팅
|
|
162
|
+
configXml["widget"]["access"] = [{"$": {"origin": "*"}}];
|
|
163
|
+
configXml["widget"]["allow-navigation"] = [{"$": {"href": "*"}}];
|
|
164
|
+
configXml["widget"]["allow-intent"] = [{"$": {"href": "*"}}];
|
|
165
|
+
configXml["widget"]["preference"] = [{"$": {"name": "MixedContentMode", "value": "0"}}];
|
|
166
|
+
|
|
167
|
+
// CONFIG: ANDROID usesCleartextTraffic 설정
|
|
168
|
+
if (this._opt.config.platform?.android) {
|
|
169
|
+
configXml.widget.$["xmlns:android"] = "http://schemas.android.com/apk/res/android";
|
|
170
|
+
|
|
171
|
+
configXml["widget"]["platform"] = configXml["widget"]["platform"] ?? [];
|
|
172
|
+
configXml["widget"]["platform"].push({
|
|
173
|
+
"$": {
|
|
174
|
+
"name": "android"
|
|
175
|
+
},
|
|
176
|
+
"edit-config": [{
|
|
177
|
+
"$": {
|
|
178
|
+
"file": "app/src/main/AndroidManifest.xml",
|
|
179
|
+
"mode": "merge",
|
|
180
|
+
"target": "/manifest/application"
|
|
181
|
+
},
|
|
182
|
+
"application": [{
|
|
183
|
+
"$": {
|
|
184
|
+
"android:usesCleartextTraffic": "true"
|
|
185
|
+
}
|
|
186
|
+
}]
|
|
187
|
+
}]
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// CONFIG: 파일 새로 쓰기
|
|
192
|
+
const configResultContent = new xml2js.Builder().buildObject(configXml);
|
|
193
|
+
await FsUtil.writeFileAsync(configFilePath, configResultContent);
|
|
194
|
+
|
|
195
|
+
// 각 플랫폼 www 준비
|
|
196
|
+
await this._execAsync(`${BIN_PATH} prepare`, this._opt.cordovaPath);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public async buildAsync(outPath: string): Promise<void> {
|
|
200
|
+
// 실행
|
|
201
|
+
const buildType = this._opt.config.debug ? "debug" : "release";
|
|
202
|
+
for (const platform of this._platforms) {
|
|
203
|
+
await this._execAsync(`${BIN_PATH} build ${platform} --${buildType}`, this._opt.cordovaPath);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 결과물 복사: ANDROID
|
|
207
|
+
if (this._opt.config.platform?.android) {
|
|
208
|
+
const targetOutPath = path.resolve(outPath, "android");
|
|
209
|
+
const apkFileName = this._opt.config.platform.android.sign ? `app-${buildType}.apk` : `app-${buildType}-unsigned.apk`;
|
|
210
|
+
const latestDistApkFileName = path.basename(`${this._opt.config.appName}${this._opt.config.platform.android.sign ? "" : "-unsigned"}-latest.apk`);
|
|
211
|
+
await FsUtil.mkdirsAsync(targetOutPath);
|
|
212
|
+
await FsUtil.copyAsync(
|
|
213
|
+
path.resolve(this._opt.cordovaPath, "platforms/android/app/build/outputs/apk", buildType, apkFileName),
|
|
214
|
+
path.resolve(targetOutPath, latestDistApkFileName)
|
|
215
|
+
);
|
|
216
|
+
// 자동업데이트를 위한 파일 쓰기
|
|
217
|
+
await FsUtil.copyAsync(
|
|
218
|
+
path.resolve(this._opt.cordovaPath, "platforms/android/app/build/outputs/apk", buildType, apkFileName),
|
|
219
|
+
path.resolve(path.resolve(targetOutPath, "updates"), this._npmConfig.version + ".apk")
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
public static async runWebviewOnDeviceAsync(opt: {
|
|
225
|
+
platform: string,
|
|
226
|
+
pkgName: string,
|
|
227
|
+
url?: string
|
|
228
|
+
}): Promise<void> {
|
|
229
|
+
const cordovaPath = path.resolve(process.cwd(), `packages/${opt.pkgName}/.cache/cordova/`);
|
|
230
|
+
|
|
231
|
+
if (opt.url !== undefined) {
|
|
232
|
+
await FsUtil.removeAsync(path.resolve(cordovaPath, "www"));
|
|
233
|
+
await FsUtil.mkdirsAsync(path.resolve(cordovaPath, "www"));
|
|
234
|
+
await FsUtil.writeFileAsync(path.resolve(cordovaPath, "www/index.html"), `'${opt.url}'로 이동중... <script>setTimeout(function () {window.location.href = "${opt.url.replace(/\/$/, "")}/${opt.pkgName}/cordova/"}, 3000);</script>`.trim());
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const binPath = path.resolve(process.cwd(), "node_modules/.bin/cordova.cmd");
|
|
238
|
+
await SdProcess.spawnAsync(`${binPath} run ${opt.platform} --device`, {cwd: cordovaPath}, true);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -17,12 +17,13 @@ import {
|
|
|
17
17
|
import nodeStdLibBrowser from "node-stdlib-browser";
|
|
18
18
|
import nodeStdLibBrowserPlugin from "node-stdlib-browser/helpers/esbuild/plugin";
|
|
19
19
|
import {ExecutionResult} from "@angular-devkit/build-angular/src/tools/esbuild/bundler-execution-result";
|
|
20
|
-
import {INpmConfig, ISdCliPackageBuildResult} from "../commons";
|
|
20
|
+
import {INpmConfig, ISdCliClientPackageConfig, ISdCliPackageBuildResult} from "../commons";
|
|
21
21
|
import {generateIndexHtml} from "@angular-devkit/build-angular/src/tools/esbuild/index-html-generator";
|
|
22
22
|
import {copyAssets} from "@angular-devkit/build-angular/src/utils/copy-assets";
|
|
23
23
|
import {extractLicenses} from "@angular-devkit/build-angular/src/tools/esbuild/license-extractor";
|
|
24
24
|
import {augmentAppWithServiceWorkerEsbuild} from "@angular-devkit/build-angular/src/utils/service-worker";
|
|
25
25
|
import {createGlobalStylesBundleOptions} from "@angular-devkit/build-angular/src/tools/esbuild/global-styles";
|
|
26
|
+
import {Entrypoint} from "@angular-devkit/build-angular/src/utils/index-file/augment-index-html";
|
|
26
27
|
|
|
27
28
|
export class SdNgBundler {
|
|
28
29
|
private readonly _logger = Logger.get(["simplysm", "sd-cli", "SdNgEsbuildBuilder"]);
|
|
@@ -34,8 +35,14 @@ export class SdNgBundler {
|
|
|
34
35
|
|
|
35
36
|
private readonly _outputCache = new Map<string, string | number>();
|
|
36
37
|
|
|
37
|
-
public constructor(
|
|
38
|
-
|
|
38
|
+
public constructor(opt: {
|
|
39
|
+
dev: boolean,
|
|
40
|
+
builderType: keyof ISdCliClientPackageConfig["builder"],
|
|
41
|
+
pkgPath: string,
|
|
42
|
+
cordovaPlatforms: string[] | undefined,
|
|
43
|
+
outputPath: string
|
|
44
|
+
}) {
|
|
45
|
+
this._options = this._getOptions(opt);
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
public removeCache(filePaths: string[]): void {
|
|
@@ -58,7 +65,7 @@ export class SdNgBundler {
|
|
|
58
65
|
|
|
59
66
|
const results = [
|
|
60
67
|
...bundlingResult.warnings.map((warn) => ({
|
|
61
|
-
filePath: warn.location?.file !== undefined ? path.resolve(this.
|
|
68
|
+
filePath: warn.location?.file !== undefined ? path.resolve(this._options.workspaceRoot, warn.location.file) : undefined,
|
|
62
69
|
line: warn.location?.line,
|
|
63
70
|
char: warn.location?.column,
|
|
64
71
|
code: undefined,
|
|
@@ -67,7 +74,7 @@ export class SdNgBundler {
|
|
|
67
74
|
type: "build" as const
|
|
68
75
|
})),
|
|
69
76
|
...bundlingResult.errors?.map((err) => ({
|
|
70
|
-
filePath: err.location?.file !== undefined ? path.resolve(this.
|
|
77
|
+
filePath: err.location?.file !== undefined ? path.resolve(this._options.workspaceRoot, err.location.file) : undefined,
|
|
71
78
|
line: err.location?.line,
|
|
72
79
|
char: err.location?.column !== undefined ? err.location.column + 1 : undefined,
|
|
73
80
|
code: undefined,
|
|
@@ -77,10 +84,10 @@ export class SdNgBundler {
|
|
|
77
84
|
})) ?? []
|
|
78
85
|
];
|
|
79
86
|
const watchFilePaths = [
|
|
80
|
-
...this._sourceFileCache.
|
|
87
|
+
...this._sourceFileCache.keys(),
|
|
81
88
|
...this._sourceFileCache.babelFileCache.keys()
|
|
82
89
|
].map((item) => path.resolve(item));
|
|
83
|
-
let affectedSourceFilePaths = watchFilePaths.filter((item) => PathUtil.isChildPath(item, this.
|
|
90
|
+
let affectedSourceFilePaths = watchFilePaths.filter((item) => PathUtil.isChildPath(item, this._options.workspaceRoot));
|
|
84
91
|
if (bundlingResult.errors) {
|
|
85
92
|
return {
|
|
86
93
|
filePaths: watchFilePaths,
|
|
@@ -92,8 +99,8 @@ export class SdNgBundler {
|
|
|
92
99
|
const depsMap = new Map<string, Set<string>>();
|
|
93
100
|
for (const entry of Object.entries(bundlingResult.metafile.inputs)) {
|
|
94
101
|
for (const imp of entry[1].imports) {
|
|
95
|
-
const deps = depsMap.getOrCreate(path.resolve(this.
|
|
96
|
-
deps.add(path.resolve(this.
|
|
102
|
+
const deps = depsMap.getOrCreate(path.resolve(this._options.workspaceRoot, imp.path), new Set<string>());
|
|
103
|
+
deps.add(path.resolve(this._options.workspaceRoot, entry[0]));
|
|
97
104
|
}
|
|
98
105
|
}
|
|
99
106
|
|
|
@@ -115,9 +122,10 @@ export class SdNgBundler {
|
|
|
115
122
|
if (this._sourceFileCache.modifiedFiles.size > 0) {
|
|
116
123
|
const affectedFilePathSet = new Set<string>();
|
|
117
124
|
for (const modFile of this._sourceFileCache.modifiedFiles) {
|
|
125
|
+
affectedFilePathSet.add(path.resolve(modFile));
|
|
118
126
|
affectedFilePathSet.adds(...searchAffectedFiles(path.resolve(modFile)));
|
|
119
127
|
}
|
|
120
|
-
affectedSourceFilePaths = Array.from(affectedFilePathSet.values()).filter((item) => PathUtil.isChildPath(item, this.
|
|
128
|
+
affectedSourceFilePaths = Array.from(affectedFilePathSet.values()).filter((item) => PathUtil.isChildPath(item, this._options.workspaceRoot));
|
|
121
129
|
}
|
|
122
130
|
|
|
123
131
|
const executionResult = new ExecutionResult(this._contexts, this._sourceFileCache);
|
|
@@ -176,9 +184,8 @@ export class SdNgBundler {
|
|
|
176
184
|
|
|
177
185
|
//-- write
|
|
178
186
|
|
|
179
|
-
const distPath = path.resolve(this._options.outputPath, "dist", ...(this._opt.builderType === "web" ? [] : [this._opt.builderType]));
|
|
180
187
|
for (const outputFile of executionResult.outputFiles) {
|
|
181
|
-
const distFilePath = path.resolve(
|
|
188
|
+
const distFilePath = path.resolve(this._options.outputPath, outputFile.path);
|
|
182
189
|
|
|
183
190
|
const prev = this._outputCache.get(distFilePath);
|
|
184
191
|
if (prev !== Buffer.from(outputFile.contents).toString("base64")) {
|
|
@@ -190,7 +197,7 @@ export class SdNgBundler {
|
|
|
190
197
|
const prev = this._outputCache.get(assetFile.source);
|
|
191
198
|
const curr = FsUtil.lstat(assetFile.source).mtime.getTime();
|
|
192
199
|
if (prev !== curr) {
|
|
193
|
-
await FsUtil.copyAsync(assetFile.source, path.resolve(
|
|
200
|
+
await FsUtil.copyAsync(assetFile.source, path.resolve(this._options.outputPath, assetFile.destination));
|
|
194
201
|
this._outputCache.set(assetFile.source, curr);
|
|
195
202
|
}
|
|
196
203
|
}
|
|
@@ -276,7 +283,7 @@ export class SdNgBundler {
|
|
|
276
283
|
minifySyntax: !this._options.watch,
|
|
277
284
|
minifyWhitespace: !this._options.watch,
|
|
278
285
|
pure: ['forwardRef'],
|
|
279
|
-
outdir: this._options.
|
|
286
|
+
outdir: this._options.workspaceRoot,
|
|
280
287
|
outExtension: undefined,
|
|
281
288
|
sourcemap: this._options.watch,
|
|
282
289
|
splitting: true,
|
|
@@ -292,6 +299,7 @@ export class SdNgBundler {
|
|
|
292
299
|
process: 'process',
|
|
293
300
|
Buffer: 'Buffer',
|
|
294
301
|
'process.env.SD_VERSION': JSON.stringify(pkgNpmConf.version),
|
|
302
|
+
"process.env.NODE_ENV": JSON.stringify(this._options.watch ? "development" : "production")
|
|
295
303
|
},
|
|
296
304
|
platform: 'browser',
|
|
297
305
|
mainFields: ['es2020', 'es2015', 'browser', 'module', 'main'],
|
|
@@ -358,7 +366,13 @@ export class SdNgBundler {
|
|
|
358
366
|
};
|
|
359
367
|
}
|
|
360
368
|
|
|
361
|
-
private _getOptions(opt: {
|
|
369
|
+
private _getOptions(opt: {
|
|
370
|
+
dev: boolean,
|
|
371
|
+
builderType: keyof ISdCliClientPackageConfig["builder"],
|
|
372
|
+
pkgPath: string,
|
|
373
|
+
cordovaPlatforms: string[] | undefined,
|
|
374
|
+
outputPath: string,
|
|
375
|
+
}): NormalizedApplicationBuildOptions {
|
|
362
376
|
const mainFilePath = path.resolve(opt.pkgPath, "src/main.ts");
|
|
363
377
|
const tsconfigFilePath = path.resolve(opt.pkgPath, "tsconfig.json");
|
|
364
378
|
const indexHtmlFilePath = path.resolve(opt.pkgPath, "src/index.html");
|
|
@@ -398,20 +412,47 @@ export class SdNgBundler {
|
|
|
398
412
|
verbose: false,
|
|
399
413
|
watch: opt.dev,
|
|
400
414
|
workspaceRoot: opt.pkgPath,
|
|
401
|
-
entryPoints: {
|
|
415
|
+
entryPoints: {
|
|
416
|
+
main: mainFilePath,
|
|
417
|
+
...opt.builderType === "cordova" ? {
|
|
418
|
+
"cordova-entry": path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../../lib/cordova-entry.js`)
|
|
419
|
+
} : {}
|
|
420
|
+
},
|
|
402
421
|
optimizationOptions: {
|
|
403
422
|
scripts: !opt.dev,
|
|
404
423
|
styles: {minify: !opt.dev, inlineCritical: !opt.dev},
|
|
405
424
|
fonts: {inline: !opt.dev}
|
|
406
425
|
},
|
|
407
|
-
outputPath: opt.
|
|
426
|
+
outputPath: opt.outputPath,
|
|
408
427
|
outExtension: undefined,
|
|
409
428
|
sourcemapOptions: {vendor: false, hidden: false, scripts: opt.dev, styles: opt.dev},
|
|
410
429
|
tsconfig: tsconfigFilePath,
|
|
411
430
|
projectRoot: opt.pkgPath,
|
|
412
431
|
assets: [
|
|
413
|
-
{
|
|
414
|
-
{
|
|
432
|
+
{input: 'src', glob: 'favicon.ico', output: ''},
|
|
433
|
+
{input: 'src\\assets', glob: '**/*', output: 'assets'},
|
|
434
|
+
...opt.dev && opt.cordovaPlatforms ? opt.cordovaPlatforms.mapMany((platform) => [
|
|
435
|
+
{
|
|
436
|
+
input: `.cache\\cordova\\platforms\\${platform}\\platform_www\\plugins`,
|
|
437
|
+
glob: '**/*',
|
|
438
|
+
output: `cordova-${platform}/plugins`
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
input: `.cache\\cordova\\platforms\\${platform}\\platform_www`,
|
|
442
|
+
glob: 'cordova.js',
|
|
443
|
+
output: `cordova-${platform}`
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
input: `.cache\\cordova\\platforms\\${platform}\\platform_www`,
|
|
447
|
+
glob: 'cordova_plugins.js',
|
|
448
|
+
output: `cordova-${platform}`
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
input: `.cache\\cordova\\platforms\\${platform}\\www`,
|
|
452
|
+
glob: 'config.xml',
|
|
453
|
+
output: `cordova-${platform}`
|
|
454
|
+
},
|
|
455
|
+
]) : []
|
|
415
456
|
],
|
|
416
457
|
outputNames: {bundles: '[name]', media: 'media/[name]'},
|
|
417
458
|
fileReplacements: undefined,
|
|
@@ -426,7 +467,10 @@ export class SdNgBundler {
|
|
|
426
467
|
['polyfills', true],
|
|
427
468
|
['styles', false],
|
|
428
469
|
['vendor', true],
|
|
429
|
-
['main', true]
|
|
470
|
+
['main', true],
|
|
471
|
+
...opt.builderType === "cordova" ? [
|
|
472
|
+
["cordova-entry", true] as Entrypoint
|
|
473
|
+
] : []
|
|
430
474
|
]
|
|
431
475
|
},
|
|
432
476
|
tailwindConfiguration: undefined
|
|
@@ -10,7 +10,7 @@ export class SdTsBundler {
|
|
|
10
10
|
dev: boolean;
|
|
11
11
|
pkgPath: string;
|
|
12
12
|
entryPoints: string[];
|
|
13
|
-
external?: string[];
|
|
13
|
+
// external?: string[];
|
|
14
14
|
}) {
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -64,14 +64,15 @@ export class SdTsBundler {
|
|
|
64
64
|
},
|
|
65
65
|
platform: "node",
|
|
66
66
|
logLevel: "silent",
|
|
67
|
-
|
|
67
|
+
packages: "external",
|
|
68
|
+
// external: this._opt.external,
|
|
68
69
|
banner: {
|
|
69
70
|
js: `
|
|
70
71
|
import __path__ from 'path';
|
|
71
72
|
import { fileURLToPath as __fileURLToPath__ } from 'url';
|
|
72
|
-
import { createRequire as __createRequire__ } from 'module';
|
|
73
|
-
|
|
74
|
-
const require = __createRequire__(import.meta.url);
|
|
73
|
+
// import { createRequire as __createRequire__ } from 'module';
|
|
74
|
+
//
|
|
75
|
+
// const require = __createRequire__(import.meta.url);
|
|
75
76
|
const __filename = __fileURLToPath__(import.meta.url);
|
|
76
77
|
const __dirname = __path__.dirname(__filename);`.trim()
|
|
77
78
|
},
|
|
@@ -3,15 +3,16 @@ import {FsUtil, Logger, PathUtil, SdFsWatcher} from "@simplysm/sd-core-node";
|
|
|
3
3
|
import {ISdCliBuilderResult, ISdCliClientPackageConfig, ISdCliConfig, ISdCliPackageBuildResult} from "../commons";
|
|
4
4
|
import {FunctionQueue, Wait} from "@simplysm/sd-core-common";
|
|
5
5
|
import path from "path";
|
|
6
|
-
import {SdTsCompiler} from "../build-tools/SdTsCompiler";
|
|
7
6
|
import {SdNgBundler} from "../build-tools/SdNgBundler";
|
|
8
7
|
import {SdLinter} from "../build-tools/SdLinter";
|
|
8
|
+
import {SdCliCordova} from "../build-tools/SdCliCordova";
|
|
9
9
|
|
|
10
|
+
// TODO: ROUTER 자동생성
|
|
10
11
|
export class SdCliClientBuilder extends EventEmitter {
|
|
11
12
|
private readonly _logger = Logger.get(["simplysm", "sd-cli", "SdCliClientBuilder"]);
|
|
12
13
|
private readonly _pkgConf: ISdCliClientPackageConfig;
|
|
13
14
|
private _builders?: SdNgBundler[];
|
|
14
|
-
private
|
|
15
|
+
private _cordova?: SdCliCordova;
|
|
15
16
|
|
|
16
17
|
public constructor(private readonly _projConf: ISdCliConfig,
|
|
17
18
|
private readonly _pkgPath: string) {
|
|
@@ -73,12 +74,27 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
73
74
|
affectedFilePaths: string[];
|
|
74
75
|
buildResults: ISdCliPackageBuildResult[];
|
|
75
76
|
}> {
|
|
77
|
+
const builderTypes = (Object.keys(this._pkgConf.builder ?? {web: {}}) as (keyof ISdCliClientPackageConfig["builder"])[]);
|
|
78
|
+
if (this._pkgConf.builder?.cordova && !this._cordova) {
|
|
79
|
+
this._debug("CORDOVA 준비...");
|
|
80
|
+
this._cordova = new SdCliCordova({
|
|
81
|
+
pkgPath: this._pkgPath,
|
|
82
|
+
config: this._pkgConf.builder.cordova,
|
|
83
|
+
cordovaPath: path.resolve(this._pkgPath, ".cache/cordova")
|
|
84
|
+
});
|
|
85
|
+
await this._cordova.initializeAsync();
|
|
86
|
+
}
|
|
87
|
+
|
|
76
88
|
this._debug(`BUILD 준비...`);
|
|
77
|
-
const builderTypes = (Object.keys(this._pkgConf.builder ?? {web: {}}) as ("web")[]);
|
|
78
89
|
this._builders = this._builders ?? builderTypes.map((builderType) => new SdNgBundler({
|
|
79
90
|
dev: opt.dev,
|
|
80
91
|
builderType: builderType,
|
|
81
|
-
pkgPath: this._pkgPath
|
|
92
|
+
pkgPath: this._pkgPath,
|
|
93
|
+
cordovaPlatforms: builderType === "cordova" ? Object.keys(this._pkgConf.builder!.cordova!.platform ?? {browser: {}}) : undefined,
|
|
94
|
+
outputPath: builderType === "web" ? path.resolve(this._pkgPath, "dist")
|
|
95
|
+
: builderType === "electron" ? path.resolve(this._pkgPath, ".cache/electron/src")
|
|
96
|
+
: builderType === "cordova" && !opt.dev ? path.resolve(this._pkgPath, ".cache/cordova/www")
|
|
97
|
+
: path.resolve(this._pkgPath, "dist", builderType)
|
|
82
98
|
}));
|
|
83
99
|
|
|
84
100
|
this._debug(`BUILD & CHECK...`);
|
|
@@ -102,6 +118,11 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
120
|
|
|
121
|
+
if (!opt.dev && this._cordova) {
|
|
122
|
+
this._debug("CORDOVA BUILD...");
|
|
123
|
+
await this._cordova.buildAsync(path.resolve(this._pkgPath, ".cache/cordova"));
|
|
124
|
+
}
|
|
125
|
+
|
|
105
126
|
const localUpdatePaths = Object.keys(this._projConf.localUpdates ?? {})
|
|
106
127
|
.mapMany((key) => FsUtil.glob(path.resolve(this._pkgPath, "../../node_modules", key)));
|
|
107
128
|
const watchFilePaths = filePaths
|
|
@@ -4,7 +4,7 @@ import {INpmConfig, ISdCliBuilderResult, ISdCliConfig, ISdCliServerPackageConfig
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import {SdTsCompiler} from "../build-tools/SdTsCompiler";
|
|
6
6
|
import {SdLinter} from "../build-tools/SdLinter";
|
|
7
|
-
import {FunctionQueue, ObjectUtil
|
|
7
|
+
import {FunctionQueue, ObjectUtil} from "@simplysm/sd-core-common";
|
|
8
8
|
import {SdTsBundler} from "../build-tools/SdTsBundler";
|
|
9
9
|
|
|
10
10
|
export class SdCliServerBuilder extends EventEmitter {
|
|
@@ -26,7 +26,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
public async watchAsync(): Promise<void> {
|
|
29
|
-
const extModules = await this._getExternalModulesAsync();
|
|
29
|
+
// const extModules = await this._getExternalModulesAsync();
|
|
30
30
|
|
|
31
31
|
this._debug("dist 초기화...");
|
|
32
32
|
await FsUtil.removeAsync(path.resolve(this._pkgPath, "dist"));
|
|
@@ -42,7 +42,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
42
42
|
entryPoints: [
|
|
43
43
|
path.resolve(this._pkgPath, "src/main.ts")
|
|
44
44
|
],
|
|
45
|
-
external: extModules.map((item) => item.name)
|
|
45
|
+
// external: extModules.map((item) => item.name)
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
const checker = new SdTsCompiler({
|
|
@@ -73,7 +73,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
73
73
|
const checkResult = await checker.buildAsync();
|
|
74
74
|
|
|
75
75
|
this._debug(`LINT...`);
|
|
76
|
-
const lintResults = await SdLinter.lintAsync(checkResult.affectedFilePaths, checker.program);
|
|
76
|
+
const lintResults = !this._withLint ? [] : await SdLinter.lintAsync(checkResult.affectedFilePaths, checker.program);
|
|
77
77
|
|
|
78
78
|
this._debug(`빌드 완료`);
|
|
79
79
|
this.emit("complete", {
|
|
@@ -86,7 +86,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
86
86
|
|
|
87
87
|
public async buildAsync(): Promise<ISdCliBuilderResult> {
|
|
88
88
|
const npmConfig = (await FsUtil.readJsonAsync(path.resolve(this._pkgPath, "package.json"))) as INpmConfig;
|
|
89
|
-
const extModules = await this._getExternalModulesAsync();
|
|
89
|
+
// const extModules = await this._getExternalModulesAsync();
|
|
90
90
|
|
|
91
91
|
this._debug("dist 초기화...");
|
|
92
92
|
await FsUtil.removeAsync(path.resolve(this._pkgPath, "dist"));
|
|
@@ -97,13 +97,13 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
97
97
|
|
|
98
98
|
this._debug("GEN package.json...");
|
|
99
99
|
{
|
|
100
|
-
const deps = extModules.filter((item) => item.exists).map((item) => item.name);
|
|
100
|
+
// const deps = extModules.filter((item) => item.exists).map((item) => item.name);
|
|
101
101
|
|
|
102
102
|
const distNpmConfig = ObjectUtil.clone(npmConfig);
|
|
103
|
-
distNpmConfig.dependencies = {};
|
|
104
|
-
for (const dep of deps) {
|
|
105
|
-
|
|
106
|
-
}
|
|
103
|
+
// distNpmConfig.dependencies = {};
|
|
104
|
+
// for (const dep of deps) {
|
|
105
|
+
// distNpmConfig.dependencies[dep] = "*";
|
|
106
|
+
// }
|
|
107
107
|
delete distNpmConfig.optionalDependencies;
|
|
108
108
|
delete distNpmConfig.devDependencies;
|
|
109
109
|
delete distNpmConfig.peerDependencies;
|
|
@@ -184,7 +184,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
184
184
|
entryPoints: [
|
|
185
185
|
path.resolve(this._pkgPath, "src/main.ts")
|
|
186
186
|
],
|
|
187
|
-
external: extModules.map((item) => item.name)
|
|
187
|
+
// external: extModules.map((item) => item.name)
|
|
188
188
|
});
|
|
189
189
|
|
|
190
190
|
const checker = new SdTsCompiler({
|
|
@@ -201,7 +201,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
201
201
|
const checkResult = await checker.buildAsync();
|
|
202
202
|
|
|
203
203
|
this._debug(`LINT...`);
|
|
204
|
-
const lintResults = await SdLinter.lintAsync(checkResult.affectedFilePaths, checker.program);
|
|
204
|
+
const lintResults = !this._withLint ? [] : await SdLinter.lintAsync(checkResult.affectedFilePaths, checker.program);
|
|
205
205
|
|
|
206
206
|
this._debug(`빌드 완료`);
|
|
207
207
|
return {
|
|
@@ -210,7 +210,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
210
210
|
};
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
private async _getExternalModulesAsync(): Promise<{
|
|
213
|
+
/*private async _getExternalModulesAsync(): Promise<{
|
|
214
214
|
name: string;
|
|
215
215
|
exists: boolean
|
|
216
216
|
}[]> {
|
|
@@ -245,7 +245,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
245
245
|
continue;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
if (FsUtil.glob(path.resolve(modulePath, "
|
|
248
|
+
if (FsUtil.glob(path.resolve(modulePath, "**!/binding.gyp")).length > 0) {
|
|
249
249
|
results.push({
|
|
250
250
|
name: moduleName,
|
|
251
251
|
exists: true
|
|
@@ -275,7 +275,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
275
275
|
continue;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
if (FsUtil.glob(path.resolve(optModulePath, "
|
|
278
|
+
if (FsUtil.glob(path.resolve(optModulePath, "**!/binding.gyp")).length > 0) {
|
|
279
279
|
results.push({
|
|
280
280
|
name: optModuleName,
|
|
281
281
|
exists: true
|
|
@@ -296,7 +296,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
296
296
|
await fn(this._pkgPath);
|
|
297
297
|
|
|
298
298
|
return results;
|
|
299
|
-
}
|
|
299
|
+
}*/
|
|
300
300
|
|
|
301
301
|
private _debug(msg: string): void {
|
|
302
302
|
this._logger.debug(`[${path.basename(this._pkgPath)}] ${msg}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {FsUtil, Logger, SdFsWatcher} from "@simplysm/sd-core-node";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import {ISdCliBuilderResult
|
|
3
|
+
import {ISdCliBuilderResult} from "../commons";
|
|
4
4
|
import {EventEmitter} from "events";
|
|
5
5
|
import {SdTsCompiler} from "../build-tools/SdTsCompiler";
|
|
6
6
|
import {SdLinter} from "../build-tools/SdLinter";
|
|
@@ -9,8 +9,7 @@ import {FunctionQueue} from "@simplysm/sd-core-common";
|
|
|
9
9
|
export class SdCliTsLibBuilder extends EventEmitter {
|
|
10
10
|
private readonly _logger = Logger.get(["simplysm", "sd-cli", "SdCliTsLibBuilder"]);
|
|
11
11
|
|
|
12
|
-
public constructor(private readonly
|
|
13
|
-
private readonly _pkgPath: string,
|
|
12
|
+
public constructor(private readonly _pkgPath: string,
|
|
14
13
|
private readonly _withLint: boolean) {
|
|
15
14
|
super();
|
|
16
15
|
}
|