@simplysm/sd-cli 7.0.66 → 7.0.77
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/.eslintrc.cjs +3 -0
- package/dist/bin/sd-cli.mjs +62 -6
- package/dist/build-tool/SdCliCordova.d.ts +1 -1
- package/dist/build-tool/SdCliCordova.mjs +40 -27
- package/dist/build-tool/SdCliNgCacheCompilerHost.d.ts +1 -0
- package/dist/build-tool/SdCliNgCacheCompilerHost.mjs +11 -4
- package/dist/build-tool/SdCliPackageLinter.mjs +9 -5
- package/dist/builder/SdCliClientBuilder.d.ts +1 -0
- package/dist/builder/SdCliClientBuilder.mjs +45 -37
- package/dist/builder/SdCliJsLibBuilder.mjs +3 -3
- package/dist/builder/SdCliServerBuilder.d.ts +0 -1
- package/dist/builder/SdCliServerBuilder.mjs +39 -54
- package/dist/builder/SdCliTsLibBuilder.d.ts +1 -0
- package/dist/builder/SdCliTsLibBuilder.mjs +19 -7
- package/dist/commons.d.ts +18 -4
- package/dist/entry-points/SdCliFileCrypto.d.ts +7 -0
- package/dist/entry-points/SdCliFileCrypto.mjs +72 -0
- package/dist/entry-points/SdCliLocalUpdate.mjs +3 -3
- package/dist/entry-points/SdCliPrepare.mjs +3 -3
- package/dist/entry-points/SdCliWorkspace.d.ts +3 -0
- package/dist/entry-points/SdCliWorkspace.mjs +57 -37
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +2 -1
- package/dist/ng-tools/SdCliNgModuleGenerator.d.ts +2 -0
- package/dist/ng-tools/SdCliNgModuleGenerator.mjs +97 -18
- package/dist/ng-tools/babel/SdCliBbFileMetadata.d.ts +1 -1
- package/dist/ng-tools/babel/SdCliBbFileMetadata.mjs +4 -4
- package/dist/ng-tools/babel/TSdCliBbNgMetadata.mjs +6 -6
- package/dist/ng-tools/typescript/SdCliTsFileMetadata.mjs +2 -3
- package/dist/packages/SdCliPackage.mjs +52 -5
- package/dist/utils/SdCliBuildResultUtil.mjs +4 -1
- package/package.json +7 -9
- package/src/bin/sd-cli.ts +77 -6
- package/src/build-tool/SdCliCordova.ts +43 -26
- package/src/build-tool/SdCliNgCacheCompilerHost.ts +19 -11
- package/src/build-tool/SdCliPackageLinter.ts +8 -4
- package/src/builder/SdCliClientBuilder.ts +42 -32
- package/src/builder/SdCliJsLibBuilder.ts +2 -2
- package/src/builder/SdCliServerBuilder.ts +37 -51
- package/src/builder/SdCliTsLibBuilder.ts +22 -6
- package/src/commons.ts +21 -2
- package/src/entry-points/SdCliFileCrypto.ts +87 -0
- package/src/entry-points/SdCliLocalUpdate.ts +2 -2
- package/src/entry-points/SdCliPrepare.ts +2 -2
- package/src/entry-points/SdCliWorkspace.ts +59 -41
- package/src/index.ts +1 -0
- package/src/ng-tools/SdCliNgModuleGenerator.ts +119 -17
- package/src/ng-tools/babel/SdCliBbFileMetadata.ts +5 -5
- package/src/ng-tools/babel/TSdCliBbNgMetadata.ts +5 -5
- package/src/ng-tools/typescript/SdCliTsFileMetadata.ts +1 -2
- package/src/packages/SdCliPackage.ts +55 -4
- package/src/utils/SdCliBuildResultUtil.ts +4 -0
- package/tsconfig.json +3 -0
|
@@ -7,7 +7,6 @@ import ts from "typescript";
|
|
|
7
7
|
import { SdCliBuildResultUtil } from "../utils/SdCliBuildResultUtil";
|
|
8
8
|
import { NamedChunksPlugin } from "@angular-devkit/build-angular/src/webpack/plugins/named-chunks-plugin";
|
|
9
9
|
import {
|
|
10
|
-
AnyComponentStyleBudgetChecker,
|
|
11
10
|
CommonJsUsageWarnPlugin,
|
|
12
11
|
DedupeModuleResolvePlugin,
|
|
13
12
|
JavaScriptOptimizerPlugin,
|
|
@@ -23,7 +22,6 @@ import wdm from "webpack-dev-middleware";
|
|
|
23
22
|
import whm from "webpack-hot-middleware";
|
|
24
23
|
import { NextHandleFunction } from "connect";
|
|
25
24
|
import { LicenseWebpackPlugin } from "license-webpack-plugin";
|
|
26
|
-
import { Type } from "@angular-devkit/build-angular";
|
|
27
25
|
import NodePolyfillPlugin from "node-polyfill-webpack-plugin";
|
|
28
26
|
import { createHash } from "crypto";
|
|
29
27
|
import ESLintWebpackPlugin from "eslint-webpack-plugin";
|
|
@@ -36,6 +34,7 @@ import { augmentAppWithServiceWorker } from "@angular-devkit/build-angular/src/u
|
|
|
36
34
|
import { StringUtil } from "@simplysm/sd-core-common";
|
|
37
35
|
import { SdCliNgModuleGenerator } from "../ng-tools/SdCliNgModuleGenerator";
|
|
38
36
|
import { SdCliCordova } from "../build-tool/SdCliCordova";
|
|
37
|
+
import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
|
|
39
38
|
import LintResult = ESLint.LintResult;
|
|
40
39
|
|
|
41
40
|
export class SdCliClientBuilder extends EventEmitter {
|
|
@@ -48,6 +47,8 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
48
47
|
|
|
49
48
|
private readonly _cordova?: SdCliCordova;
|
|
50
49
|
|
|
50
|
+
private readonly _hasAngularRoute: boolean;
|
|
51
|
+
|
|
51
52
|
public constructor(private readonly _rootPath: string,
|
|
52
53
|
private readonly _config: ISdCliClientPackageConfig,
|
|
53
54
|
private readonly _workspaceRootPath: string) {
|
|
@@ -61,6 +62,9 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
61
62
|
const tsconfig = FsUtil.readJson(this._tsconfigFilePath) as ITsconfig;
|
|
62
63
|
this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._rootPath, tsconfig.angularCompilerOptions);
|
|
63
64
|
|
|
65
|
+
// isAngular
|
|
66
|
+
this._hasAngularRoute = SdCliNpmConfigUtil.getDependencies(npmConfig).defaults.includes("@angular/router");
|
|
67
|
+
|
|
64
68
|
// NgModule 생성기 초기화
|
|
65
69
|
this._ngModuleGenerator = new SdCliNgModuleGenerator(this._rootPath, [
|
|
66
70
|
"controls",
|
|
@@ -73,11 +77,11 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
73
77
|
"print-templates",
|
|
74
78
|
"toasts",
|
|
75
79
|
"AppPage"
|
|
76
|
-
], {
|
|
80
|
+
], this._hasAngularRoute ? {
|
|
77
81
|
glob: "**/*Page.ts",
|
|
78
82
|
fileEndsWith: "Page",
|
|
79
83
|
rootClassName: "AppPage"
|
|
80
|
-
});
|
|
84
|
+
} : undefined);
|
|
81
85
|
|
|
82
86
|
// CORDOVA
|
|
83
87
|
if (this._config.cordova) {
|
|
@@ -244,7 +248,7 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
244
248
|
const ngVersion = this._getNpmConfig(FsUtil.findAllParentChildDirPaths("node_modules/@angular/core", this._rootPath, this._workspaceRootPath)[0])!.version;
|
|
245
249
|
|
|
246
250
|
const pkgKey = npmConfig.name.split("/").last()!;
|
|
247
|
-
const publicPath = `/${pkgKey}/`;
|
|
251
|
+
const publicPath = (this._cordova && !watch) ? `/` : `/${pkgKey}/`;
|
|
248
252
|
|
|
249
253
|
const cacheBasePath = path.resolve(this._rootPath, ".cache");
|
|
250
254
|
const cachePath = path.resolve(cacheBasePath, pkgVersion);
|
|
@@ -257,6 +261,7 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
257
261
|
const polyfillsFilePath = path.resolve(this._rootPath, "src/polyfills.ts");
|
|
258
262
|
const stylesFilePath = path.resolve(this._rootPath, "src/styles.scss");
|
|
259
263
|
|
|
264
|
+
let prevProgressMessage = "";
|
|
260
265
|
return {
|
|
261
266
|
mode: watch ? "development" : "production",
|
|
262
267
|
devtool: false,
|
|
@@ -323,6 +328,7 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
323
328
|
.update(watch.toString())
|
|
324
329
|
.digest("hex")
|
|
325
330
|
},
|
|
331
|
+
// cache: { type: "memory", maxGenerations: 1 },
|
|
326
332
|
...watch ? {
|
|
327
333
|
snapshot: {
|
|
328
334
|
immutablePaths: internalModuleCachePaths,
|
|
@@ -395,6 +401,27 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
395
401
|
test: /[/\\]rxjs[/\\]add[/\\].+\.js$/,
|
|
396
402
|
sideEffects: true
|
|
397
403
|
},
|
|
404
|
+
...watch ? [
|
|
405
|
+
{
|
|
406
|
+
loader: HmrLoader,
|
|
407
|
+
include: [mainFilePath]
|
|
408
|
+
}
|
|
409
|
+
] : [],
|
|
410
|
+
...watch ? [
|
|
411
|
+
{
|
|
412
|
+
test: /\.[cm]?jsx?$/,
|
|
413
|
+
enforce: "pre" as const,
|
|
414
|
+
loader: "source-map-loader",
|
|
415
|
+
options: {
|
|
416
|
+
filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
|
|
417
|
+
const workspaceRegex = new RegExp(`node_modules[\\\\/]@${workspaceName}[\\\\/]`);
|
|
418
|
+
return !resourcePath.includes("node_modules")
|
|
419
|
+
|| (/node_modules[\\/]@simplysm[\\/]/).test(resourcePath)
|
|
420
|
+
|| workspaceRegex.test(resourcePath);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
] : [],
|
|
398
425
|
{
|
|
399
426
|
test: /\.[cm]?[tj]sx?$/,
|
|
400
427
|
resolve: { fullySpecified: false },
|
|
@@ -412,21 +439,6 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
412
439
|
}
|
|
413
440
|
]
|
|
414
441
|
},
|
|
415
|
-
...watch ? [
|
|
416
|
-
{
|
|
417
|
-
test: /\.[cm]?jsx?$/,
|
|
418
|
-
enforce: "pre" as const,
|
|
419
|
-
loader: "source-map-loader",
|
|
420
|
-
options: {
|
|
421
|
-
filterSourceMappingUrl: (mapUri: string, resourcePath: string) => {
|
|
422
|
-
const workspaceRegex = new RegExp(`node_modules[\\\\/]@${workspaceName}[\\\\/]`);
|
|
423
|
-
return !resourcePath.includes("node_modules")
|
|
424
|
-
|| (/node_modules[\\/]@simplysm[\\/]/).test(resourcePath)
|
|
425
|
-
|| workspaceRegex.test(resourcePath);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
] : [],
|
|
430
442
|
{
|
|
431
443
|
test: /\.[cm]?tsx?$/,
|
|
432
444
|
loader: "@ngtools/webpack",
|
|
@@ -485,12 +497,6 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
485
497
|
}
|
|
486
498
|
]
|
|
487
499
|
},
|
|
488
|
-
...watch ? [
|
|
489
|
-
{
|
|
490
|
-
loader: HmrLoader,
|
|
491
|
-
include: [mainFilePath]
|
|
492
|
-
}
|
|
493
|
-
] : [],
|
|
494
500
|
{
|
|
495
501
|
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf|xlsx?|pptx?|docx?|zip|pfx|pkl)$/,
|
|
496
502
|
type: "asset/resource"
|
|
@@ -501,13 +507,17 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
501
507
|
new NodePolyfillPlugin(),
|
|
502
508
|
new NamedChunksPlugin(),
|
|
503
509
|
new DedupeModuleResolvePlugin(),
|
|
504
|
-
|
|
510
|
+
new webpack.ProgressPlugin({
|
|
505
511
|
handler: (per: number, msg: string, ...args: string[]) => {
|
|
506
512
|
const phaseText = msg ? ` - phase: ${msg}` : "";
|
|
507
513
|
const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
|
|
508
|
-
|
|
514
|
+
const progressMessage = `Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`;
|
|
515
|
+
if (progressMessage !== prevProgressMessage) {
|
|
516
|
+
prevProgressMessage = progressMessage;
|
|
517
|
+
this._logger.debug(progressMessage);
|
|
518
|
+
}
|
|
509
519
|
}
|
|
510
|
-
})
|
|
520
|
+
}),
|
|
511
521
|
new CommonJsUsageWarnPlugin(),
|
|
512
522
|
...watch ? [] : [
|
|
513
523
|
new LicenseWebpackPlugin({
|
|
@@ -564,9 +574,9 @@ export class SdCliClientBuilder extends EventEmitter {
|
|
|
564
574
|
emitNgModuleScope: watch,
|
|
565
575
|
inlineStyleFileExtension: "scss"
|
|
566
576
|
}),
|
|
567
|
-
new AnyComponentStyleBudgetChecker(watch ? [] : [
|
|
568
|
-
|
|
569
|
-
]),
|
|
577
|
+
// new AnyComponentStyleBudgetChecker(watch ? [] : [
|
|
578
|
+
// { type: Type.AnyComponentStyle, maximumWarning: "2kb", maximumError: "4kb" }
|
|
579
|
+
// ]),
|
|
570
580
|
{
|
|
571
581
|
apply: (compiler: webpack.Compiler) => {
|
|
572
582
|
compiler.hooks.shutdown.tap("sass-worker", () => {
|
|
@@ -29,7 +29,7 @@ export class SdCliJsLibBuilder extends EventEmitter {
|
|
|
29
29
|
const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
|
|
30
30
|
if (changeFilePaths.length === 0) return;
|
|
31
31
|
|
|
32
|
-
this._logger.debug("파일 변경 감지"
|
|
32
|
+
this._logger.debug("파일 변경 감지");
|
|
33
33
|
this.emit("change");
|
|
34
34
|
const watchBuildResults: ISdCliPackageBuildResult[] = [];
|
|
35
35
|
|
|
@@ -56,7 +56,7 @@ export class SdCliJsLibBuilder extends EventEmitter {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
private async getRelatedPathsAsync(): Promise<string[]> {
|
|
59
|
-
const mySourceGlobPath = path.resolve(this._rootPath, "**", "+(*.js|*.cjs|*.mjs
|
|
59
|
+
const mySourceGlobPath = path.resolve(this._rootPath, "**", "+(*.js|*.cjs|*.mjs)");
|
|
60
60
|
const mySourceFilePaths = await FsUtil.globAsync(mySourceGlobPath, {
|
|
61
61
|
ignore: [
|
|
62
62
|
"**/node_modules/**",
|
|
@@ -13,8 +13,8 @@ import { ObjectUtil, StringUtil } from "@simplysm/sd-core-common";
|
|
|
13
13
|
import ESLintWebpackPlugin from "eslint-webpack-plugin";
|
|
14
14
|
import CopyWebpackPlugin from "copy-webpack-plugin";
|
|
15
15
|
import { LicenseWebpackPlugin } from "license-webpack-plugin";
|
|
16
|
-
import { createHash } from "crypto";
|
|
17
16
|
import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
|
|
17
|
+
import { createHash } from "crypto";
|
|
18
18
|
import LintResult = ESLint.LintResult;
|
|
19
19
|
|
|
20
20
|
export class SdCliServerBuilder extends EventEmitter {
|
|
@@ -47,7 +47,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
47
47
|
|
|
48
48
|
// 빌드 준비
|
|
49
49
|
const extModules = this._getExternalModules();
|
|
50
|
-
const webpackConfig = this._getWebpackConfig(true, extModules
|
|
50
|
+
const webpackConfig = this._getWebpackConfig(true, extModules);
|
|
51
51
|
const compiler = webpack(webpackConfig);
|
|
52
52
|
await new Promise<void>((resolve, reject) => {
|
|
53
53
|
compiler.hooks.watchRun.tapAsync(this.constructor.name, (args, callback) => {
|
|
@@ -81,7 +81,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
81
81
|
// 빌드
|
|
82
82
|
this._logger.debug("Webpack 빌드 수행...");
|
|
83
83
|
const extModules = this._getExternalModules();
|
|
84
|
-
const webpackConfig = this._getWebpackConfig(false, extModules
|
|
84
|
+
const webpackConfig = this._getWebpackConfig(false, extModules);
|
|
85
85
|
const compiler = webpack(webpackConfig);
|
|
86
86
|
const buildResults = await new Promise<ISdCliPackageBuildResult[]>((resolve, reject) => {
|
|
87
87
|
compiler.run((err, stats) => {
|
|
@@ -102,9 +102,6 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
102
102
|
// pm2.json 파일 쓰기
|
|
103
103
|
await this._writeDistPm2ConfigFileAsync();
|
|
104
104
|
|
|
105
|
-
// iis 파일 쓰기
|
|
106
|
-
await this._writeDistIisConfigFileAsync();
|
|
107
|
-
|
|
108
105
|
// 배포용 package.json 파일 생성
|
|
109
106
|
await this._writeDistNpmConfigFileAsync(extModules.filter((item) => item.exists).map((item) => item.name));
|
|
110
107
|
|
|
@@ -130,6 +127,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
130
127
|
{
|
|
131
128
|
"name": npmConfig.name.replace(/@/g, "").replace(/\//g, "-"),
|
|
132
129
|
"script": path.basename(path.resolve(this._parsedTsconfig.options.outDir!, "main.mjs")),
|
|
130
|
+
"node_args": "--experimental-specifier-resolution=node --experimental-import-meta-resolve",
|
|
133
131
|
"watch": true,
|
|
134
132
|
"watch_delay": 2000,
|
|
135
133
|
"ignore_watch": [
|
|
@@ -154,36 +152,6 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
154
152
|
);
|
|
155
153
|
}
|
|
156
154
|
|
|
157
|
-
private async _writeDistIisConfigFileAsync(): Promise<void> {
|
|
158
|
-
if (this._config.iis === undefined || this._config.iis === false) return;
|
|
159
|
-
|
|
160
|
-
const iisDistPath = path.resolve(this._parsedTsconfig.options.outDir!, "web.config");
|
|
161
|
-
const serverExeFilePath = (this._config.iis !== true && "serverExeFilePath" in this._config.iis)
|
|
162
|
-
? (this._config.iis.serverExeFilePath ?? "C:\\Program Files\\nodejs\\node.exe")
|
|
163
|
-
: "C:\\Program Files\\nodejs\\node.exe";
|
|
164
|
-
await FsUtil.writeFileAsync(iisDistPath, `
|
|
165
|
-
<configuration>
|
|
166
|
-
<system.webServer>
|
|
167
|
-
<webSocket enabled="false" />
|
|
168
|
-
<handlers>
|
|
169
|
-
<add name="iisnode" path="main.js" verb="*" modules="iisnode" />
|
|
170
|
-
</handlers>
|
|
171
|
-
<iisnode nodeProcessCommandLine="${serverExeFilePath}"
|
|
172
|
-
watchedFiles="web.config;*.js"
|
|
173
|
-
loggingEnabled="true"
|
|
174
|
-
devErrorsEnabled="true" />
|
|
175
|
-
<rewrite>
|
|
176
|
-
<rules>
|
|
177
|
-
<rule name="main">
|
|
178
|
-
<action type="Rewrite" url="main.mjs" />
|
|
179
|
-
</rule>
|
|
180
|
-
</rules>
|
|
181
|
-
</rewrite>
|
|
182
|
-
<httpErrors errorMode="Detailed" />
|
|
183
|
-
</system.webServer>
|
|
184
|
-
</configuration>`.trim());
|
|
185
|
-
}
|
|
186
|
-
|
|
187
155
|
private async _writeDistNpmConfigFileAsync(deps: string[]): Promise<void> {
|
|
188
156
|
const distNpmConfig = ObjectUtil.clone(this._getNpmConfig(this._rootPath))!;
|
|
189
157
|
distNpmConfig.dependencies = {};
|
|
@@ -211,7 +179,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
211
179
|
].map((p) => path.dirname(p));
|
|
212
180
|
}
|
|
213
181
|
|
|
214
|
-
private _getWebpackConfig(watch: boolean,
|
|
182
|
+
private _getWebpackConfig(watch: boolean, extModules: { name: string; exists: boolean }[]): webpack.Configuration {
|
|
215
183
|
const workspaceNpmConfig = this._getNpmConfig(this._workspaceRootPath)!;
|
|
216
184
|
const workspaceName = workspaceNpmConfig.name;
|
|
217
185
|
|
|
@@ -224,18 +192,20 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
224
192
|
const cacheBasePath = path.resolve(this._rootPath, ".cache");
|
|
225
193
|
const cachePath = path.resolve(cacheBasePath, pkgVersion);
|
|
226
194
|
|
|
195
|
+
|
|
196
|
+
let prevProgressMessage = "";
|
|
227
197
|
return {
|
|
228
198
|
mode: watch ? "development" : "production",
|
|
229
199
|
devtool: false,
|
|
230
|
-
target: ["node", "
|
|
200
|
+
target: ["node", "es2020"],
|
|
231
201
|
profile: false,
|
|
232
202
|
resolve: {
|
|
233
203
|
roots: [this._rootPath],
|
|
234
204
|
extensions: [".ts", ".js", ".mjs", ".cjs"],
|
|
235
205
|
symlinks: true,
|
|
236
206
|
modules: [this._workspaceRootPath, "node_modules"],
|
|
237
|
-
mainFields: ["
|
|
238
|
-
conditionNames: ["
|
|
207
|
+
mainFields: ["es2020", "default", "module", "main"],
|
|
208
|
+
conditionNames: ["es2020", "..."]
|
|
239
209
|
},
|
|
240
210
|
resolveLoader: {
|
|
241
211
|
symlinks: true
|
|
@@ -251,17 +221,23 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
251
221
|
hashFunction: "xxhash64",
|
|
252
222
|
clean: true,
|
|
253
223
|
path: this._parsedTsconfig.options.outDir,
|
|
254
|
-
filename: "[name].
|
|
255
|
-
chunkFilename: "[name].
|
|
224
|
+
filename: "[name].mjs",
|
|
225
|
+
chunkFilename: "[name].mjs",
|
|
256
226
|
assetModuleFilename: "res/[name][ext][query]",
|
|
257
|
-
|
|
227
|
+
library: {
|
|
228
|
+
type: "module"
|
|
229
|
+
},
|
|
230
|
+
module: true
|
|
231
|
+
},
|
|
232
|
+
experiments: {
|
|
233
|
+
outputModule: true
|
|
258
234
|
},
|
|
259
235
|
watch: false,
|
|
260
236
|
watchOptions: { poll: undefined, ignored: undefined },
|
|
261
237
|
performance: { hints: false },
|
|
262
238
|
infrastructureLogging: { level: "error" },
|
|
263
239
|
stats: "errors-warnings",
|
|
264
|
-
externals:
|
|
240
|
+
externals: extModules.toObject((item) => item.name, (item) => "node-commonjs " + item.name),
|
|
265
241
|
cache: {
|
|
266
242
|
type: "filesystem",
|
|
267
243
|
profile: watch ? undefined : false,
|
|
@@ -276,6 +252,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
276
252
|
.update(watch.toString())
|
|
277
253
|
.digest("hex")
|
|
278
254
|
},
|
|
255
|
+
// cache: { type: "memory", maxGenerations: 1 },
|
|
279
256
|
...watch ? {
|
|
280
257
|
snapshot: {
|
|
281
258
|
immutablePaths: internalModuleCachePaths,
|
|
@@ -289,7 +266,7 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
289
266
|
extractComments: false,
|
|
290
267
|
terserOptions: {
|
|
291
268
|
compress: true,
|
|
292
|
-
ecma:
|
|
269
|
+
ecma: 2020,
|
|
293
270
|
sourceMap: false,
|
|
294
271
|
keep_classnames: true,
|
|
295
272
|
keep_fnames: true,
|
|
@@ -318,9 +295,6 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
318
295
|
test: /\.[cm]?[tj]sx?$/,
|
|
319
296
|
resolve: {
|
|
320
297
|
fullySpecified: false
|
|
321
|
-
},
|
|
322
|
-
use: {
|
|
323
|
-
loader: "babel-loader"
|
|
324
298
|
}
|
|
325
299
|
},
|
|
326
300
|
...watch ? [
|
|
@@ -420,14 +394,18 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
420
394
|
}
|
|
421
395
|
return resultMessages.join(os.EOL);
|
|
422
396
|
}
|
|
423
|
-
})
|
|
397
|
+
}),
|
|
424
398
|
new webpack.ProgressPlugin({
|
|
425
399
|
handler: (per: number, msg: string, ...args: string[]) => {
|
|
426
400
|
const phaseText = msg ? ` - phase: ${msg}` : "";
|
|
427
401
|
const argsText = args.length > 0 ? ` - args: [${args.join(", ")}]` : "";
|
|
428
|
-
|
|
402
|
+
const progressMessage = `Webpack 빌드 수행중...(${Math.round(per * 100)}%)${phaseText}${argsText}`;
|
|
403
|
+
if (progressMessage !== prevProgressMessage) {
|
|
404
|
+
prevProgressMessage = progressMessage;
|
|
405
|
+
this._logger.debug(progressMessage);
|
|
406
|
+
}
|
|
429
407
|
}
|
|
430
|
-
})
|
|
408
|
+
})
|
|
431
409
|
]
|
|
432
410
|
};
|
|
433
411
|
}
|
|
@@ -455,6 +433,10 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
455
433
|
results.push({ name: moduleName, exists: true });
|
|
456
434
|
}
|
|
457
435
|
|
|
436
|
+
if (this._config.externalNodeModules?.includes(moduleName)) {
|
|
437
|
+
results.push({ name: moduleName, exists: true });
|
|
438
|
+
}
|
|
439
|
+
|
|
458
440
|
fn(modulePath);
|
|
459
441
|
}
|
|
460
442
|
|
|
@@ -472,6 +454,10 @@ export class SdCliServerBuilder extends EventEmitter {
|
|
|
472
454
|
results.push({ name: optModuleName, exists: true });
|
|
473
455
|
}
|
|
474
456
|
|
|
457
|
+
if (this._config.externalNodeModules?.includes(optModuleName)) {
|
|
458
|
+
results.push({ name: optModuleName, exists: true });
|
|
459
|
+
}
|
|
460
|
+
|
|
475
461
|
fn(optModulePath);
|
|
476
462
|
}
|
|
477
463
|
};
|
|
@@ -37,6 +37,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
|
|
|
37
37
|
private readonly _npmConfigMap = new Map<string, INpmConfig>();
|
|
38
38
|
|
|
39
39
|
private readonly _isAngular: boolean;
|
|
40
|
+
private readonly _hasAngularRoute: boolean;
|
|
40
41
|
|
|
41
42
|
public constructor(private readonly _rootPath: string,
|
|
42
43
|
private readonly _config: ISdCliLibPackageConfig,
|
|
@@ -52,6 +53,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
|
|
|
52
53
|
|
|
53
54
|
// isAngular
|
|
54
55
|
this._isAngular = SdCliNpmConfigUtil.getDependencies(npmConfig).defaults.includes("@angular/core");
|
|
56
|
+
this._hasAngularRoute = SdCliNpmConfigUtil.getDependencies(npmConfig).defaults.includes("@angular/router");
|
|
55
57
|
|
|
56
58
|
// tsconfig
|
|
57
59
|
this._tsconfigFilePath = path.resolve(this._rootPath, "tsconfig-build.json");
|
|
@@ -71,11 +73,11 @@ export class SdCliTsLibBuilder extends EventEmitter {
|
|
|
71
73
|
"print-templates",
|
|
72
74
|
"toasts",
|
|
73
75
|
"AppPage"
|
|
74
|
-
], {
|
|
76
|
+
], this._hasAngularRoute ? {
|
|
75
77
|
glob: "**/*Page.ts",
|
|
76
78
|
fileEndsWith: "Page",
|
|
77
79
|
rootClassName: "AppPage"
|
|
78
|
-
});
|
|
80
|
+
} : undefined);
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
// index 생성기 초기화
|
|
@@ -117,12 +119,21 @@ export class SdCliTsLibBuilder extends EventEmitter {
|
|
|
117
119
|
const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
|
|
118
120
|
if (changeFilePaths.length === 0) return;
|
|
119
121
|
|
|
120
|
-
this._logger.debug("파일 변경 감지",
|
|
122
|
+
this._logger.debug("파일 변경 감지", changeFilePaths);
|
|
121
123
|
this.emit("change");
|
|
122
124
|
|
|
123
125
|
this._logger.debug("변경된 파일의 캐쉬 삭제...");
|
|
124
126
|
for (const changeFilePath of changeFilePaths) {
|
|
125
|
-
this._fileCache.
|
|
127
|
+
const fileCache = this._fileCache.get(PathUtil.posix(changeFilePath));
|
|
128
|
+
if (fileCache) {
|
|
129
|
+
if (fileCache.importerSet) {
|
|
130
|
+
for (const importer of fileCache.importerSet.values()) {
|
|
131
|
+
this._fileCache.delete(importer);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this._fileCache.delete(PathUtil.posix(changeFilePath));
|
|
136
|
+
}
|
|
126
137
|
}
|
|
127
138
|
|
|
128
139
|
if (this._ngModuleGenerator) {
|
|
@@ -161,6 +172,10 @@ export class SdCliTsLibBuilder extends EventEmitter {
|
|
|
161
172
|
this._logger.debug("린트...");
|
|
162
173
|
buildResults.push(...await this._linter.lintAsync(relatedPaths, buildPack.program));
|
|
163
174
|
|
|
175
|
+
this._logger.debug("변경감지 대상목록 재구성...");
|
|
176
|
+
const watchRelatedPaths = await this.getAllRelatedPathsAsync();
|
|
177
|
+
watcher.add(watchRelatedPaths);
|
|
178
|
+
|
|
164
179
|
this.emit("complete", buildResults);
|
|
165
180
|
}
|
|
166
181
|
|
|
@@ -202,7 +217,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
|
|
|
202
217
|
|| (/node_modules[\\/]@simplysm[\\/]/).test(filePath)
|
|
203
218
|
|| workspaceRegex.test(filePath);
|
|
204
219
|
});
|
|
205
|
-
const mySourceGlobPath = path.resolve(this._rootPath, "**", "+(*.js|*.cjs|*.mjs|*.ts)");
|
|
220
|
+
const mySourceGlobPath = path.resolve(this._rootPath, "**", "+(*.js|*.cjs|*.mjs|*.ts|*.scss)");
|
|
206
221
|
const mySourceFilePaths = await FsUtil.globAsync(mySourceGlobPath, {
|
|
207
222
|
ignore: [
|
|
208
223
|
"**/node_modules/**",
|
|
@@ -296,7 +311,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
|
|
|
296
311
|
char: undefined,
|
|
297
312
|
code: undefined,
|
|
298
313
|
severity: "error",
|
|
299
|
-
message: err.
|
|
314
|
+
message: err.stack
|
|
300
315
|
}];
|
|
301
316
|
}
|
|
302
317
|
}
|
|
@@ -419,6 +434,7 @@ interface IFileCache {
|
|
|
419
434
|
sourceFile?: ts.SourceFile;
|
|
420
435
|
content?: string;
|
|
421
436
|
styleContent?: string;
|
|
437
|
+
importerSet?: Set<string>;
|
|
422
438
|
}
|
|
423
439
|
|
|
424
440
|
interface ISdBuildPack {
|
package/src/commons.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface INpmConfig {
|
|
2
2
|
name: string;
|
|
3
3
|
version: string;
|
|
4
|
+
type?: "module";
|
|
4
5
|
workspaces?: string[];
|
|
5
6
|
main?: string;
|
|
6
7
|
types?: string;
|
|
@@ -56,7 +57,8 @@ export interface ISdCliServerPackageConfig {
|
|
|
56
57
|
env?: Record<string, string>;
|
|
57
58
|
configs?: Record<string, any>;
|
|
58
59
|
pm2?: Record<string, any> | boolean;
|
|
59
|
-
|
|
60
|
+
externalNodeModules?: string[];
|
|
61
|
+
publish?: TSdCliPublishConfig;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
export interface ISdCliClientPackageConfig {
|
|
@@ -65,6 +67,23 @@ export interface ISdCliClientPackageConfig {
|
|
|
65
67
|
server: string;
|
|
66
68
|
env?: Record<string, string>;
|
|
67
69
|
configs?: Record<string, any>;
|
|
70
|
+
publish?: TSdCliPublishConfig;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type TSdCliPublishConfig = ISdCliFtpPublishConfig | ISdCliLocalDirectoryPublishConfig;
|
|
74
|
+
|
|
75
|
+
export interface ISdCliFtpPublishConfig {
|
|
76
|
+
type: "ftp" | "ftps" | "sftp";
|
|
77
|
+
host: string;
|
|
78
|
+
port?: number;
|
|
79
|
+
path: string;
|
|
80
|
+
user: string;
|
|
81
|
+
pass: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ISdCliLocalDirectoryPublishConfig {
|
|
85
|
+
type: "local-directory";
|
|
86
|
+
path: string;
|
|
68
87
|
}
|
|
69
88
|
|
|
70
89
|
export interface ISdCliClientPackageCordovaConfig {
|
|
@@ -88,4 +107,4 @@ export interface ISdCliClientPackageCordovaConfig {
|
|
|
88
107
|
};
|
|
89
108
|
}
|
|
90
109
|
|
|
91
|
-
export type TSdCliCordovaPlatform = "browser" | "ios" | "android" | "
|
|
110
|
+
export type TSdCliCordovaPlatform = "browser" | "ios" | "android" | "electron";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import readline from "readline";
|
|
2
|
+
import { Writable } from "stream";
|
|
3
|
+
import crypto from "crypto";
|
|
4
|
+
import { FsUtil } from "@simplysm/sd-core-node";
|
|
5
|
+
import os from "os";
|
|
6
|
+
|
|
7
|
+
export class SdCliFileCrypto {
|
|
8
|
+
public async encryptAsync(filePath: string): Promise<void> {
|
|
9
|
+
if (!FsUtil.exists(filePath)) {
|
|
10
|
+
throw new Error(`파일 '${filePath}'을 찾을 수 없습니다.`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const key = await this._readKeyAsync("password: ");
|
|
14
|
+
if (!key) {
|
|
15
|
+
throw new Error("암호화키를 반드시 입력해야 합니다.");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const confirmKey = await this._readKeyAsync("confirm password: ");
|
|
19
|
+
if (key !== confirmKey) {
|
|
20
|
+
throw new Error("암호화키가 서로 다릅니다.");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this._encryptFile(filePath, key, filePath + ".enc");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public async decryptAsync(encFilePath: string): Promise<void> {
|
|
27
|
+
if (!FsUtil.exists(encFilePath)) {
|
|
28
|
+
throw new Error(`파일 '${encFilePath}'을 찾을 수 없습니다.`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!encFilePath.endsWith(".enc")) {
|
|
32
|
+
throw new Error(`파일 ${encFilePath}의 확장자가 '.enc'가 아닙니다.`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const resultFilePath = encFilePath.slice(0, -4);
|
|
36
|
+
if (FsUtil.exists(resultFilePath)) {
|
|
37
|
+
process.stdout.write(`복호화 시, 현재 존재하는 '${resultFilePath}'파일을 덮어씁니다.${os.EOL}`, "utf-8");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const key = await this._readKeyAsync("password: ");
|
|
41
|
+
if (!key) {
|
|
42
|
+
throw new Error("암호화키를 반드시 입력해야 합니다.");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this._decryptFile(encFilePath, key, resultFilePath);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private _encryptFile(filePath: string, key: string, encFilePath: string): void {
|
|
49
|
+
const iv = Buffer.alloc(16, 0);
|
|
50
|
+
const cipheriv = crypto.createCipheriv("aes-192-cbc", crypto.scryptSync(key, "salt", 24), iv);
|
|
51
|
+
|
|
52
|
+
const input = FsUtil.createReadStream(filePath);
|
|
53
|
+
const output = FsUtil.createWriteStream(encFilePath);
|
|
54
|
+
input.pipe(cipheriv).pipe(output);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private _decryptFile(encFilePath: string, key: string, filePath: string): void {
|
|
58
|
+
const iv = Buffer.alloc(16, 0);
|
|
59
|
+
const cipheriv = crypto.createDecipheriv("aes-192-cbc", crypto.scryptSync(key, "salt", 24), iv);
|
|
60
|
+
|
|
61
|
+
const input = FsUtil.createReadStream(encFilePath);
|
|
62
|
+
const output = FsUtil.createWriteStream(filePath);
|
|
63
|
+
input.pipe(cipheriv).pipe(output);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private async _readKeyAsync(message: string): Promise<string> {
|
|
67
|
+
process.stdout.write(message, "utf-8");
|
|
68
|
+
|
|
69
|
+
return await new Promise((resolve) => {
|
|
70
|
+
const rl = readline.createInterface({
|
|
71
|
+
input: process.stdin,
|
|
72
|
+
output: new Writable({
|
|
73
|
+
write: (chunk, encoding, callback) => {
|
|
74
|
+
callback();
|
|
75
|
+
}
|
|
76
|
+
}),
|
|
77
|
+
terminal: true
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
rl.question(message, (answer) => {
|
|
81
|
+
process.stdout.write(os.EOL, "utf-8");
|
|
82
|
+
resolve(answer);
|
|
83
|
+
rl.close();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -14,7 +14,7 @@ export class SdCliLocalUpdate {
|
|
|
14
14
|
if (!conf.localUpdates) return;
|
|
15
15
|
|
|
16
16
|
const updatePathInfos = await this._getUpdatePathInfosAsync(conf.localUpdates);
|
|
17
|
-
this._logger.debug("로컬 업데이트 구성"
|
|
17
|
+
this._logger.debug("로컬 업데이트 구성");
|
|
18
18
|
|
|
19
19
|
this._logger.log("로컬 라이브러리 업데이트 시작...");
|
|
20
20
|
for (const updatePathInfo of updatePathInfos) {
|
|
@@ -37,7 +37,7 @@ export class SdCliLocalUpdate {
|
|
|
37
37
|
if (!conf.localUpdates) return;
|
|
38
38
|
|
|
39
39
|
const updatePathInfos = await this._getUpdatePathInfosAsync(conf.localUpdates);
|
|
40
|
-
this._logger.debug("로컬 업데이트 구성"
|
|
40
|
+
this._logger.debug("로컬 업데이트 구성");
|
|
41
41
|
|
|
42
42
|
const watchPaths = (await updatePathInfos.mapManyAsync(async (item) => await this._getWatchPathsAsync(item.source))).distinct();
|
|
43
43
|
|
|
@@ -38,7 +38,7 @@ export class SdCliPrepare {
|
|
|
38
38
|
if (node.hasChildPerformanceWarning) {
|
|
39
39
|
node.parent.hasChildPerformanceWarning = true;
|
|
40
40
|
}
|
|
41
|
-
else if (usage.user + usage.system >
|
|
41
|
+
else if (usage.user + usage.system > 1000 * 1000 && node.kind !== 253) {
|
|
42
42
|
error(node, {
|
|
43
43
|
code: 9000,
|
|
44
44
|
category: ts.DiagnosticCategory.Warning,
|
|
@@ -50,6 +50,6 @@ export class SdCliPrepare {
|
|
|
50
50
|
}
|
|
51
51
|
}`);
|
|
52
52
|
await FsUtil.writeFileAsync(filePath, modifiedFileContent);
|
|
53
|
-
return
|
|
53
|
+
return modifiedFileContent.includes("const prevUsage = process.cpuUsage();");
|
|
54
54
|
}
|
|
55
55
|
}
|