@simplysm/sd-cli 7.0.59 → 7.0.66

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.
Files changed (79) hide show
  1. package/dist/SdCliBuildResultError.d.ts +5 -0
  2. package/dist/SdCliBuildResultError.mjs +8 -0
  3. package/dist/bin/sd-cli.d.ts +2 -0
  4. package/dist/bin/sd-cli.mjs +3 -3
  5. package/dist/build-tool/SdCliCacheCompilerHost.d.ts +10 -0
  6. package/dist/build-tool/SdCliCordova.d.ts +16 -0
  7. package/dist/build-tool/SdCliCordova.mjs +171 -0
  8. package/dist/build-tool/SdCliIndexFileGenerator.d.ts +13 -0
  9. package/dist/{entry-points → build-tool}/SdCliIndexFileGenerator.mjs +4 -3
  10. package/dist/build-tool/SdCliNgCacheCompilerHost.d.ts +11 -0
  11. package/dist/build-tool/SdCliPackageLinter.d.ts +8 -0
  12. package/dist/build-tool/SdCliPackageLinter.mjs +19 -2
  13. package/dist/builder/SdCliClientBuilder.d.ts +23 -0
  14. package/dist/builder/SdCliClientBuilder.mjs +68 -6
  15. package/dist/builder/SdCliJsLibBuilder.d.ts +14 -0
  16. package/dist/builder/SdCliServerBuilder.d.ts +25 -0
  17. package/dist/builder/SdCliServerBuilder.mjs +11 -13
  18. package/dist/builder/SdCliTsLibBuilder.d.ts +34 -0
  19. package/dist/builder/SdCliTsLibBuilder.mjs +90 -20
  20. package/dist/commons.d.ts +85 -0
  21. package/dist/entry-points/SdCliLocalUpdate.d.ts +13 -0
  22. package/dist/entry-points/SdCliNpm.d.ts +6 -0
  23. package/dist/entry-points/SdCliPrepare.d.ts +5 -0
  24. package/dist/entry-points/SdCliWorkspace.d.ts +28 -0
  25. package/dist/entry-points/SdCliWorkspace.mjs +12 -16
  26. package/dist/index.d.ts +29 -0
  27. package/dist/index.mjs +30 -0
  28. package/dist/ng-tools/SdCliNgModuleGenerator.d.ts +26 -0
  29. package/dist/ng-tools/SdCliNgModuleGenerator.mjs +404 -0
  30. package/dist/ng-tools/babel/SdCliBbFileMetadata.d.ts +23 -0
  31. package/dist/ng-tools/babel/SdCliBbFileMetadata.mjs +482 -0
  32. package/dist/ng-tools/babel/SdCliBbRootMetadata.d.ts +22 -0
  33. package/dist/ng-tools/babel/SdCliBbRootMetadata.mjs +174 -0
  34. package/dist/ng-tools/babel/SdCliBbUtil.d.ts +5 -0
  35. package/dist/ng-tools/babel/SdCliBbUtil.mjs +14 -0
  36. package/dist/ng-tools/babel/TSdCliBbNgMetadata.d.ts +42 -0
  37. package/dist/ng-tools/babel/TSdCliBbNgMetadata.mjs +200 -0
  38. package/dist/ng-tools/babel/TSdCliBbTypeMetadata.d.ts +44 -0
  39. package/dist/ng-tools/babel/TSdCliBbTypeMetadata.mjs +127 -0
  40. package/dist/ng-tools/commons.d.ts +9 -0
  41. package/dist/ng-tools/commons.mjs +2 -0
  42. package/dist/ng-tools/typescript/SdCliTsFileMetadata.d.ts +61 -0
  43. package/dist/ng-tools/typescript/SdCliTsFileMetadata.mjs +326 -0
  44. package/dist/ng-tools/typescript/SdCliTsRootMetadata.d.ts +8 -0
  45. package/dist/ng-tools/typescript/SdCliTsRootMetadata.mjs +28 -0
  46. package/dist/ng-tools/typescript/SdCliTsUtil.d.ts +5 -0
  47. package/dist/ng-tools/typescript/SdCliTsUtil.mjs +28 -0
  48. package/dist/packages/SdCliPackage.d.ts +23 -0
  49. package/dist/packages/SdCliPackage.mjs +2 -3
  50. package/dist/utils/SdCliBuildResultUtil.d.ts +9 -0
  51. package/dist/utils/SdCliConfigUtil.d.ts +7 -0
  52. package/dist/utils/SdCliNpmConfigUtil.d.ts +7 -0
  53. package/dist/utils/SdCliNpmConfigUtil.mjs +3 -3
  54. package/package.json +16 -7
  55. package/src/SdCliBuildResultError.ts +9 -0
  56. package/src/bin/sd-cli.ts +2 -2
  57. package/src/build-tool/SdCliCordova.ts +219 -0
  58. package/src/{entry-points → build-tool}/SdCliIndexFileGenerator.ts +3 -2
  59. package/src/build-tool/SdCliPackageLinter.ts +18 -2
  60. package/src/builder/SdCliClientBuilder.ts +83 -5
  61. package/src/builder/SdCliServerBuilder.ts +10 -12
  62. package/src/builder/SdCliTsLibBuilder.ts +105 -21
  63. package/src/commons.ts +24 -0
  64. package/src/entry-points/SdCliWorkspace.ts +14 -17
  65. package/src/index.ts +29 -0
  66. package/src/ng-tools/SdCliNgModuleGenerator.ts +538 -0
  67. package/src/ng-tools/babel/SdCliBbFileMetadata.ts +568 -0
  68. package/src/ng-tools/babel/SdCliBbRootMetadata.ts +211 -0
  69. package/src/ng-tools/babel/SdCliBbUtil.ts +15 -0
  70. package/src/ng-tools/babel/TSdCliBbNgMetadata.ts +234 -0
  71. package/src/ng-tools/babel/TSdCliBbTypeMetadata.ts +177 -0
  72. package/src/ng-tools/commons.ts +3 -0
  73. package/src/ng-tools/typescript/SdCliTsFileMetadata.ts +385 -0
  74. package/src/ng-tools/typescript/SdCliTsRootMetadata.ts +32 -0
  75. package/src/ng-tools/typescript/SdCliTsUtil.ts +29 -0
  76. package/src/packages/SdCliPackage.ts +1 -2
  77. package/src/utils/SdCliNpmConfigUtil.ts +2 -2
  78. package/tsconfig-build.json +6 -2
  79. package/tsconfig.json +5 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-cli",
3
- "version": "7.0.59",
3
+ "version": "7.0.66",
4
4
  "description": "심플리즘 패키지 - CLI",
5
5
  "author": "김석래",
6
6
  "repository": {
@@ -12,6 +12,8 @@
12
12
  "bin": {
13
13
  "sd-cli": "./dist/bin/sd-cli.mjs"
14
14
  },
15
+ "main": "./dist/index.mjs",
16
+ "types": "./dist/index.d.ts",
15
17
  "type": "module",
16
18
  "engines": {
17
19
  "node": "^16"
@@ -44,19 +46,26 @@
44
46
  "sass-loader": "^12.4.0",
45
47
  "connect": "^3.7.0",
46
48
  "node-polyfill-webpack-plugin": "^1.1.4",
47
- "chokidar": "^3.5.3"
49
+ "chokidar": "^3.5.3",
50
+ "@babel/parser": "^7.17.0",
51
+ "@babel/types": "^7.17.0",
52
+ "babel-loader": "^8.2.3",
53
+ "jsdom": "^19.0.0",
54
+ "cordova": "^11.0.0",
55
+ "jszip": "^3.7.1"
48
56
  },
49
57
  "devDependencies": {
50
58
  "@types/semver": "^7.3.9",
51
59
  "@types/yargs": "^17.0.8",
52
60
  "@types/webpack-hot-middleware": "^2.25.5",
53
61
  "@types/connect": "^3.4.35",
54
- "@types/source-map-support": "^0.5.4"
62
+ "@types/source-map-support": "^0.5.4",
63
+ "@types/jsdom": "^16.2.14"
55
64
  },
56
65
  "peerDependencies": {
57
- "@simplysm/sd-core-common": "7.0.59",
58
- "@simplysm/sd-core-node": "7.0.59",
59
- "@simplysm/sd-service-common": "7.0.59",
60
- "@simplysm/sd-service-server": "7.0.59"
66
+ "@simplysm/sd-core-common": "7.0.66",
67
+ "@simplysm/sd-core-node": "7.0.66",
68
+ "@simplysm/sd-service-common": "7.0.66",
69
+ "@simplysm/sd-service-server": "7.0.66"
61
70
  }
62
71
  }
@@ -0,0 +1,9 @@
1
+ import { SdError } from "@simplysm/sd-core-common";
2
+ import { ISdCliPackageBuildResult } from "./commons";
3
+ import { SdCliBuildResultUtil } from "./utils/SdCliBuildResultUtil";
4
+
5
+ export class SdCliBuildResultError extends SdError {
6
+ public constructor(buildResult: ISdCliPackageBuildResult) {
7
+ super(SdCliBuildResultUtil.getMessage(buildResult));
8
+ }
9
+ }
package/src/bin/sd-cli.ts CHANGED
@@ -5,11 +5,11 @@ import { hideBin } from "yargs/helpers";
5
5
  import { Logger, LoggerSeverity } from "@simplysm/sd-core-node";
6
6
  import { SdCliWorkspace } from "../entry-points/SdCliWorkspace";
7
7
  import { SdCliLocalUpdate } from "../entry-points/SdCliLocalUpdate";
8
- import sourceMapSupport from "source-map-support";
8
+ // import sourceMapSupport from "source-map-support";
9
9
  import { SdCliNpm } from "../entry-points/SdCliNpm";
10
10
  import { SdCliPrepare } from "../entry-points/SdCliPrepare";
11
11
 
12
- sourceMapSupport.install();
12
+ // sourceMapSupport.install();
13
13
 
14
14
  Error.stackTraceLimit = Infinity;
15
15
 
@@ -0,0 +1,219 @@
1
+ import { INpmConfig, ISdCliClientPackageCordovaConfig, TSdCliCordovaPlatform } from "../commons";
2
+ import * as path from "path";
3
+ import { FsUtil, Logger, SdProcess } from "@simplysm/sd-core-node";
4
+ import { NotImplementError } from "@simplysm/sd-core-common";
5
+ import JSZip from "jszip";
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(): TSdCliCordovaPlatform[] {
16
+ return this._config.platforms;
17
+ }
18
+
19
+ public constructor(private readonly _rootPath: string, private readonly _config: ISdCliClientPackageCordovaConfig) {
20
+ this._npmConfig = FsUtil.readJson(path.resolve(this._rootPath, "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.cordovaPath)) {
32
+ this._logger.log("미리 생성되어있는 '.cordova'를 사용합니다.");
33
+ }
34
+ else {
35
+ await this._execAsync(`${this._binPath} telemetry on`, this._rootPath);
36
+
37
+ // 프로젝트 생성
38
+ await this._execAsync(`${this._binPath} create "${this.cordovaPath}" "${this._config.appId}" "${this._config.appName}"`, process.cwd());
39
+ }
40
+
41
+ // platforms 폴더 혹시 없으면 생성
42
+ await FsUtil.mkdirsAsync(path.resolve(this.cordovaPath, "platforms"));
43
+
44
+ // www 폴더 혹시 없으면 생성
45
+ await FsUtil.mkdirsAsync(path.resolve(this.cordovaPath, "www"));
46
+
47
+ // 미설치 빌드 플랫폼 신규 생성
48
+ const alreadyPlatforms = await FsUtil.readdirAsync(path.resolve(this.cordovaPath, "platforms"));
49
+ for (const platform of this.platforms) {
50
+ if (!alreadyPlatforms.includes(platform)) {
51
+ await this._execAsync(`${this._binPath} platform add ${platform}`, this.cordovaPath);
52
+ }
53
+ }
54
+
55
+ // 미설치 플러그인들 설치
56
+ if (this._config.plugins) {
57
+ const pluginsFetch = await FsUtil.readJsonAsync(path.resolve(this.cordovaPath, "plugins/fetch.json"));
58
+ const alreadyPlugins = Object.values(pluginsFetch)
59
+ .map((item: any) => (item.source.id !== undefined ? item.source.id.replace(/@.*$/, "") : item.source.url));
60
+
61
+ for (const plugin of this._config.plugins.distinct()) {
62
+ if (!alreadyPlugins.includes(plugin)) {
63
+ await this._execAsync(`${this._binPath} plugin add ${plugin}`, this.cordovaPath);
64
+ }
65
+ }
66
+ }
67
+
68
+ // ICON 파일 복사
69
+ if (this._config.icon !== undefined) {
70
+ await FsUtil.copyAsync(path.resolve(this._rootPath, this._config.icon), path.resolve(this.cordovaPath, "res", "icon", "icon.png"));
71
+ }
72
+
73
+ // ANDROID GRADLE 오류 강제 수정
74
+ // if (this._config.targets.includes("android")) {
75
+ // const gradleFilePath = path.resolve(this._cordovaPath, "platforms/android/app/build.gradle");
76
+ // let gradleFileContent = await FsUtil.readFileAsync(gradleFilePath);
77
+ // gradleFileContent = gradleFileContent.replace(/lintOptions {/g, "lintOptions {\r\n checkReleaseBuilds false;");
78
+ // await FsUtil.writeFileAsync(gradleFilePath, gradleFileContent);
79
+ // }
80
+
81
+ // CONFIG
82
+ const configFilePath = path.resolve(this.cordovaPath, "config.xml");
83
+ let configFileContent = await FsUtil.readFileAsync(configFilePath);
84
+
85
+ // CONFIG: 버전 설정
86
+ configFileContent = configFileContent.replace(/version="[^"]*"/g, `version="${this._npmConfig.version}"`);
87
+
88
+ // CONFIG: ICON 설정
89
+ if (this._config.icon !== undefined && !configFileContent.includes("<icon")) {
90
+ configFileContent = configFileContent.replace("</widget>", " <icon src=\"res/icon/icon.png\" />\r\n</widget>");
91
+ }
92
+
93
+ // CONFIG: ANDROID usesCleartextTraffic 설정
94
+ // if (this._config.targets.includes("android")) {
95
+ // if (!configFileContent.includes("xmlns:android=\"http://schemas.android.com/apk/res/android\"")) {
96
+ // configFileContent = configFileContent.replace(
97
+ // "xmlns=\"http://www.w3.org/ns/widgets\"",
98
+ // `xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android"`
99
+ // );
100
+ // }
101
+ // if (!configFileContent.includes("application android:usesCleartextTraffic=\"true\" />")) {
102
+ // configFileContent = configFileContent.replace("<platform name=\"android\">", `<platform name="android">
103
+ // <edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
104
+ // <application android:usesCleartextTraffic="true" />
105
+ // </edit-config>`);
106
+ // }
107
+ // }
108
+
109
+ // CONFIG: 파일쓰기
110
+ await FsUtil.writeFileAsync(configFilePath, configFileContent);
111
+ }
112
+
113
+ public async buildAsync(outPath: string): Promise<void> {
114
+ // ANDROID 서명 처리
115
+ if (this._config.buildOption?.sign?.android) {
116
+ await FsUtil.copyAsync(
117
+ path.resolve(this._rootPath, this._config.buildOption.sign.android.keystore),
118
+ path.resolve(this.cordovaPath, path.basename(this._config.buildOption.sign.android.keystore))
119
+ );
120
+ await FsUtil.writeJsonAsync(
121
+ path.resolve(this.cordovaPath, "build.json"),
122
+ {
123
+ android: {
124
+ release: {
125
+ keystore: path.basename(this._config.buildOption.sign.android.keystore),
126
+ storePassword: this._config.buildOption.sign.android.storePassword,
127
+ alias: this._config.buildOption.sign.android.alias,
128
+ password: this._config.buildOption.sign.android.password,
129
+ keystoreType: this._config.buildOption.sign.android.keystoreType
130
+ }
131
+ }
132
+ }
133
+ );
134
+ }
135
+
136
+ // CONFIG
137
+ const configFilePath = path.resolve(this.cordovaPath, "config.xml");
138
+ let configFileContent = await FsUtil.readFileAsync(configFilePath);
139
+
140
+ // CONFIG: 접근허용 일단 모두 지우기
141
+ configFileContent = configFileContent.replace(/ {4}<allow-navigation href="[^"]*" \/>\n/g, "");
142
+
143
+ // CONFIG: 접근허용 등록
144
+ // configFileContent = configFileContent.replace("</widget>", ` <allow-navigation href="file://data/user/*" />\n</widget>`);
145
+ configFileContent = configFileContent.replace("</widget>", ` <allow-navigation href="*://*/*" />\n</widget>`);
146
+
147
+ // CONFIG: 파일쓰기
148
+ await FsUtil.writeFileAsync(configFilePath, configFileContent);
149
+
150
+ // 실행
151
+ const buildType = this._config.buildOption?.debug ? "debug" : "release";
152
+ const packageType = this._config.buildOption?.bundle ? "bundle" : "apk";
153
+ for (const platform of this.platforms) {
154
+ await this._execAsync(`${this._binPath} build ${platform} --${buildType} --packageType=${packageType}`, this.cordovaPath);
155
+ }
156
+
157
+ // 결과물 복사
158
+ for (const platform of this.platforms) {
159
+ if (platform === "android") {
160
+ const targetOutPath = path.resolve(outPath, platform);
161
+ const apkFileName = this._config.buildOption?.sign?.android ? `app-${buildType}.apk` : `app-${buildType}-unsigned.apk`;
162
+ const distApkFileName = path.basename(`${this._config.appName}${this._config.buildOption?.sign?.android ? "" : "-unsigned"}-v${this._npmConfig.version}.apk`);
163
+ const latestDistApkFileName = path.basename(`${this._config.appName}${this._config.buildOption?.sign?.android ? "" : "-unsigned"}-latest.apk`);
164
+ await FsUtil.mkdirsAsync(targetOutPath);
165
+ await FsUtil.copyAsync(
166
+ path.resolve(this.cordovaPath, "platforms/android/app/build/outputs/apk", buildType, apkFileName),
167
+ path.resolve(targetOutPath, distApkFileName)
168
+ );
169
+ await FsUtil.copyAsync(
170
+ path.resolve(this.cordovaPath, "platforms/android/app/build/outputs/apk", buildType, apkFileName),
171
+ path.resolve(targetOutPath, latestDistApkFileName)
172
+ );
173
+ }
174
+ else {
175
+ throw new NotImplementError();
176
+ }
177
+ }
178
+
179
+ // 자동업데이트를 위한 zip 파일 쓰기
180
+ const zip = new JSZip();
181
+ const resultFiles = await FsUtil.globAsync(path.resolve(this.cordovaPath, "www", "**/*"), {
182
+ dot: true,
183
+ nodir: true
184
+ });
185
+ for (const resultFile of resultFiles) {
186
+ const contentBuffer = await FsUtil.readFileBufferAsync(resultFile);
187
+ const relativePath = path.relative(path.resolve(this.cordovaPath, "www"), resultFile);
188
+ zip.file("/" + relativePath, contentBuffer);
189
+ }
190
+
191
+ const zipFileName = path.basename(`${this._config.appName}-v${this._npmConfig.version}.zip`);
192
+ const resultBuffer = await zip.generateAsync({ type: "nodebuffer" });
193
+
194
+ await FsUtil.writeFileAsync(path.resolve(outPath, "updates/", zipFileName), resultBuffer);
195
+ }
196
+
197
+ public async runWebviewOnDeviceAsync(platform: TSdCliCordovaPlatform, url: string): Promise<void> {
198
+ await FsUtil.removeAsync(path.resolve(this.cordovaPath, "www"));
199
+ await FsUtil.mkdirsAsync(path.resolve(this.cordovaPath, "www"));
200
+ await FsUtil.writeFileAsync(path.resolve(this.cordovaPath, "www/index.html"), `'${url}'로 이동중... <script>setTimeout(function () {window.location.href = "${url}"}, 3000);</script>`.trim());
201
+
202
+ // CONFIG
203
+ const configFilePath = path.resolve(this.cordovaPath, "config.xml");
204
+ let configFileContent = await FsUtil.readFileAsync(configFilePath);
205
+
206
+ // CONFIG: 접근허용 일단 모두 지우기
207
+ configFileContent = configFileContent.replace(/ {4}<allow-navigation href="[^"]*" \/>\n/g, "");
208
+
209
+ // CONFIG: 접근허용 등록
210
+ // configFileContent = configFileContent.replace("</widget>", ` <allow-navigation href="${url}" />\n</widget>`);
211
+ // configFileContent = configFileContent.replace("</widget>", ` <allow-navigation href="${url}/*" />\n</widget>`);
212
+ configFileContent = configFileContent.replace("</widget>", ` <allow-navigation href="*://*/*" />\n</widget>`);
213
+
214
+ // CONFIG: 파일쓰기
215
+ await FsUtil.writeFileAsync(configFilePath, configFileContent);
216
+
217
+ await SdProcess.spawnAsync(`${this._binPath} run ${platform} --device`, { cwd: this.cordovaPath });
218
+ }
219
+ }
@@ -10,6 +10,7 @@ export class SdCliIndexFileGenerator {
10
10
 
11
11
  public constructor(public readonly rootPath: string,
12
12
  public readonly config: ISdAutoIndexConfig) {
13
+ this._contentCache = FsUtil.exists(this._indexFilePath) ? FsUtil.readFile(this._indexFilePath) : undefined;
13
14
  }
14
15
 
15
16
  public async watchAsync(): Promise<void> {
@@ -55,9 +56,9 @@ export class SdCliIndexFileGenerator {
55
56
  }
56
57
 
57
58
  const content = importTexts.join(os.EOL) + os.EOL;
58
- const prevContent = this._contentCache ?? (FsUtil.exists(this._indexFilePath) ? FsUtil.readFile(this._indexFilePath) : undefined);
59
+ const prevContent = this._contentCache;
59
60
  this._contentCache = content;
60
- if (content !== prevContent) {
61
+ if (content.trim() !== prevContent?.trim()) {
61
62
  await FsUtil.writeFileAsync(this._indexFilePath, content);
62
63
  }
63
64
  }
@@ -16,7 +16,24 @@ export class SdCliPackageLinter {
16
16
  const linter = new ESLint(
17
17
  program && filePaths.some((item) => item.endsWith(".ts")) ? {
18
18
  overrideConfig: {
19
- parserOptions: { program }
19
+ overrides: [
20
+ {
21
+ files: ["*.ts"],
22
+ parserOptions: {
23
+ programs: [program],
24
+ tsconfigRootDir: null,
25
+ project: null
26
+ },
27
+ settings: {
28
+ "import/resolver": {
29
+ "typescript": {
30
+ programs: [program],
31
+ project: null
32
+ }
33
+ }
34
+ }
35
+ }
36
+ ]
20
37
  }
21
38
  } : {}
22
39
  );
@@ -32,7 +49,6 @@ export class SdCliPackageLinter {
32
49
 
33
50
  const lintResults = await linter.lintFiles(lintFilePaths);
34
51
 
35
-
36
52
  const result = lintResults.map((lintResult) => ({
37
53
  filePath: lintResult.filePath,
38
54
  results: lintResult.messages.map((msg) => ({
@@ -34,24 +34,55 @@ import { CssOptimizerPlugin } from "@angular-devkit/build-angular/src/webpack/pl
34
34
  import browserslist from "browserslist";
35
35
  import { augmentAppWithServiceWorker } from "@angular-devkit/build-angular/src/utils/service-worker";
36
36
  import { StringUtil } from "@simplysm/sd-core-common";
37
+ import { SdCliNgModuleGenerator } from "../ng-tools/SdCliNgModuleGenerator";
38
+ import { SdCliCordova } from "../build-tool/SdCliCordova";
37
39
  import LintResult = ESLint.LintResult;
38
40
 
39
41
  export class SdCliClientBuilder extends EventEmitter {
40
- private readonly _logger = Logger.get(["simplysm", "sd-cli", this.constructor.name]);
42
+ private readonly _logger: Logger;
41
43
 
42
44
  private readonly _tsconfigFilePath: string;
43
45
  private readonly _parsedTsconfig: ts.ParsedCommandLine;
44
46
  private readonly _npmConfigMap = new Map<string, INpmConfig>();
47
+ private readonly _ngModuleGenerator: SdCliNgModuleGenerator;
48
+
49
+ private readonly _cordova?: SdCliCordova;
45
50
 
46
51
  public constructor(private readonly _rootPath: string,
47
52
  private readonly _config: ISdCliClientPackageConfig,
48
53
  private readonly _workspaceRootPath: string) {
49
54
  super();
50
55
 
56
+ const npmConfig = this._getNpmConfig(this._rootPath)!;
57
+ this._logger = Logger.get(["simplysm", "sd-cli", this.constructor.name, npmConfig.name]);
58
+
51
59
  // tsconfig
52
60
  this._tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
53
61
  const tsconfig = FsUtil.readJson(this._tsconfigFilePath) as ITsconfig;
54
62
  this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath, tsconfig.angularCompilerOptions);
63
+
64
+ // NgModule 생성기 초기화
65
+ this._ngModuleGenerator = new SdCliNgModuleGenerator(this._rootPath, [
66
+ "controls",
67
+ "directives",
68
+ "guards",
69
+ "modals",
70
+ "providers",
71
+ "app",
72
+ "pages",
73
+ "print-templates",
74
+ "toasts",
75
+ "AppPage"
76
+ ], {
77
+ glob: "**/*Page.ts",
78
+ fileEndsWith: "Page",
79
+ rootClassName: "AppPage"
80
+ });
81
+
82
+ // CORDOVA
83
+ if (this._config.cordova) {
84
+ this._cordova = new SdCliCordova(this._rootPath, this._config.cordova);
85
+ }
55
86
  }
56
87
 
57
88
  public override on(event: "change", listener: () => void): this;
@@ -64,18 +95,47 @@ export class SdCliClientBuilder extends EventEmitter {
64
95
  // DIST 비우기
65
96
  await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
66
97
 
98
+ // NgModule 생성
99
+ await this._ngModuleGenerator.runAsync();
100
+
101
+ // CORDOVA
102
+ if (this._cordova) {
103
+ this._logger.debug("CORDOVA 구성...");
104
+ await this._cordova.initializeAsync();
105
+ }
106
+
67
107
  // 빌드 준비
68
108
  const webpackConfig = this._getWebpackConfig(true);
69
109
  const compiler = webpack(webpackConfig);
70
110
  return await new Promise<NextHandleFunction[]>((resolve, reject) => {
71
- compiler.hooks.watchRun.tapAsync(this.constructor.name, (args, callback) => {
111
+ compiler.hooks.invalid.tap(this.constructor.name, (fileName) => {
112
+ if (fileName != null) {
113
+ this._logger.debug("파일변경 감지", fileName);
114
+ // NgModule 캐시 삭제
115
+ this._ngModuleGenerator.removeCaches([path.resolve(fileName)]);
116
+ }
117
+ });
118
+
119
+ compiler.hooks.watchRun.tapAsync(this.constructor.name, async (args, callback) => {
72
120
  this.emit("change");
121
+
122
+ // NgModule 생성
123
+ await this._ngModuleGenerator.runAsync();
124
+
73
125
  callback();
74
126
 
75
127
  this._logger.debug("Webpack 빌드 수행...");
76
128
  });
77
129
 
78
130
  compiler.hooks.failed.tap(this.constructor.name, (err) => {
131
+ this.emit("complete", [{
132
+ filePath: undefined,
133
+ line: undefined,
134
+ char: undefined,
135
+ code: undefined,
136
+ severity: "error",
137
+ message: err.stack
138
+ }]);
79
139
  reject(err);
80
140
  return;
81
141
  });
@@ -83,7 +143,6 @@ export class SdCliClientBuilder extends EventEmitter {
83
143
  compiler.hooks.done.tap(this.constructor.name, async (stats) => {
84
144
  // 결과 반환
85
145
  const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
86
- this.emit("complete", results);
87
146
 
88
147
  // .config.json 파일 쓰기
89
148
  const npmConfig = this._getNpmConfig(this._rootPath)!;
@@ -97,6 +156,8 @@ export class SdCliClientBuilder extends EventEmitter {
97
156
  // 마무리
98
157
  this._logger.debug("Webpack 빌드 완료");
99
158
  resolve([devMiddleware, hotMiddleware]);
159
+
160
+ this.emit("complete", results);
100
161
  });
101
162
 
102
163
  const devMiddleware = wdm(compiler, {
@@ -116,6 +177,9 @@ export class SdCliClientBuilder extends EventEmitter {
116
177
  // DIST 비우기
117
178
  await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
118
179
 
180
+ // NgModule 생성
181
+ await this._ngModuleGenerator.runAsync();
182
+
119
183
  // 빌드
120
184
  this._logger.debug("Webpack 빌드 수행...");
121
185
  const webpackConfig = this._getWebpackConfig(false);
@@ -148,6 +212,15 @@ export class SdCliClientBuilder extends EventEmitter {
148
212
  );
149
213
  }
150
214
 
215
+ // CORDOVA
216
+ if (this._cordova) {
217
+ this._logger.debug("CORDOVA 구성...");
218
+ await this._cordova.initializeAsync();
219
+
220
+ this._logger.debug("CORDOVA 빌드...");
221
+ await this._cordova.buildAsync(this._parsedTsconfig.options.outDir!);
222
+ }
223
+
151
224
  // 마무리
152
225
  this._logger.debug("Webpack 빌드 완료");
153
226
  return buildResults;
@@ -176,7 +249,7 @@ export class SdCliClientBuilder extends EventEmitter {
176
249
  const cacheBasePath = path.resolve(this._rootPath, ".cache");
177
250
  const cachePath = path.resolve(cacheBasePath, pkgVersion);
178
251
 
179
- const distPath = this._parsedTsconfig.options.outDir;
252
+ const distPath = (this._cordova && !watch) ? path.resolve(this._cordova.cordovaPath, "www") : this._parsedTsconfig.options.outDir;
180
253
 
181
254
  const sassImplementation = new SassWorkerImplementation();
182
255
 
@@ -462,7 +535,12 @@ export class SdCliClientBuilder extends EventEmitter {
462
535
  ].map((i) => PathUtil.posix(this._rootPath, i))
463
536
  },
464
537
  priority: 0
465
- }))
538
+ })),
539
+ ...this._cordova ? this._cordova.platforms.map((platform) => ({
540
+ context: this._cordova!.cordovaPath,
541
+ to: `cordova-${platform}`,
542
+ from: `platforms/${platform}/platform_www`
543
+ })) : []
466
544
  ]
467
545
  }),
468
546
  ...watch ? [
@@ -227,15 +227,15 @@ export class SdCliServerBuilder extends EventEmitter {
227
227
  return {
228
228
  mode: watch ? "development" : "production",
229
229
  devtool: false,
230
- target: ["node", "es2020"],
230
+ target: ["node", "es2015"],
231
231
  profile: false,
232
232
  resolve: {
233
233
  roots: [this._rootPath],
234
234
  extensions: [".ts", ".js", ".mjs", ".cjs"],
235
235
  symlinks: true,
236
236
  modules: [this._workspaceRootPath, "node_modules"],
237
- mainFields: ["es2020", "default", "module", "main"],
238
- conditionNames: ["es2020", "..."]
237
+ mainFields: ["es2015", "default", "module", "main"],
238
+ conditionNames: ["es2015", "..."]
239
239
  },
240
240
  resolveLoader: {
241
241
  symlinks: true
@@ -251,19 +251,14 @@ export class SdCliServerBuilder extends EventEmitter {
251
251
  hashFunction: "xxhash64",
252
252
  clean: true,
253
253
  path: this._parsedTsconfig.options.outDir,
254
- filename: "[name].mjs",
255
- chunkFilename: "[name].mjs",
254
+ filename: "[name].cjs",
255
+ chunkFilename: "[name].cjs",
256
256
  assetModuleFilename: "res/[name][ext][query]",
257
- library: {
258
- type: "module"
259
- }
257
+ libraryTarget: "commonjs2"
260
258
  },
261
259
  watch: false,
262
260
  watchOptions: { poll: undefined, ignored: undefined },
263
261
  performance: { hints: false },
264
- experiments: {
265
- outputModule: true
266
- },
267
262
  infrastructureLogging: { level: "error" },
268
263
  stats: "errors-warnings",
269
264
  externals: extModuleNames.toObject((item) => item, (item) => "commonjs2 " + item),
@@ -294,7 +289,7 @@ export class SdCliServerBuilder extends EventEmitter {
294
289
  extractComments: false,
295
290
  terserOptions: {
296
291
  compress: true,
297
- ecma: 2020,
292
+ ecma: 2015,
298
293
  sourceMap: false,
299
294
  keep_classnames: true,
300
295
  keep_fnames: true,
@@ -323,6 +318,9 @@ export class SdCliServerBuilder extends EventEmitter {
323
318
  test: /\.[cm]?[tj]sx?$/,
324
319
  resolve: {
325
320
  fullySpecified: false
321
+ },
322
+ use: {
323
+ loader: "babel-loader"
326
324
  }
327
325
  },
328
326
  ...watch ? [