@simplysm/sd-cli 10.0.45 → 10.0.49

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.
@@ -17,7 +17,7 @@ export class SdCliElectron {
17
17
 
18
18
  logger.log("설정 가져오기...");
19
19
  const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(true, opt.optNames) as TSdCliConfig;
20
- const pkgConf = projConf[opt.pkgName];
20
+ const pkgConf = projConf.packages[opt.pkgName];
21
21
  if (pkgConf?.type !== "client" || pkgConf.builder?.electron === undefined) {
22
22
  throw new Error();
23
23
  }
@@ -67,7 +67,7 @@ export class SdCliElectron {
67
67
 
68
68
  logger.log("설정 가져오기...");
69
69
  const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(true, opt.optNames) as TSdCliConfig;
70
- const pkgConf = projConf[opt.pkgName];
70
+ const pkgConf = projConf.packages[opt.pkgName];
71
71
  if (pkgConf?.type !== "client" || pkgConf.builder?.electron === undefined) {
72
72
  throw new Error();
73
73
  }
@@ -0,0 +1,127 @@
1
+ import {FsUtil, Logger, PathUtil, SdFsWatcher} from "@simplysm/sd-core-node";
2
+ import path from "path";
3
+ import {pathToFileURL} from "url";
4
+ import {TSdCliConfig} from "../commons";
5
+
6
+ export class SdCliLocalUpdate {
7
+ public static async runAsync(opt: {
8
+ confFileRelPath: string;
9
+ optNames: string[];
10
+ }): Promise<void> {
11
+ const logger = Logger.get(["simplysm", "sd-cli", "SdCliLocalUpdate", "runAsync"]);
12
+
13
+ logger.debug("프로젝트 설정 가져오기...");
14
+ const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(true, opt.optNames) as TSdCliConfig;
15
+ if (!projConf.localUpdates) return;
16
+
17
+ const updatePathInfos = await this._getUpdatePathInfosAsync(projConf.localUpdates);
18
+ logger.debug("로컬 업데이트 구성", updatePathInfos);
19
+
20
+ logger.log("로컬 라이브러리 업데이트 시작...");
21
+ for (const updatePathInfo of updatePathInfos) {
22
+ if (!FsUtil.exists(updatePathInfo.source)) {
23
+ logger.warn(`소스경로를 찾을 수 없어 무시됩니다(${updatePathInfo.source})`);
24
+ return;
25
+ }
26
+
27
+ // 소스경로에서 대상경로로 파일 복사
28
+ await FsUtil.copyAsync(updatePathInfo.source, updatePathInfo.target, (src) => {
29
+ return !src.includes("node_modules") && !src.endsWith("package.json");
30
+ });
31
+ }
32
+ logger.info("로컬 라이브러리 업데이트 완료");
33
+ }
34
+
35
+ public static async watchAsync(opt: {
36
+ confFileRelPath: string;
37
+ optNames: string[];
38
+ }): Promise<void> {
39
+ const logger = Logger.get(["simplysm", "sd-cli", "SdCliLocalUpdate", "watchAsync"]);
40
+
41
+ logger.debug("프로젝트 설정 가져오기...");
42
+ const projConf = (await import(pathToFileURL(path.resolve(process.cwd(), opt.confFileRelPath)).href)).default(true, opt.optNames) as TSdCliConfig;
43
+ if (!projConf.localUpdates) return;
44
+
45
+ const updatePathInfos = await this._getUpdatePathInfosAsync(projConf.localUpdates);
46
+ logger.debug("로컬 업데이트 구성");
47
+
48
+ const watchPaths = (await updatePathInfos.mapManyAsync(async (item) => await this._getWatchPathsAsync(item.source))).distinct();
49
+
50
+ const watcher = SdFsWatcher.watch(watchPaths);
51
+ watcher.onChange({delay: 1000}, async (changedInfos) => {
52
+ const changeFilePaths = changedInfos.filter((item) => ["add", "change", "unlink"].includes(item.event)).map((item) => item.path);
53
+ if (changeFilePaths.length === 0) return;
54
+
55
+ logger.log("로컬 라이브러리 변경감지...");
56
+ for (const changedFilePath of changeFilePaths) {
57
+ if (!FsUtil.exists(changedFilePath)) continue;
58
+
59
+ for (const updatePathInfo of updatePathInfos) {
60
+ if (!PathUtil.isChildPath(changedFilePath, updatePathInfo.source)) continue;
61
+
62
+ const sourceRelPath = path.relative(updatePathInfo.source, changedFilePath);
63
+ if (sourceRelPath.includes("node_modules")) continue;
64
+ if (sourceRelPath.includes("package.json")) continue;
65
+
66
+ const targetFilePath = path.resolve(updatePathInfo.target, sourceRelPath);
67
+
68
+ logger.debug(`변경파일감지(복사): ${changedFilePath} => ${targetFilePath}`);
69
+ await FsUtil.copyAsync(changedFilePath, targetFilePath);
70
+ }
71
+ }
72
+
73
+ const watchWatchPaths = (await updatePathInfos.mapManyAsync(async (item) => await this._getWatchPathsAsync(item.source))).distinct();
74
+ watcher.add(watchWatchPaths);
75
+
76
+ logger.info("로컬 라이브러리 복사 완료");
77
+ });
78
+ }
79
+
80
+ private static async _getUpdatePathInfosAsync(record: Record<string, string>): Promise<IUpdatePathInfo[]> {
81
+ const result: IUpdatePathInfo[] = [];
82
+ for (const pkgGlobPath of Object.keys(record)) {
83
+ // "node_modules'에서 로컬업데이트 설정에 맞는 패키지를 "glob"하여 대상 패키지경로 목록 가져오기
84
+ const targetPaths = [
85
+ ...await FsUtil.globAsync(path.resolve(process.cwd(), "node_modules", pkgGlobPath)),
86
+ ...await FsUtil.globAsync(path.resolve(process.cwd(), "packages", "*", "node_modules", pkgGlobPath))
87
+ ];
88
+
89
+ result.push(
90
+ ...targetPaths
91
+ .map((targetPath) => {
92
+ // 대상의 명칭 추출
93
+ const regexpText = pkgGlobPath.replace(/[\\/.*]/g, (item) => (
94
+ item === "/" ? "[\\\\\\/]"
95
+ : item === "." ? "\\."
96
+ : item === "*" ? "(.*)"
97
+ : item
98
+ ));
99
+ const targetNameMatch = new RegExp(regexpText).exec(targetPath);
100
+ if (!targetNameMatch || typeof targetNameMatch[1] === "undefined") return undefined;
101
+ const targetName = targetNameMatch[1];
102
+
103
+ // 가져올 소스 경로 추출
104
+ const sourcePath = path.resolve(record[pkgGlobPath].replace(/\*/g, targetName));
105
+ return {source: sourcePath, target: targetPath};
106
+ })
107
+ .filterExists()
108
+ );
109
+ }
110
+
111
+ return result;
112
+ }
113
+
114
+ private static async _getWatchPathsAsync(sourcePath: string): Promise<string[]> {
115
+ return await FsUtil.globAsync(path.resolve(sourcePath, "**"), {
116
+ ignore: [
117
+ "**/node_modules/**",
118
+ "**/package.json"
119
+ ]
120
+ });
121
+ }
122
+ }
123
+
124
+ interface IUpdatePathInfo {
125
+ source: string;
126
+ target: string;
127
+ }
@@ -5,6 +5,7 @@ import {
5
5
  ISdCliBuildClusterReqMessage,
6
6
  ISdCliBuildClusterResMessage,
7
7
  ISdCliPackageBuildResult,
8
+ ISdCliServerPackageConfig,
8
9
  TSdCliConfig,
9
10
  TSdCliPackageConfig
10
11
  } from "../commons";
@@ -36,20 +37,20 @@ export class SdCliProject {
36
37
  throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
37
38
  }
38
39
  const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
39
- let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf);
40
+ let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
40
41
  if (opt.pkgNames.length !== 0) {
41
42
  pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
42
43
  }
43
44
 
44
45
  logger.debug("패키지 존재 확인...");
45
- const notExistsPkgs = Object.keys(projConf).filter((pkgConfKey) => allPkgPaths.every((pkgPath) => path.basename(pkgPath) !== pkgConfKey));
46
+ const notExistsPkgs = Object.keys(projConf.packages).filter((pkgConfKey) => allPkgPaths.every((pkgPath) => path.basename(pkgPath) !== pkgConfKey));
46
47
  if (notExistsPkgs.length > 0) {
47
48
  throw new Error("패키지를 찾을 수 없습니다. (" + notExistsPkgs.join(", ") + ")");
48
49
  }
49
50
 
50
51
  logger.debug("라이브러리 INDEX 파일 생성...");
51
52
  await pkgPaths.parallelAsync(async (pkgPath) => {
52
- const pkgConf = projConf[path.basename(pkgPath)]!;
53
+ const pkgConf = projConf.packages[path.basename(pkgPath)]!;
53
54
  if (pkgConf.type === "library" && FsUtil.exists(path.resolve(pkgPath, "tsconfig.json"))) {
54
55
  await SdCliIndexFileGenerator.watchAsync(pkgPath, pkgConf.polyfills);
55
56
  }
@@ -131,7 +132,11 @@ export class SdCliProject {
131
132
  if (serverInfo.pkgPath !== undefined && serverInfo.hasChanges) {
132
133
  logger.debug("서버 재시작...");
133
134
  try {
134
- const restartServerResult = await this._restartServerAsync(serverInfo.pkgPath, serverInfo.worker);
135
+ const restartServerResult = await this._restartServerAsync(
136
+ serverInfo.pkgPath,
137
+ projConf.packages[path.basename(serverInfo.pkgPath)] as ISdCliServerPackageConfig,
138
+ serverInfo.worker
139
+ );
135
140
  serverInfo.worker = restartServerResult.worker;
136
141
  serverInfo.port = restartServerResult.port;
137
142
  serverInfo.hasChanges = false;
@@ -177,15 +182,15 @@ export class SdCliProject {
177
182
  logger.log("빌드를 시작합니다...");
178
183
 
179
184
  await pkgPaths.parallelAsync(async (pkgPath) => {
180
- const pkgConf = projConf[path.basename(pkgPath)]!;
185
+ const pkgConf = projConf.packages[path.basename(pkgPath)]!;
181
186
  if (pkgConf.type === "client") {
182
187
  const builderKeys = Object.keys(pkgConf.builder ?? {web: {}});
183
188
  await builderKeys.parallelAsync(async (builderKey) => {
184
- await this._runCommandAsync(cluster, "watch", pkgPath, projConf[path.basename(pkgPath)]!, opt.withLint, builderKey);
189
+ await this._runCommandAsync(cluster, "watch", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint, builderKey);
185
190
  });
186
191
  }
187
192
  else {
188
- await this._runCommandAsync(cluster, "watch", pkgPath, projConf[path.basename(pkgPath)]!, opt.withLint);
193
+ await this._runCommandAsync(cluster, "watch", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint);
189
194
  }
190
195
  });
191
196
 
@@ -215,7 +220,7 @@ export class SdCliProject {
215
220
  throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
216
221
  }
217
222
  const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
218
- let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf);
223
+ let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
219
224
  if (opt.pkgNames.length !== 0) {
220
225
  pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
221
226
  }
@@ -225,7 +230,7 @@ export class SdCliProject {
225
230
 
226
231
  logger.debug("라이브러리 INDEX 파일 생성...");
227
232
  await pkgPaths.parallelAsync(async (pkgPath) => {
228
- const pkgConf = projConf[path.basename(pkgPath)]!;
233
+ const pkgConf = projConf.packages[path.basename(pkgPath)]!;
229
234
  if (pkgConf.type === "library" && FsUtil.exists(path.resolve(pkgPath, "tsconfig.json"))) {
230
235
  await SdCliIndexFileGenerator.runAsync(pkgPath, pkgConf.polyfills);
231
236
  }
@@ -237,15 +242,15 @@ export class SdCliProject {
237
242
  logger.debug("빌드 프로세스 명령 전달...");
238
243
  const results = (
239
244
  await pkgPaths.parallelAsync(async (pkgPath) => {
240
- const pkgConf = projConf[path.basename(pkgPath)]!;
245
+ const pkgConf = projConf.packages[path.basename(pkgPath)]!;
241
246
  if (pkgConf.type === "client") {
242
247
  const builderKeys = Object.keys(pkgConf.builder ?? {web: {}});
243
248
  return (await builderKeys.parallelAsync(async (builderKey) => {
244
- return await this._runCommandAsync(cluster, "build", pkgPath, projConf[path.basename(pkgPath)]!, opt.withLint, builderKey);
249
+ return await this._runCommandAsync(cluster, "build", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint, builderKey);
245
250
  })).mapMany();
246
251
  }
247
252
  else {
248
- return await this._runCommandAsync(cluster, "build", pkgPath, projConf[path.basename(pkgPath)]!, opt.withLint);
253
+ return await this._runCommandAsync(cluster, "build", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint);
249
254
  }
250
255
  })
251
256
  ).mapMany();
@@ -290,7 +295,7 @@ export class SdCliProject {
290
295
  throw new Error("프로젝트 package.json에 workspaces가 설정되어있지 않습니다.");
291
296
  }
292
297
  const allPkgPaths = await projNpmConf.workspaces.mapManyAsync(async (item) => await FsUtil.globAsync(item));
293
- let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf);
298
+ let pkgPaths = allPkgPaths.filter((pkgPath) => path.basename(pkgPath) in projConf.packages);
294
299
  if (opt.pkgNames.length !== 0) {
295
300
  pkgPaths = pkgPaths.filter((pkgPath) => opt.pkgNames.includes(path.basename(pkgPath)));
296
301
  }
@@ -302,7 +307,7 @@ export class SdCliProject {
302
307
  if (!opt.noBuild) {
303
308
  logger.debug("라이브러리 INDEX 파일 생성...");
304
309
  await pkgPaths.parallelAsync(async (pkgPath) => {
305
- const pkgConf = projConf[path.basename(pkgPath)]!;
310
+ const pkgConf = projConf.packages[path.basename(pkgPath)]!;
306
311
  if (pkgConf.type === "library" && FsUtil.exists(path.resolve(pkgPath, "tsconfig.json"))) {
307
312
  await SdCliIndexFileGenerator.runAsync(pkgPath, pkgConf.polyfills);
308
313
  }
@@ -314,15 +319,15 @@ export class SdCliProject {
314
319
  logger.debug("빌드 프로세스 명령 전달...");
315
320
  const results = (
316
321
  await pkgPaths.parallelAsync(async (pkgPath) => {
317
- const pkgConf = projConf[path.basename(pkgPath)]!;
322
+ const pkgConf = projConf.packages[path.basename(pkgPath)]!;
318
323
  if (pkgConf.type === "client") {
319
324
  const builderKeys = Object.keys(pkgConf.builder ?? {web: {}});
320
325
  return (await builderKeys.parallelAsync(async (builderKey) => {
321
- return await this._runCommandAsync(cluster, "build", pkgPath, projConf[path.basename(pkgPath)]!, opt.withLint, builderKey);
326
+ return await this._runCommandAsync(cluster, "build", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint, builderKey);
322
327
  })).mapMany();
323
328
  }
324
329
  else {
325
- return await this._runCommandAsync(cluster, "build", pkgPath, projConf[path.basename(pkgPath)]!, opt.withLint);
330
+ return await this._runCommandAsync(cluster, "build", pkgPath, projConf.packages[path.basename(pkgPath)]!, opt.withLint);
326
331
  }
327
332
  })
328
333
  ).mapMany();
@@ -348,7 +353,7 @@ export class SdCliProject {
348
353
  logger.debug("배포 시작...");
349
354
  await pkgPaths.parallelAsync(async (pkgPath) => {
350
355
  const pkgName = path.basename(pkgPath);
351
- const pkgConf = projConf[pkgName];
356
+ const pkgConf = projConf.packages[pkgName];
352
357
  if (pkgConf?.publish == null) return;
353
358
 
354
359
  logger.debug(`[${pkgName}] 배포 시작...`);
@@ -545,7 +550,7 @@ export class SdCliProject {
545
550
  cluster.kill("SIGKILL");
546
551
  }
547
552
 
548
- private static async _restartServerAsync(pkgPath: string, prevServerProcess?: cp.ChildProcess): Promise<{
553
+ private static async _restartServerAsync(pkgPath: string, pkgConf: ISdCliServerPackageConfig, prevServerProcess?: cp.ChildProcess): Promise<{
549
554
  worker: cp.ChildProcess,
550
555
  port: number
551
556
  }> {
@@ -555,6 +560,8 @@ export class SdCliProject {
555
560
  prevServerProcess.kill("SIGKILL");
556
561
  }
557
562
 
563
+ const npmConf = (await FsUtil.readJsonAsync(path.resolve(pkgPath, "package.json"))) as INpmConfig;
564
+
558
565
  return await new Promise<{
559
566
  worker: cp.ChildProcess,
560
567
  port: number
@@ -564,7 +571,13 @@ export class SdCliProject {
564
571
  [pkgPath],
565
572
  {
566
573
  stdio: ["pipe", "pipe", "pipe", "ipc"],
567
- env: process.env
574
+ env: {
575
+ ...process.env,
576
+ NODE_ENV: "development",
577
+ TZ: "Asia/Seoul",
578
+ SD_VERSION: npmConf.version,
579
+ ...pkgConf.env
580
+ }
568
581
  }
569
582
  );
570
583
 
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from "./builders/SdCliServerBuilder";
7
7
  export * from "./builders/SdCliTsLibBuilder";
8
8
  export * from "./commons";
9
9
  export * from "./entry/SdCliElectron";
10
+ export * from "./entry/SdCliLocalUpdate";
10
11
  export * from "./entry/SdCliProject";
11
12
  export * from "./utils/getElectronReactExternals";
12
13
  export * from "./utils/SdCliBuildResultUtil";
package/src/sd-cli.ts CHANGED
@@ -6,6 +6,7 @@ import {SdCliProject} from "./entry/SdCliProject";
6
6
  import {Logger, LoggerSeverity} from "@simplysm/sd-core-node";
7
7
  import {EventEmitter} from "events";
8
8
  import {SdCliElectron} from "./entry/SdCliElectron";
9
+ import {SdCliLocalUpdate} from "./entry/SdCliLocalUpdate";
9
10
 
10
11
  Error.stackTraceLimit = Infinity;
11
12
  EventEmitter.defaultMaxListeners = 0;
@@ -21,6 +22,22 @@ const argv = (
21
22
  default: false
22
23
  }
23
24
  })
25
+ .command(
26
+ "local-update",
27
+ "로컬 라이브러리 업데이트를 수행합니다.",
28
+ (cmd) => cmd.version(false).hide("help").hide("debug")
29
+ .options({
30
+ config: {
31
+ string: true,
32
+ describe: "simplysm.cjs 파일 경로"
33
+ },
34
+ options: {
35
+ string: true,
36
+ array: true,
37
+ describe: "옵션 설정"
38
+ }
39
+ })
40
+ )
24
41
  .command(
25
42
  "watch",
26
43
  "프로젝트의 각 패키지에 대한 변경감지 빌드를 수행합니다.",
@@ -163,7 +180,13 @@ else {
163
180
  });
164
181
  }
165
182
 
166
- if (argv._[0] === "watch") {
183
+ if (argv._[0] === "local-update") {
184
+ await SdCliLocalUpdate.runAsync({
185
+ confFileRelPath: argv.config ?? "simplysm.cjs",
186
+ optNames: argv.options ?? [],
187
+ });
188
+ }
189
+ else if (argv._[0] === "watch") {
167
190
  await SdCliProject
168
191
  .watchAsync({
169
192
  confFileRelPath: argv.config ?? "simplysm.cjs",