@simplysm/sd-cli 7.0.80 → 7.0.145

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/bin/sd-cli.mjs +36 -6
  2. package/dist/build-tool/SdCliCordova.d.ts +4 -4
  3. package/dist/build-tool/SdCliCordova.mjs +108 -107
  4. package/dist/build-tool/SdCliElectron.d.ts +9 -0
  5. package/dist/build-tool/SdCliElectron.mjs +66 -0
  6. package/dist/build-tool/SdCliGithubApi.d.ts +14 -0
  7. package/dist/build-tool/SdCliGithubApi.mjs +96 -0
  8. package/dist/builder/SdCliClientBuilder.mjs +151 -65
  9. package/dist/builder/SdCliJsLibBuilder.mjs +4 -4
  10. package/dist/builder/SdCliServerBuilder.mjs +6 -7
  11. package/dist/builder/SdCliTsLibBuilder.mjs +4 -4
  12. package/dist/commons.d.ts +38 -11
  13. package/dist/entry-points/SdCliLocalUpdate.mjs +3 -3
  14. package/dist/entry-points/SdCliWorkspace.mjs +48 -6
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.mjs +3 -1
  17. package/dist/ng-tools/babel/SdCliBbFileMetadata.mjs +2 -2
  18. package/dist/ng-tools/babel/SdCliBbRootMetadata.d.ts +1 -0
  19. package/dist/ng-tools/babel/SdCliBbRootMetadata.mjs +29 -3
  20. package/dist/packages/SdCliPackage.mjs +13 -2
  21. package/package.json +13 -7
  22. package/src/bin/sd-cli.ts +46 -7
  23. package/src/build-tool/SdCliCordova.ts +133 -141
  24. package/src/build-tool/SdCliElectron.ts +76 -0
  25. package/src/build-tool/SdCliGithubApi.ts +117 -0
  26. package/src/builder/SdCliClientBuilder.ts +165 -65
  27. package/src/builder/SdCliJsLibBuilder.ts +3 -3
  28. package/src/builder/SdCliServerBuilder.ts +6 -7
  29. package/src/builder/SdCliTsLibBuilder.ts +3 -3
  30. package/src/commons.ts +35 -11
  31. package/src/entry-points/SdCliLocalUpdate.ts +2 -2
  32. package/src/entry-points/SdCliWorkspace.ts +47 -6
  33. package/src/index.ts +2 -0
  34. package/src/ng-tools/babel/SdCliBbFileMetadata.ts +1 -1
  35. package/src/ng-tools/babel/SdCliBbRootMetadata.ts +30 -2
  36. package/src/packages/SdCliPackage.ts +18 -1
  37. 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
- CommonJsUsageWarnPlugin,
11
10
  DedupeModuleResolvePlugin,
12
11
  JavaScriptOptimizerPlugin,
13
12
  SuppressExtractedTextChunksWebpackPlugin
@@ -31,10 +30,10 @@ import { TransferSizePlugin } from "@angular-devkit/build-angular/src/webpack/pl
31
30
  import { CssOptimizerPlugin } from "@angular-devkit/build-angular/src/webpack/plugins/css-optimizer-plugin";
32
31
  import browserslist from "browserslist";
33
32
  import { augmentAppWithServiceWorker } from "@angular-devkit/build-angular/src/utils/service-worker";
34
- import { StringUtil } from "@simplysm/sd-core-common";
35
33
  import { SdCliNgModuleGenerator } from "../ng-tools/SdCliNgModuleGenerator";
36
34
  import { SdCliCordova } from "../build-tool/SdCliCordova";
37
35
  import { SdCliNpmConfigUtil } from "../utils/SdCliNpmConfigUtil";
36
+ import electronBuilder from "electron-builder";
38
37
  import LintResult = ESLint.LintResult;
39
38
 
40
39
  export class SdCliClientBuilder extends EventEmitter {
@@ -84,8 +83,8 @@ export class SdCliClientBuilder extends EventEmitter {
84
83
  } : undefined);
85
84
 
86
85
  // CORDOVA
87
- if (this._config.cordova) {
88
- this._cordova = new SdCliCordova(this._rootPath, this._config.cordova);
86
+ if (this._config.builder?.cordova) {
87
+ this._cordova = new SdCliCordova(this._rootPath, this._config.builder.cordova);
89
88
  }
90
89
  }
91
90
 
@@ -97,6 +96,7 @@ export class SdCliClientBuilder extends EventEmitter {
97
96
 
98
97
  public async watchAsync(): Promise<NextHandleFunction[]> {
99
98
  // DIST 비우기
99
+ await FsUtil.removeAsync(path.resolve(this._rootPath, ".electron"));
100
100
  await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
101
101
 
102
102
  // NgModule 생성
@@ -109,10 +109,11 @@ export class SdCliClientBuilder extends EventEmitter {
109
109
  }
110
110
 
111
111
  // 빌드 준비
112
- const webpackConfig = this._getWebpackConfig(true);
113
- const compiler = webpack(webpackConfig);
112
+ const webpackConfigs = (Object.keys(this._config.builder ?? { web: {} }) as ("web" | "cordova" | "electron")[])
113
+ .map((builderType) => this._getWebpackConfig(true, builderType));
114
+ const multiCompiler = webpack(webpackConfigs);
114
115
  return await new Promise<NextHandleFunction[]>((resolve, reject) => {
115
- compiler.hooks.invalid.tap(this.constructor.name, (fileName) => {
116
+ multiCompiler.hooks.invalid.tap(this.constructor.name, (fileName) => {
116
117
  if (fileName != null) {
117
118
  this._logger.debug("파일변경 감지", fileName);
118
119
  // NgModule 캐시 삭제
@@ -120,7 +121,7 @@ export class SdCliClientBuilder extends EventEmitter {
120
121
  }
121
122
  });
122
123
 
123
- compiler.hooks.watchRun.tapAsync(this.constructor.name, async (args, callback) => {
124
+ multiCompiler.hooks.watchRun.tapAsync(this.constructor.name, async (args, callback) => {
124
125
  this.emit("change");
125
126
 
126
127
  // NgModule 생성
@@ -131,54 +132,58 @@ export class SdCliClientBuilder extends EventEmitter {
131
132
  this._logger.debug("Webpack 빌드 수행...");
132
133
  });
133
134
 
134
- compiler.hooks.failed.tap(this.constructor.name, (err) => {
135
- this.emit("complete", [{
136
- filePath: undefined,
137
- line: undefined,
138
- char: undefined,
139
- code: undefined,
140
- severity: "error",
141
- message: err.stack
142
- }]);
143
- reject(err);
144
- return;
145
- });
135
+ for (const compiler of multiCompiler.compilers) {
136
+ compiler.hooks.failed.tap(this.constructor.name, (err) => {
137
+ this.emit("complete", [{
138
+ filePath: undefined,
139
+ line: undefined,
140
+ char: undefined,
141
+ code: undefined,
142
+ severity: "error",
143
+ message: err.stack
144
+ }]);
145
+ reject(err);
146
+ return;
147
+ });
148
+ }
146
149
 
147
- compiler.hooks.done.tap(this.constructor.name, async (stats) => {
150
+ multiCompiler.hooks.done.tap(this.constructor.name, async (multiStats) => {
148
151
  // 결과 반환
149
- const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
152
+ const results = multiStats.stats.mapMany((stats) => SdCliBuildResultUtil.convertFromWebpackStats(stats));
150
153
 
151
154
  // .config.json 파일 쓰기
152
155
  const npmConfig = this._getNpmConfig(this._rootPath)!;
153
156
  const packageKey = npmConfig.name.split("/").last()!;
154
157
 
155
- const configDistPath = !StringUtil.isNullOrEmpty(this._config.server)
158
+ const configDistPath = typeof this._config.server === "string"
156
159
  ? path.resolve(this._workspaceRootPath, "packages", this._config.server, "dist/www", packageKey, ".config.json")
157
160
  : path.resolve(this._parsedTsconfig.options.outDir!, ".config.json");
158
161
  await FsUtil.writeFileAsync(configDistPath, JSON.stringify(this._config.configs ?? {}, undefined, 2));
159
162
 
160
163
  // 마무리
161
164
  this._logger.debug("Webpack 빌드 완료");
162
- resolve([devMiddleware, hotMiddleware]);
165
+ resolve(middlewares);
163
166
 
164
167
  this.emit("complete", results);
165
168
  });
166
169
 
167
- const devMiddleware = wdm(compiler, {
168
- publicPath: webpackConfig.output!.publicPath as string,
169
- index: "index.html",
170
- stats: false
171
- });
172
-
173
- const hotMiddleware = whm(compiler, {
174
- path: `${webpackConfig.output!.publicPath as string}__webpack_hmr`,
175
- log: false
176
- });
170
+ const middlewares = multiCompiler.compilers.mapMany((compiler) => [
171
+ wdm(compiler, {
172
+ publicPath: compiler.options.output.publicPath,
173
+ index: "index.html",
174
+ stats: false
175
+ }),
176
+ whm(compiler, {
177
+ path: `${compiler.options.output.publicPath}__webpack_hmr`,
178
+ log: false
179
+ })
180
+ ]);
177
181
  });
178
182
  }
179
183
 
180
184
  public async buildAsync(): Promise<ISdCliPackageBuildResult[]> {
181
185
  // DIST 비우기
186
+ await FsUtil.removeAsync(path.resolve(this._rootPath, ".electron"));
182
187
  await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
183
188
 
184
189
  // NgModule 생성
@@ -186,17 +191,18 @@ export class SdCliClientBuilder extends EventEmitter {
186
191
 
187
192
  // 빌드
188
193
  this._logger.debug("Webpack 빌드 수행...");
189
- const webpackConfig = this._getWebpackConfig(false);
190
- const compiler = webpack(webpackConfig);
194
+ const builderTypes = (Object.keys(this._config.builder ?? { web: {} }) as ("web" | "cordova" | "electron")[]);
195
+ const webpackConfigs = builderTypes.map((builderType) => this._getWebpackConfig(false, builderType));
196
+ const multipleCompiler = webpack(webpackConfigs);
191
197
  const buildResults = await new Promise<ISdCliPackageBuildResult[]>((resolve, reject) => {
192
- compiler.run((err, stats) => {
193
- if (err != null || stats == null) {
198
+ multipleCompiler.run((err, multiStats) => {
199
+ if (err != null || multiStats == null) {
194
200
  reject(err);
195
201
  return;
196
202
  }
197
203
 
198
204
  // 결과 반환
199
- const results = SdCliBuildResultUtil.convertFromWebpackStats(stats);
205
+ const results = multiStats.stats.mapMany((stats) => SdCliBuildResultUtil.convertFromWebpackStats(stats));
200
206
  resolve(results);
201
207
  });
202
208
  });
@@ -206,11 +212,11 @@ export class SdCliClientBuilder extends EventEmitter {
206
212
  await FsUtil.writeFileAsync(targetPath, JSON.stringify(this._config.configs ?? {}, undefined, 2));
207
213
 
208
214
  // service-worker 처리
209
- if (FsUtil.exists(path.resolve(this._rootPath, "ngsw-config.json"))) {
215
+ if (builderTypes.includes("web") && FsUtil.exists(path.resolve(this._rootPath, "ngsw-config.json"))) {
210
216
  const packageKey = this._getNpmConfig(this._rootPath)!.name.split("/").last()!;
211
217
  await augmentAppWithServiceWorker(
212
218
  PathUtil.posix(path.relative(this._workspaceRootPath, this._rootPath)) as any,
213
- PathUtil.posix(path.relative(this._workspaceRootPath, this._parsedTsconfig.options.outDir!)) as any,
219
+ PathUtil.posix(path.relative(this._workspaceRootPath, path.resolve(this._parsedTsconfig.options.outDir!))) as any,
214
220
  `/${packageKey}/`,
215
221
  PathUtil.posix(path.relative(this._workspaceRootPath, path.resolve(this._rootPath, "ngsw-config.json")))
216
222
  );
@@ -222,7 +228,74 @@ export class SdCliClientBuilder extends EventEmitter {
222
228
  await this._cordova.initializeAsync();
223
229
 
224
230
  this._logger.debug("CORDOVA 빌드...");
225
- await this._cordova.buildAsync(this._parsedTsconfig.options.outDir!);
231
+ await this._cordova.buildAsync(path.resolve(this._parsedTsconfig.options.outDir!, "cordova"));
232
+ }
233
+
234
+ // ELECTRON
235
+ if (this._config.builder?.electron) {
236
+ const npmConfig = this._getNpmConfig(this._rootPath)!;
237
+
238
+ const electronVersion = npmConfig.dependencies?.["electron"];
239
+ if (electronVersion === undefined) {
240
+ throw new Error("ELECTRON 빌드 패키지의 'dependencies'에는 'electron'이 반드시 포함되어야 합니다.");
241
+ }
242
+
243
+ const dotenvVersion = npmConfig.dependencies?.["dotenv"];
244
+ if (dotenvVersion === undefined) {
245
+ throw new Error("ELECTRON 빌드 패키지의 'dependencies'에는 'dotenv'가 반드시 포함되어야 합니다.");
246
+ }
247
+
248
+ const electronSrcPath = path.resolve(this._rootPath, `.electron/src`);
249
+ const electronDistPath = path.resolve(this._rootPath, `.electron/dist`);
250
+
251
+ await FsUtil.writeJsonAsync(path.resolve(electronSrcPath, `package.json`), {
252
+ name: npmConfig.name,
253
+ version: npmConfig.version,
254
+ description: npmConfig.description,
255
+ main: "electron.js",
256
+ author: npmConfig.author,
257
+ license: npmConfig.license,
258
+ devDependencies: {
259
+ "electron": electronVersion.replace("^", "")
260
+ },
261
+ dependencies: {
262
+ "dotenv": dotenvVersion
263
+ }
264
+ });
265
+
266
+ await FsUtil.writeFileAsync(path.resolve(electronSrcPath, `.env`), [
267
+ "NODE_ENV=production",
268
+ `SD_VERSION=${npmConfig.version}`,
269
+ (this._config.builder.electron.icon !== undefined) ? `SD_ELECTRON_ICON=${this._config.builder.electron.icon}` : `SD_ELECTRON_ICON=favicon.ico`,
270
+ ...(this._config.env !== undefined) ? Object.keys(this._config.env).map((key) => `${key}=${this._config.env![key]}`) : []
271
+ ].filterExists().join("\n"));
272
+
273
+ let electronTsFileContent = await FsUtil.readFileAsync(path.resolve(this._rootPath, `src/electron.ts`));
274
+ electronTsFileContent = "require(\"dotenv\").config({ path: `${__dirname}\\\\.env` });\n" + electronTsFileContent;
275
+ const result = ts.transpileModule(electronTsFileContent, { compilerOptions: { module: ts.ModuleKind.CommonJS } });
276
+ await FsUtil.writeFileAsync(path.resolve(electronSrcPath, "electron.js"), result.outputText);
277
+
278
+ await electronBuilder.build({
279
+ targets: electronBuilder.Platform.WINDOWS.createTarget(),
280
+ config: {
281
+ appId: this._config.builder.electron.appId,
282
+ productName: npmConfig.description,
283
+ nsis: {},
284
+ directories: {
285
+ app: electronSrcPath,
286
+ output: electronDistPath
287
+ }
288
+ }
289
+ });
290
+
291
+ await FsUtil.copyAsync(
292
+ path.resolve(this._rootPath, `.electron/dist/${npmConfig.description} Setup ${npmConfig.version}.exe`),
293
+ path.resolve(this._parsedTsconfig.options.outDir!, `electron/${npmConfig.description}-v${npmConfig.version}.exe`)
294
+ );
295
+ await FsUtil.copyAsync(
296
+ path.resolve(this._rootPath, `.electron/dist/${npmConfig.description} Setup ${npmConfig.version}.exe`),
297
+ path.resolve(this._parsedTsconfig.options.outDir!, `electron/${npmConfig.description}-latest.exe`)
298
+ );
226
299
  }
227
300
 
228
301
  // 마무리
@@ -237,23 +310,28 @@ export class SdCliClientBuilder extends EventEmitter {
237
310
  ].map((p) => path.dirname(p));
238
311
  }
239
312
 
240
- private _getWebpackConfig(watch: boolean): webpack.Configuration {
313
+ private _getWebpackConfig(watch: boolean, builderType: "web" | "cordova" | "electron"): webpack.Configuration {
241
314
  const workspaceNpmConfig = this._getNpmConfig(this._workspaceRootPath)!;
242
315
  const workspaceName = workspaceNpmConfig.name;
243
316
 
244
317
  const internalModuleCachePaths = watch ? this._getInternalModuleCachePaths(workspaceName) : undefined;
245
318
 
246
319
  const npmConfig = this._getNpmConfig(this._rootPath)!;
247
- const pkgVersion = npmConfig.version;
248
- const ngVersion = this._getNpmConfig(FsUtil.findAllParentChildDirPaths("node_modules/@angular/core", this._rootPath, this._workspaceRootPath)[0])!.version;
320
+ // const pkgVersion = npmConfig.version;
321
+ // const ngVersion = this._getNpmConfig(FsUtil.findAllParentChildDirPaths("node_modules/@angular/core", this._rootPath, this._workspaceRootPath)[0])!.version;
322
+
323
+ const workspacePkgLockContent = FsUtil.readFile(path.resolve(this._workspaceRootPath, "package-lock.json"));
249
324
 
250
325
  const pkgKey = npmConfig.name.split("/").last()!;
251
- const publicPath = (this._cordova && !watch) ? `` : `/${pkgKey}/`;
326
+ const publicPath = builderType === "web" ? `/${pkgKey}/` : watch ? `/${pkgKey}/${builderType}/` : ``;
252
327
 
253
328
  const cacheBasePath = path.resolve(this._rootPath, ".cache");
254
- const cachePath = path.resolve(cacheBasePath, pkgVersion);
329
+ // const cachePath = path.resolve(cacheBasePath, pkgVersion);
255
330
 
256
- const distPath = (this._cordova && !watch) ? path.resolve(this._cordova.cordovaPath, "www") : this._parsedTsconfig.options.outDir;
331
+ const distPath = (builderType === "cordova" && !watch) ? path.resolve(this._cordova!.cordovaPath, "www")
332
+ : (builderType === "electron" && !watch) ? path.resolve(this._rootPath, ".electron/src")
333
+ : builderType === "web" ? this._parsedTsconfig.options.outDir
334
+ : `${this._parsedTsconfig.options.outDir}/${builderType}`;
257
335
 
258
336
  const sassImplementation = new SassWorkerImplementation();
259
337
 
@@ -265,7 +343,7 @@ export class SdCliClientBuilder extends EventEmitter {
265
343
  return {
266
344
  mode: watch ? "development" : "production",
267
345
  devtool: false,
268
- target: ["web", "es2015"],
346
+ target: builderType === "electron" ? ["electron-renderer", "es2015"] : ["web", "es2015"],
269
347
  profile: false,
270
348
  resolve: {
271
349
  roots: [this._rootPath],
@@ -273,7 +351,7 @@ export class SdCliClientBuilder extends EventEmitter {
273
351
  symlinks: true,
274
352
  modules: [this._workspaceRootPath, "node_modules"],
275
353
  mainFields: ["es2015", "browser", "module", "main"],
276
- conditionNames: ["es2015", "..."]
354
+ conditionNames: ["es2015", "..."],
277
355
  },
278
356
  resolveLoader: {
279
357
  symlinks: true
@@ -316,14 +394,15 @@ export class SdCliClientBuilder extends EventEmitter {
316
394
  cache: {
317
395
  type: "filesystem",
318
396
  profile: watch ? undefined : false,
319
- cacheDirectory: path.resolve(cachePath, "angular-webpack"),
397
+ cacheDirectory: path.resolve(cacheBasePath, "angular-webpack"),
320
398
  maxMemoryGenerations: 1,
321
399
  name: createHash("sha1")
322
- .update(pkgVersion)
323
- .update(ngVersion)
400
+ .update(workspacePkgLockContent)
401
+ // .update(pkgVersion)
402
+ // .update(ngVersion)
324
403
  .update(JSON.stringify(this._parsedTsconfig.options))
325
- .update(this._workspaceRootPath)
326
- .update(this._rootPath)
404
+ // .update(this._workspaceRootPath)
405
+ // .update(this._rootPath)
327
406
  .update(JSON.stringify(this._config))
328
407
  .update(watch.toString())
329
408
  .digest("hex")
@@ -430,7 +509,7 @@ export class SdCliClientBuilder extends EventEmitter {
430
509
  {
431
510
  loader: "@angular-devkit/build-angular/src/babel/webpack-loader",
432
511
  options: {
433
- cacheDirectory: path.resolve(cachePath, "babel-webpack"),
512
+ cacheDirectory: path.resolve(cacheBasePath, "babel-webpack"),
434
513
  scriptTarget: ts.ScriptTarget.ES2017,
435
514
  aot: true,
436
515
  optimize: !watch,
@@ -504,7 +583,9 @@ export class SdCliClientBuilder extends EventEmitter {
504
583
  ]
505
584
  },
506
585
  plugins: [
507
- new NodePolyfillPlugin(),
586
+ new NodePolyfillPlugin({
587
+ excludeAliases: builderType === "electron" ? ["process"] : []
588
+ }),
508
589
  new NamedChunksPlugin(),
509
590
  new DedupeModuleResolvePlugin(),
510
591
  new webpack.ProgressPlugin({
@@ -518,7 +599,6 @@ export class SdCliClientBuilder extends EventEmitter {
518
599
  }
519
600
  }
520
601
  }),
521
- new CommonJsUsageWarnPlugin(),
522
602
  ...watch ? [] : [
523
603
  new LicenseWebpackPlugin({
524
604
  stats: { warnings: false, errors: false },
@@ -529,7 +609,7 @@ export class SdCliClientBuilder extends EventEmitter {
529
609
  ],
530
610
  new CopyWebpackPlugin({
531
611
  patterns: [
532
- ...["favicon.ico", "assets/", "manifest.webmanifest"].map((item) => ({
612
+ ...["favicon.ico", "assets/", "manifest.json"].map((item) => ({
533
613
  context: this._rootPath,
534
614
  to: item,
535
615
  from: `src/${item}`,
@@ -546,11 +626,31 @@ export class SdCliClientBuilder extends EventEmitter {
546
626
  },
547
627
  priority: 0
548
628
  })),
549
- ...this._cordova ? this._cordova.platforms.map((platform) => ({
550
- context: this._cordova!.cordovaPath,
551
- to: `cordova-${platform}`,
552
- from: `platforms/${platform}/platform_www`
553
- })) : []
629
+ ...builderType === "cordova" && watch ? this._cordova!.platforms.mapMany((platform) => [
630
+ {
631
+ context: this._cordova!.cordovaPath,
632
+ to: `cordova-${platform}/plugins`,
633
+ from: `platforms/${platform}/platform_www/plugins`,
634
+ noErrorOnMissing: true
635
+ },
636
+ {
637
+ context: this._cordova!.cordovaPath,
638
+ to: `cordova-${platform}/cordova.js`,
639
+ from: `platforms/${platform}/platform_www/cordova.js`
640
+ },
641
+ {
642
+ context: this._cordova!.cordovaPath,
643
+ to: `cordova-${platform}/cordova_plugins.js`,
644
+ from: `platforms/${platform}/platform_www/cordova_plugins.js`,
645
+ noErrorOnMissing: true
646
+ },
647
+ {
648
+ context: this._cordova!.cordovaPath,
649
+ to: `cordova-${platform}/config.xml`,
650
+ from: `platforms/${platform}/www/config.xml`,
651
+ noErrorOnMissing: true
652
+ }
653
+ ]) : []
554
654
  ]
555
655
  }),
556
656
  ...watch ? [
@@ -603,7 +703,7 @@ export class SdCliClientBuilder extends EventEmitter {
603
703
  cache: {
604
704
  enabled: true,
605
705
  basePath: cacheBasePath,
606
- path: cachePath
706
+ path: path.resolve(cacheBasePath, "index-webpack")
607
707
  },
608
708
  postTransform: undefined,
609
709
  optimization: {
@@ -25,15 +25,15 @@ export class SdCliJsLibBuilder extends EventEmitter {
25
25
 
26
26
  const relatedPaths = await this.getRelatedPathsAsync();
27
27
  const watcher = SdFsWatcher.watch(relatedPaths);
28
- watcher.onChange({}, async (changeInfos) => {
29
- const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
28
+ watcher.onChange({}, async (changedInfos) => {
29
+ const changeFilePaths = changedInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
30
30
  if (changeFilePaths.length === 0) return;
31
31
 
32
32
  this._logger.debug("파일 변경 감지");
33
33
  this.emit("change");
34
34
  const watchBuildResults: ISdCliPackageBuildResult[] = [];
35
35
 
36
- const lintFilePaths = changeInfos.filter((item) => ["add", "change"].includes(item.event)).map((item) => item.path);
36
+ const lintFilePaths = changedInfos.filter((item) => ["add", "change"].includes(item.event)).map((item) => item.path);
37
37
  if (lintFilePaths.length > 0) {
38
38
  watchBuildResults.push(...await this._linter.lintAsync(lintFilePaths));
39
39
  }
@@ -187,11 +187,12 @@ export class SdCliServerBuilder extends EventEmitter {
187
187
 
188
188
  const npmConfig = this._getNpmConfig(this._rootPath)!;
189
189
  const pkgKey = npmConfig.name.split("/").last()!;
190
- const pkgVersion = npmConfig.version;
190
+ // const pkgVersion = npmConfig.version;
191
191
 
192
- const cacheBasePath = path.resolve(this._rootPath, ".cache");
193
- const cachePath = path.resolve(cacheBasePath, pkgVersion);
192
+ const workspacePkgLockContent = FsUtil.readFile(path.resolve(this._workspaceRootPath, "package-lock.json"));
194
193
 
194
+ const cacheBasePath = path.resolve(this._rootPath, ".cache");
195
+ // const cachePath = path.resolve(cacheBasePath, pkgVersion);
195
196
 
196
197
  let prevProgressMessage = "";
197
198
  return {
@@ -241,13 +242,11 @@ export class SdCliServerBuilder extends EventEmitter {
241
242
  cache: {
242
243
  type: "filesystem",
243
244
  profile: watch ? undefined : false,
244
- cacheDirectory: path.resolve(cachePath, "server-webpack"),
245
+ cacheDirectory: path.resolve(cacheBasePath, "server-webpack"),
245
246
  maxMemoryGenerations: 1,
246
247
  name: createHash("sha1")
247
- .update(pkgVersion)
248
+ .update(workspacePkgLockContent)
248
249
  .update(JSON.stringify(this._parsedTsconfig.options))
249
- .update(this._workspaceRootPath)
250
- .update(this._rootPath)
251
250
  .update(JSON.stringify(this._config))
252
251
  .update(watch.toString())
253
252
  .digest("hex")
@@ -115,8 +115,8 @@ export class SdCliTsLibBuilder extends EventEmitter {
115
115
  this._logger.debug("변경감지 구성...");
116
116
  const relatedPaths = await this.getAllRelatedPathsAsync();
117
117
  const watcher = SdFsWatcher.watch(relatedPaths);
118
- watcher.onChange({}, async (changeInfos) => {
119
- const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
118
+ watcher.onChange({}, async (changedInfos) => {
119
+ const changeFilePaths = changedInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
120
120
  if (changeFilePaths.length === 0) return;
121
121
 
122
122
  this._logger.debug("파일 변경 감지", changeFilePaths);
@@ -153,7 +153,7 @@ export class SdCliTsLibBuilder extends EventEmitter {
153
153
  this._logger.debug("린트...");
154
154
  const lintFilePaths = [
155
155
  ...watchBuildPack.affectedSourceFiles.map((item) => item.fileName),
156
- ...changeInfos.filter((item) => ["add", "change"].includes(item.event)).map((item) => item.path)
156
+ ...changedInfos.filter((item) => ["add", "change"].includes(item.event)).map((item) => item.path)
157
157
  ];
158
158
  if (lintFilePaths.length > 0) {
159
159
  watchBuildResults.push(...await this._linter.lintAsync(lintFilePaths, watchBuildPack.program));
package/src/commons.ts CHANGED
@@ -1,6 +1,14 @@
1
1
  export interface INpmConfig {
2
2
  name: string;
3
3
  version: string;
4
+ description?: string;
5
+ author?: string;
6
+ license?: string;
7
+ repository: string | {
8
+ type: string;
9
+ url: string;
10
+ directory?: string;
11
+ };
4
12
  type?: "module";
5
13
  workspaces?: string[];
6
14
  main?: string;
@@ -63,14 +71,18 @@ export interface ISdCliServerPackageConfig {
63
71
 
64
72
  export interface ISdCliClientPackageConfig {
65
73
  type: "client";
66
- cordova?: ISdCliClientPackageCordovaConfig;
67
- server: string;
74
+ builder?: {
75
+ web?: ISdCliClientBuilderWebConfig;
76
+ cordova?: ISdCliClientBuilderCordovaConfig;
77
+ electron?: ISdCliClientBuilderElectronConfig;
78
+ };
79
+ server: string | { port: number };
68
80
  env?: Record<string, string>;
69
81
  configs?: Record<string, any>;
70
82
  publish?: TSdCliPublishConfig;
71
83
  }
72
84
 
73
- export type TSdCliPublishConfig = ISdCliFtpPublishConfig | ISdCliLocalDirectoryPublishConfig;
85
+ export type TSdCliPublishConfig = ISdCliFtpPublishConfig | ISdCliLocalDirectoryPublishConfig | ISdCliGithubPublishConfig;
74
86
 
75
87
  export interface ISdCliFtpPublishConfig {
76
88
  type: "ftp" | "ftps" | "sftp";
@@ -86,17 +98,26 @@ export interface ISdCliLocalDirectoryPublishConfig {
86
98
  path: string;
87
99
  }
88
100
 
89
- export interface ISdCliClientPackageCordovaConfig {
90
- platforms: TSdCliCordovaPlatform[];
101
+ export interface ISdCliGithubPublishConfig {
102
+ type: "github";
103
+ apiKey: string;
104
+ files: { from: string; to: string }[];
105
+ }
106
+
107
+ export interface ISdCliClientBuilderWebConfig {
108
+ }
109
+
110
+ export interface ISdCliClientBuilderCordovaConfig {
91
111
  appId: string;
92
112
  appName: string;
93
113
  plugins?: string[];
94
114
  icon?: string;
95
- buildOption?: {
96
- debug?: boolean;
97
- bundle?: boolean;
98
- sign?: {
99
- android?: {
115
+ debug?: boolean;
116
+ target?: {
117
+ browser?: {};
118
+ android?: {
119
+ bundle?: boolean;
120
+ sign?: {
100
121
  keystore: string;
101
122
  storePassword: string;
102
123
  alias: string;
@@ -107,4 +128,7 @@ export interface ISdCliClientPackageCordovaConfig {
107
128
  };
108
129
  }
109
130
 
110
- export type TSdCliCordovaPlatform = "browser" | "ios" | "android" | "electron";
131
+ export interface ISdCliClientBuilderElectronConfig {
132
+ appId: string;
133
+ icon?: string;
134
+ }
@@ -42,8 +42,8 @@ export class SdCliLocalUpdate {
42
42
  const watchPaths = (await updatePathInfos.mapManyAsync(async (item) => await this._getWatchPathsAsync(item.source))).distinct();
43
43
 
44
44
  const watcher = SdFsWatcher.watch(watchPaths);
45
- watcher.onChange({ delay: 1000 }, async (changeInfos) => {
46
- const changeFilePaths = changeInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
45
+ watcher.onChange({ delay: 1000 }, async (changedInfos) => {
46
+ const changeFilePaths = changedInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
47
47
  if (changeFilePaths.length === 0) return;
48
48
 
49
49
  this._logger.log("로컬 라이브러리 변경감지...");
@@ -80,9 +80,33 @@ export class SdCliWorkspace {
80
80
  if (pkg.config.type === "client") {
81
81
  const middlewares = (await pkg.watchAsync()) as NextHandleFunction[];
82
82
 
83
- const serverInfo = this._serverInfoMap.getOrCreate(pkg.config.server, { middlewares: [], clientInfos: [] });
84
- serverInfo.middlewares.push(...middlewares);
85
- serverInfo.clientInfos.push({ pkgKey: pkg.name.split("/").last()! });
83
+ if (typeof pkg.config.server === "string") {
84
+ const serverInfo = this._serverInfoMap.getOrCreate(pkg.config.server, { middlewares: [], clientInfos: [] });
85
+ serverInfo.middlewares.push(...middlewares);
86
+ serverInfo.clientInfos.push({
87
+ pkgKey: pkg.name.split("/").last()!,
88
+ platforms: pkg.config.builder ? Object.keys(pkg.config.builder) : ["web"],
89
+ cordovaTargets: pkg.config.builder?.cordova?.target ? Object.keys(pkg.config.builder.cordova.target) : ["browser"]
90
+ });
91
+ }
92
+ else { // DEV SERVER
93
+ const serverInfo = this._serverInfoMap.getOrCreate("_", { middlewares: [], clientInfos: [] });
94
+ if (serverInfo.server === undefined) {
95
+ const server = new SdServiceServer({
96
+ rootPath: process.cwd(),
97
+ services: [],
98
+ port: pkg.config.server.port
99
+ });
100
+ await server.listenAsync();
101
+ serverInfo.server = server;
102
+ serverInfo.server.devMiddlewares = middlewares;
103
+ serverInfo.clientInfos.push({
104
+ pkgKey: pkg.name.split("/").last()!,
105
+ platforms: pkg.config.builder ? Object.keys(pkg.config.builder) : ["web"],
106
+ cordovaTargets: pkg.config.builder?.cordova?.target ? Object.keys(pkg.config.builder.cordova.target) : ["browser"]
107
+ });
108
+ }
109
+ }
86
110
  }
87
111
  else {
88
112
  await pkg.watchAsync();
@@ -345,11 +369,28 @@ export class SdCliWorkspace {
345
369
  const portStr = serverInfo.server.options.port.toString();
346
370
 
347
371
  for (const clientInfo of serverInfo.clientInfos) {
348
- clientHrefs.push(`${protocolStr}://localhost:${portStr}/${clientInfo.pkgKey}/`);
372
+ for (const platform of clientInfo.platforms) {
373
+ if (platform === "web") {
374
+ clientHrefs.push(`${protocolStr}://localhost:${portStr}/${clientInfo.pkgKey}/`);
375
+ }
376
+ else if (platform === "electron") {
377
+ clientHrefs.push(`sd-cli run-electron ${clientInfo.pkgKey} http://localhost:${portStr}`);
378
+ }
379
+ else if (platform === "cordova") {
380
+ for (const target of clientInfo.cordovaTargets) {
381
+ if (target === "browser") {
382
+ clientHrefs.push(`${protocolStr}://localhost:${portStr}/${clientInfo.pkgKey}/${platform}/`);
383
+ }
384
+ else {
385
+ clientHrefs.push(`sd-cli run-cordova ${clientInfo.pkgKey} http://[IP]:${portStr}`);
386
+ }
387
+ }
388
+ }
389
+ }
349
390
  }
350
391
  }
351
392
  if (clientHrefs.length > 0) {
352
- this._logger.log(`오픈된 클라이언트: ${clientHrefs.join(", ")}`);
393
+ this._logger.log(`오픈된 클라이언트:\n${clientHrefs.join("\n")}`);
353
394
  }
354
395
  }
355
396
  }
@@ -357,5 +398,5 @@ export class SdCliWorkspace {
357
398
  interface IServerInfo {
358
399
  server?: SdServiceServer;
359
400
  middlewares: NextHandleFunction[];
360
- clientInfos: { pkgKey: string }[];
401
+ clientInfos: { pkgKey: string; platforms: string[]; cordovaTargets: string[] }[];
361
402
  }
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from "./build-tool/SdCliCacheCompilerHost";
2
2
  export * from "./build-tool/SdCliCordova";
3
+ export * from "./build-tool/SdCliElectron";
4
+ export * from "./build-tool/SdCliGithubApi";
3
5
  export * from "./build-tool/SdCliIndexFileGenerator";
4
6
  export * from "./build-tool/SdCliNgCacheCompilerHost";
5
7
  export * from "./build-tool/SdCliPackageLinter";