@simplysm/sd-cli 6.0.19 → 6.0.27

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 (99) hide show
  1. package/.eslintrc.js +1 -2
  2. package/dist-node/bin/sd-cli.js +25 -8
  3. package/dist-node/bin/sd-cli.js.map +1 -1
  4. package/dist-node/build-tools/SdCliIndexFileGenerator.js +51 -0
  5. package/dist-node/build-tools/SdCliIndexFileGenerator.js.map +1 -0
  6. package/dist-node/build-tools/SdCliNgModuleFilesGenerator.js +621 -0
  7. package/dist-node/build-tools/SdCliNgModuleFilesGenerator.js.map +1 -0
  8. package/dist-node/build-tools/metadata/SdAotMetadataReader.js +270 -0
  9. package/dist-node/build-tools/metadata/SdAotMetadataReader.js.map +1 -0
  10. package/dist-node/build-tools/metadata/SdIvyMetadataReader.js +101 -0
  11. package/dist-node/build-tools/metadata/SdIvyMetadataReader.js.map +1 -0
  12. package/dist-node/build-tools/metadata/SdMyMetadataReader.js +298 -0
  13. package/dist-node/build-tools/metadata/SdMyMetadataReader.js.map +1 -0
  14. package/dist-node/{build-tools → builders}/SdCliJavascriptLinter.js +1 -1
  15. package/dist-node/builders/SdCliJavascriptLinter.js.map +1 -0
  16. package/dist-node/{build-tools → builders}/SdCliNgClientBuilder.js +176 -102
  17. package/dist-node/builders/SdCliNgClientBuilder.js.map +1 -0
  18. package/dist-node/builders/SdCliNgLibraryBuilder.js +238 -0
  19. package/dist-node/builders/SdCliNgLibraryBuilder.js.map +1 -0
  20. package/dist-node/{build-tools → builders}/SdCliPublisher.js +0 -0
  21. package/dist-node/builders/SdCliPublisher.js.map +1 -0
  22. package/dist-node/{build-tools → builders}/SdCliServerBuilder.js +8 -6
  23. package/dist-node/builders/SdCliServerBuilder.js.map +1 -0
  24. package/dist-node/builders/SdCliTypescriptBuilder.js +304 -0
  25. package/dist-node/builders/SdCliTypescriptBuilder.js.map +1 -0
  26. package/dist-node/entry-points/SdCliLocalUpdater.js +9 -8
  27. package/dist-node/entry-points/SdCliLocalUpdater.js.map +1 -1
  28. package/dist-node/entry-points/SdCliProject.js +11 -9
  29. package/dist-node/entry-points/SdCliProject.js.map +1 -1
  30. package/dist-node/packages/SdCliClientPackage.js +12 -2
  31. package/dist-node/packages/SdCliClientPackage.js.map +1 -1
  32. package/dist-node/packages/SdCliLibraryPackage.js +17 -9
  33. package/dist-node/packages/SdCliLibraryPackage.js.map +1 -1
  34. package/dist-node/packages/SdCliServerPackage.js +2 -2
  35. package/dist-node/packages/SdCliServerPackage.js.map +1 -1
  36. package/dist-node/utils/SdCliFileCrypto.js +76 -0
  37. package/dist-node/utils/SdCliFileCrypto.js.map +1 -0
  38. package/dist-node/utils/SdProjectConfigUtil.js +1 -1
  39. package/dist-node/utils/SdProjectConfigUtil.js.map +1 -1
  40. package/dist-node/utils/SdWebpackUtil.js +12 -3
  41. package/dist-node/utils/SdWebpackUtil.js.map +1 -1
  42. package/dist-node/workers/lib-build-worker.js +7 -6
  43. package/dist-node/workers/lib-build-worker.js.map +1 -1
  44. package/dist-types/build-tools/SdCliIndexFileGenerator.d.ts +12 -0
  45. package/dist-types/build-tools/SdCliNgModuleFilesGenerator.d.ts +42 -0
  46. package/dist-types/build-tools/metadata/SdAotMetadataReader.d.ts +57 -0
  47. package/dist-types/build-tools/metadata/SdIvyMetadataReader.d.ts +41 -0
  48. package/dist-types/build-tools/metadata/SdMyMetadataReader.d.ts +57 -0
  49. package/dist-types/{build-tools → builders}/SdCliJavascriptLinter.d.ts +0 -0
  50. package/dist-types/{build-tools → builders}/SdCliNgClientBuilder.d.ts +1 -1
  51. package/dist-types/builders/SdCliNgLibraryBuilder.d.ts +27 -0
  52. package/dist-types/{build-tools → builders}/SdCliPublisher.d.ts +0 -0
  53. package/dist-types/{build-tools → builders}/SdCliServerBuilder.d.ts +0 -0
  54. package/dist-types/builders/SdCliTypescriptBuilder.d.ts +46 -0
  55. package/dist-types/commons.d.ts +11 -3
  56. package/dist-types/packages/SdCliClientPackage.d.ts +1 -1
  57. package/dist-types/utils/SdCliFileCrypto.d.ts +7 -0
  58. package/dist-types/utils/SdWebpackUtil.d.ts +1 -0
  59. package/package.json +10 -7
  60. package/schema/schema.json +247 -0
  61. package/src/bin/sd-cli.ts +37 -14
  62. package/src/build-tools/SdCliIndexFileGenerator.ts +61 -0
  63. package/src/build-tools/SdCliNgModuleFilesGenerator.ts +837 -0
  64. package/src/build-tools/metadata/SdAotMetadataReader.ts +368 -0
  65. package/src/build-tools/metadata/SdIvyMetadataReader.ts +152 -0
  66. package/src/build-tools/metadata/SdMyMetadataReader.ts +379 -0
  67. package/src/{build-tools → builders}/SdCliJavascriptLinter.ts +1 -1
  68. package/src/{build-tools → builders}/SdCliNgClientBuilder.ts +209 -111
  69. package/src/builders/SdCliNgLibraryBuilder.ts +336 -0
  70. package/src/{build-tools → builders}/SdCliPublisher.ts +0 -0
  71. package/src/{build-tools → builders}/SdCliServerBuilder.ts +14 -8
  72. package/src/builders/SdCliTypescriptBuilder.ts +488 -0
  73. package/src/commons.ts +11 -3
  74. package/src/entry-points/SdCliLocalUpdater.ts +9 -8
  75. package/src/entry-points/SdCliProject.ts +12 -9
  76. package/src/packages/SdCliClientPackage.ts +14 -3
  77. package/src/packages/SdCliLibraryPackage.ts +19 -11
  78. package/src/packages/SdCliServerPackage.ts +2 -2
  79. package/src/utils/SdCliFileCrypto.ts +87 -0
  80. package/src/utils/SdCliUtils.ts +56 -0
  81. package/src/utils/SdProjectConfigUtil.ts +1 -1
  82. package/src/utils/SdWebpackUtil.ts +13 -3
  83. package/src/workers/lib-build-worker.ts +8 -7
  84. package/dist-node/build-tools/SdCliJavascriptLinter.js.map +0 -1
  85. package/dist-node/build-tools/SdCliNgClientBuilder.js.map +0 -1
  86. package/dist-node/build-tools/SdCliNgLibraryBuilder.js +0 -164
  87. package/dist-node/build-tools/SdCliNgLibraryBuilder.js.map +0 -1
  88. package/dist-node/build-tools/SdCliPublisher.js.map +0 -1
  89. package/dist-node/build-tools/SdCliServerBuilder.js.map +0 -1
  90. package/dist-node/build-tools/SdCliTypescriptBuilder.js +0 -316
  91. package/dist-node/build-tools/SdCliTypescriptBuilder.js.map +0 -1
  92. package/dist-node/build-tools/SdCliTypescriptIndexFileGenerator.js +0 -75
  93. package/dist-node/build-tools/SdCliTypescriptIndexFileGenerator.js.map +0 -1
  94. package/dist-types/build-tools/SdCliNgLibraryBuilder.d.ts +0 -23
  95. package/dist-types/build-tools/SdCliTypescriptBuilder.d.ts +0 -37
  96. package/dist-types/build-tools/SdCliTypescriptIndexFileGenerator.d.ts +0 -12
  97. package/src/build-tools/SdCliNgLibraryBuilder.ts +0 -225
  98. package/src/build-tools/SdCliTypescriptBuilder.ts +0 -424
  99. package/src/build-tools/SdCliTypescriptIndexFileGenerator.ts +0 -93
@@ -0,0 +1,488 @@
1
+ import * as ts from "typescript";
2
+ import { INpmConfig, ISdAutoIndexConfig, ISdPackageBuildResult, ITsconfig } from "../commons";
3
+ import { FsUtil, Logger, PathUtil, SdFsWatcher } from "@simplysm/sd-core-node";
4
+ import { NeverEntryError } from "@simplysm/sd-core-common";
5
+ import { EventEmitter } from "events";
6
+ import * as path from "path";
7
+ import { SdTsDiagnosticUtil } from "../utils/SdTsDiagnosticUtil";
8
+ import { ESLint } from "eslint";
9
+ import { createHash } from "crypto";
10
+ import { SdCliIndexFileGenerator } from "../build-tools/SdCliIndexFileGenerator";
11
+ import { ParserOptions } from "@typescript-eslint/types";
12
+
13
+ export class SdCliTypescriptBuilder extends EventEmitter {
14
+ protected readonly _logger: Logger;
15
+
16
+ protected _parsedTsconfig: ts.ParsedCommandLine;
17
+ public readonly npmConfig: INpmConfig;
18
+
19
+ protected _moduleResolutionCache?: ts.ModuleResolutionCache;
20
+ protected _cacheCompilerHost?: ts.CompilerHost;
21
+ protected _program?: ts.Program;
22
+ protected _builder?: ts.BuilderProgram | ts.EmitAndSemanticDiagnosticsBuilderProgram;
23
+ protected readonly _fileCache = new Map<string, ITsBuildFileCache>();
24
+
25
+ private readonly _indexFileGenerator?: SdCliIndexFileGenerator;
26
+
27
+ private _watcher?: SdFsWatcher;
28
+
29
+ public ignoreProcesses: string[];
30
+
31
+ public constructor(public rootPath: string,
32
+ public tsconfigFilePath: string,
33
+ ignoreProcesses: ("emit" | "check" | "lint" | "genIndex")[],
34
+ public autoIndexConfig: ISdAutoIndexConfig | undefined) {
35
+ super();
36
+ this.ignoreProcesses = ignoreProcesses;
37
+
38
+ const tsconfig: ITsconfig = FsUtil.readJson(this.tsconfigFilePath);
39
+ this._parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this.rootPath);
40
+
41
+ this.npmConfig = FsUtil.readJson(path.resolve(this.rootPath, "package.json"));
42
+ this._logger = Logger.get(["simplysm", "sd-cli", this.constructor.name, this.npmConfig.name]);
43
+
44
+ if (this.autoIndexConfig) {
45
+ this._indexFileGenerator = new SdCliIndexFileGenerator(this.rootPath, this.autoIndexConfig);
46
+ }
47
+ }
48
+
49
+ public on(event: "change", listener: () => void): this;
50
+ public on(event: "complete", listener: (results: ISdPackageBuildResult[]) => void): this;
51
+ public on(event: string | symbol, listener: (...args: any[]) => void): this {
52
+ return super.on(event, listener);
53
+ }
54
+
55
+ public async buildAsync(watch: boolean): Promise<void> {
56
+ this.emit("change");
57
+
58
+ // DIST 비우기
59
+ await FsUtil.removeAsync(this._parsedTsconfig.options.outDir!);
60
+ if (this._parsedTsconfig.options.declarationDir !== undefined) {
61
+ await FsUtil.removeAsync(this._parsedTsconfig.options.declarationDir);
62
+ }
63
+
64
+ // 프로그램 리로드
65
+ const buildResult: ISdPackageBuildResult[] = [];
66
+ const buildDirtyFilePaths = await this.reloadProgramAsync(watch);
67
+
68
+ // 자동생성할 소스파일들 생성 및 리로드 (index.ts등)
69
+ const buildGenAdditionalResult = await this.generateAdditionalFilesAsync(buildDirtyFilePaths, watch);
70
+ buildResult.push(...buildGenAdditionalResult.result);
71
+ buildDirtyFilePaths.push(...buildGenAdditionalResult.dirtyFilePaths);
72
+
73
+ // 빌드시, 빌드 수행하고 종료
74
+ if (!watch) {
75
+ const buildDoAllResults = await this.doAllAsync(buildDirtyFilePaths.distinct());
76
+ buildResult.push(...buildDoAllResults);
77
+ this.emit("complete", buildResult.distinct());
78
+ return;
79
+ }
80
+
81
+ // 변경감지용, 변경감지기 시작
82
+ const resultMap = new Map<string, ISdPackageBuildResult[]>();
83
+
84
+ const watchPaths = this.getWatchPaths();
85
+ this._watcher = new SdFsWatcher();
86
+ this._watcher
87
+ .onChange(async (changeInfos) => {
88
+ try {
89
+ // 변경파일 중, 유효한 파일 추출
90
+ const changedFilePaths = changeInfos
91
+ .map((item) => PathUtil.posix(item.filePath))
92
+ .filter((item) => path.basename(item).includes("."))
93
+ .distinct();
94
+ if (changedFilePaths.length === 0) return;
95
+
96
+ // 변경감지 이벤트 발생
97
+ this.emit("change");
98
+
99
+ // 변경파일에 대한 프로그램 리로드
100
+ const watchDirtyFilePaths = await this.reloadChangedProgramAsync(changedFilePaths, watch);
101
+ if (watchDirtyFilePaths.length === 0) {
102
+ this.emit("complete", Array.from(resultMap.values()).mapMany());
103
+ return;
104
+ }
105
+
106
+ // 변경파일에 대해 자동생성할 소스파일들 생성 및 리로드 (index.ts등)
107
+ const watchGenAdditionalResult = await this.generateAdditionalFilesAsync(watchDirtyFilePaths, watch);
108
+ watchDirtyFilePaths.push(...watchGenAdditionalResult.dirtyFilePaths);
109
+ watchDirtyFilePaths.distinctThis();
110
+
111
+ // 변경파일에 대한 빌드 및 린트
112
+ const watchDoAllResults = await this.doAllAsync(watchDirtyFilePaths);
113
+
114
+ // 결과 MAP에서 DIRTY FILE 삭제
115
+ resultMap.delete("undefined");
116
+ for (const dirtyFilePath of watchDirtyFilePaths) {
117
+ resultMap.delete(dirtyFilePath);
118
+ }
119
+
120
+ // 결과 MAP 구성
121
+ const result = [...watchGenAdditionalResult.result, ...watchDoAllResults].distinct();
122
+ for (const resultItem of result) {
123
+ const posixFilePath = resultItem.filePath !== undefined ? PathUtil.posix(resultItem.filePath) : "undefined";
124
+ const resultMapValue = resultMap.getOrCreate(posixFilePath, []);
125
+ resultMapValue.push(resultItem);
126
+ }
127
+
128
+ this.emit("complete", Array.from(resultMap.values()).mapMany());
129
+ }
130
+ catch (err) {
131
+ this.emit("complete", [{
132
+ filePath: undefined,
133
+ severity: "error",
134
+ message: err.stack ?? err.message
135
+ }]);
136
+ }
137
+ })
138
+ .watch(watchPaths);
139
+
140
+ // 변경감지용, 최초 빌드
141
+
142
+ // 빌드 및 린트
143
+ const buildDoAllResults = await this.doAllAsync(buildDirtyFilePaths.distinct());
144
+ buildResult.push(...buildDoAllResults);
145
+
146
+ // 결과 MAP 구성
147
+ buildResult.distinctThis();
148
+ for (const buildResultItem of buildResult) {
149
+ const posixFilePath = buildResultItem.filePath !== undefined ? PathUtil.posix(buildResultItem.filePath) : "undefined";
150
+ const resultMapValue = resultMap.getOrCreate(posixFilePath, []);
151
+ resultMapValue.push(buildResultItem);
152
+ resultMapValue.distinctThis();
153
+ }
154
+ this.emit("complete", Array.from(resultMap.values()).mapMany());
155
+ }
156
+
157
+ public async generateAdditionalFilesAsync(dirtyFilePaths: string[], watch: boolean): Promise<ITsGenResult> {
158
+ return await this._generateIndexFileAsync(watch);
159
+ }
160
+
161
+ protected async _generateIndexFileAsync(watch: boolean): Promise<ITsGenResult> {
162
+ this._logger.debug("index.ts 생성");
163
+
164
+ const result: ITsGenResult = { dirtyFilePaths: [], result: [] };
165
+
166
+ if (!this.ignoreProcesses.includes("genIndex") && this._indexFileGenerator && this.autoIndexConfig) {
167
+ const generateResult = await this._indexFileGenerator.generateAsync();
168
+ result.result.push(...generateResult.result);
169
+ if (generateResult.changed) {
170
+ result.dirtyFilePaths = await this.reloadChangedProgramAsync([this._indexFileGenerator.indexFilePath], watch);
171
+ }
172
+ }
173
+
174
+ this._logger.debug("index.ts 생성 결과", result);
175
+
176
+ return result;
177
+ }
178
+
179
+ protected _reloadWatchPaths(): void {
180
+ if (this._watcher) {
181
+ this._watcher.replaceWatchPaths(this.getWatchPaths());
182
+ }
183
+ }
184
+
185
+ public async doAllAsync(dirtyFilePaths: string[]): Promise<ISdPackageBuildResult[]> {
186
+ return (
187
+ await Promise.all([
188
+ this._runProgramAsync(dirtyFilePaths),
189
+ this._lintAsync(dirtyFilePaths)
190
+ ])
191
+ ).mapMany();
192
+ }
193
+
194
+ protected async _lintAsync(dirtyFilePaths: string[]): Promise<ISdPackageBuildResult[]> {
195
+ this._logger.debug("LINT");
196
+
197
+ const linter = new ESLint({
198
+ overrideConfig: {
199
+ overrides: [
200
+ {
201
+ files: ["*.ts"],
202
+ parserOptions: {
203
+ program: this._program
204
+ } as ParserOptions,
205
+ settings: {
206
+ "import/resolver": {
207
+ typescript: {
208
+ project: this.tsconfigFilePath
209
+ }
210
+ }
211
+ }
212
+ }
213
+ ]
214
+ }
215
+ });
216
+
217
+ const filePaths = await dirtyFilePaths
218
+ .filterAsync(async (item) => (
219
+ !item.includes("node_modules")
220
+ && PathUtil.isChildPath(item, this.rootPath)
221
+ && FsUtil.exists(item)
222
+ && !FsUtil.isDirectory(item)
223
+ && !await linter.isPathIgnored(item)
224
+ ));
225
+
226
+ const lintResults = await linter.lintFiles(filePaths);
227
+
228
+ const result = lintResults.mapMany((report) => (
229
+ report.messages.map((msg) => {
230
+ const severity: "warning" | "error" = msg.severity === 1 ? "warning" : "error";
231
+
232
+ return {
233
+ filePath: report.filePath,
234
+ severity,
235
+ message: `${report.filePath}(${msg.line}, ${msg.column}): ${msg.ruleId ?? ""}: ${severity} ${msg.message}`
236
+ };
237
+ })
238
+ ));
239
+
240
+ this._logger.debug("LINT 결과", result);
241
+
242
+ return result;
243
+ }
244
+
245
+ /*protected _getAllDependencies(filePath: string): string[] {
246
+ this._logger.debug("파일 의존성 확인", filePath);
247
+
248
+ const sourceFile = this._builder!.getSourceFile(filePath);
249
+ if (!sourceFile) {
250
+ this._logger.debug("파일 의존성 확인 결과", []);
251
+ return [];
252
+ }
253
+ const result = [...this._builder!.getAllDependencies(sourceFile)];
254
+
255
+ this._logger.debug("파일 의존성 확인 결과", result);
256
+ return result;
257
+ }*/
258
+
259
+ public getWatchPaths(): string[] {
260
+ const builder = this._builder as ts.EmitAndSemanticDiagnosticsBuilderProgram;
261
+
262
+ const result: string[] = [];
263
+ for (const sourceFile of builder.getSourceFiles()) {
264
+ // result.push(sourceFile.fileName, ...this._getAllDependencies(sourceFile.fileName));
265
+ result.push(sourceFile.fileName);
266
+ }
267
+ return result.map((item) => PathUtil.posix(path.dirname(item))).filter((item) => FsUtil.exists(item)).distinct();
268
+ }
269
+
270
+ /*protected _getRelativeFilePaths(filePath: string): string[] {
271
+ const builder = this._builder as ts.EmitAndSemanticDiagnosticsBuilderProgram;
272
+
273
+ const result: string[] = [];
274
+ for (const sourceFile of builder.getSourceFiles()) {
275
+ const dependencies = this._getAllDependencies(sourceFile.fileName);
276
+ if (dependencies.includes(filePath)) {
277
+ result.push(sourceFile.fileName);
278
+ }
279
+ }
280
+
281
+ return result;
282
+ }*/
283
+
284
+ protected _deleteFileCaches(filePaths: string[]): void {
285
+ for (const filePath of filePaths) {
286
+ this._fileCache.delete(filePath);
287
+ }
288
+ }
289
+
290
+ // eslint-disable-next-line @typescript-eslint/require-await
291
+ protected async _runProgramAsync(dirtyFilePaths: string[]): Promise<ISdPackageBuildResult[]> {
292
+ this._logger.debug("빌드");
293
+
294
+ if (!this._builder) throw new NeverEntryError();
295
+
296
+ const diagnostics: ts.Diagnostic[] = [];
297
+
298
+ if (!this.ignoreProcesses.includes("check")) {
299
+ diagnostics.push(
300
+ ...this._builder.getOptionsDiagnostics(),
301
+ ...this._builder.getGlobalDiagnostics()
302
+ );
303
+
304
+ for (const dirtyFilePath of dirtyFilePaths) {
305
+ const sourceFile = this._builder.getSourceFile(dirtyFilePath);
306
+ if (!sourceFile) continue;
307
+
308
+ if (sourceFile.isDeclarationFile) continue;
309
+
310
+ diagnostics.push(
311
+ ...this._builder.getSyntacticDiagnostics(sourceFile),
312
+ ...this._builder.getSemanticDiagnostics(sourceFile)
313
+ );
314
+ }
315
+ }
316
+
317
+ if (!this.ignoreProcesses.includes("emit")) {
318
+ for (const dirtyFilePath of dirtyFilePaths) {
319
+ const sourceFile = this._builder.getSourceFile(dirtyFilePath);
320
+ if (!sourceFile) continue;
321
+ if (sourceFile.isDeclarationFile) continue;
322
+
323
+ this._builder.emit(sourceFile);
324
+ }
325
+ }
326
+
327
+ const result = diagnostics.map((item) => SdTsDiagnosticUtil.convertDiagnosticsToResult(item)).filterExists();
328
+
329
+ this._logger.debug("빌드", result);
330
+
331
+ return result;
332
+ }
333
+
334
+ public async reloadChangedProgramAsync(changedFilePaths: string[], watch: boolean): Promise<string[]> {
335
+ this._logger.debug("변경 프로그램 리로드", changedFilePaths);
336
+
337
+ // 캐시 삭제
338
+ // const delCachePaths = changedFilePaths.mapMany((item) => [item, ...this._getRelativeFilePaths(item)]).distinct();
339
+ // this._deleteFileCaches(delCachePaths);
340
+ this._deleteFileCaches(changedFilePaths);
341
+
342
+ // 프로그램 리로드
343
+ const dirtyFilePaths = await this.reloadProgramAsync(watch);
344
+
345
+ // 변경사항 있을 경우, 변경감지중이면, 변경감지 경로 리로드
346
+ if (dirtyFilePaths.length > 0 && watch) {
347
+ this._reloadWatchPaths();
348
+ }
349
+
350
+ dirtyFilePaths.push(...changedFilePaths.filter((key) => this._fileCache.has(key)));
351
+ dirtyFilePaths.distinctThis();
352
+
353
+ this._logger.debug("변경 프로그램 리로드 결과", dirtyFilePaths);
354
+ return dirtyFilePaths;
355
+ }
356
+
357
+ public async reloadProgramAsync(watch: boolean): Promise<string[]> {
358
+ this._logger.debug("프로그램 리로드");
359
+
360
+ this._moduleResolutionCache = ts.createModuleResolutionCache(this.rootPath, (s) => s, this._parsedTsconfig.options);
361
+ this._cacheCompilerHost = await this._createCacheCompilerHostAsync(this._parsedTsconfig, this._moduleResolutionCache);
362
+
363
+ this._program = ts.createProgram(
364
+ this._parsedTsconfig.fileNames,
365
+ this._parsedTsconfig.options,
366
+ this._cacheCompilerHost,
367
+ this._program
368
+ );
369
+
370
+ const baseGetSourceFiles = this._program.getSourceFiles;
371
+ this._program.getSourceFiles = function (...parameters) {
372
+ const files: readonly (ts.SourceFile & { version?: string })[] = baseGetSourceFiles(...parameters);
373
+
374
+ for (const file of files) {
375
+ if (file.version === undefined) {
376
+ file.version = createHash("sha256").update(file.text).digest("hex");
377
+ }
378
+ }
379
+
380
+ return files;
381
+ };
382
+
383
+ if (watch) {
384
+ const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
385
+ this._program,
386
+ this._cacheCompilerHost,
387
+ this._builder as ts.EmitAndSemanticDiagnosticsBuilderProgram
388
+ );
389
+ this._builder = builder;
390
+
391
+ const affectedFilePaths: string[] = [];
392
+ while (true) {
393
+ const result = builder.getSemanticDiagnosticsOfNextAffectedFile();
394
+
395
+ if (!result) break;
396
+
397
+ if ("fileName" in result.affected) {
398
+ affectedFilePaths.push(result.affected.fileName);
399
+ }
400
+ }
401
+
402
+ const result = affectedFilePaths.distinct();
403
+
404
+ this._logger.debug("프로그램 리로드 결과", result);
405
+
406
+ return result;
407
+ }
408
+ else {
409
+ this._builder = ts.createAbstractBuilder(this._program, this._cacheCompilerHost);
410
+ const result = this._builder.getSourceFiles().map((item) => item.fileName).distinct();
411
+
412
+ this._logger.debug("프로그램 리로드 결과", result);
413
+
414
+ return result;
415
+ }
416
+ }
417
+
418
+ // eslint-disable-next-line @typescript-eslint/require-await
419
+ protected async _createCacheCompilerHostAsync(parsedTsconfig: ts.ParsedCommandLine, moduleResolutionCache: ts.ModuleResolutionCache): Promise<ts.CompilerHost> {
420
+ const compilerHost = ts.createCompilerHost(parsedTsconfig.options);
421
+
422
+ const cacheCompilerHost = { ...compilerHost };
423
+ cacheCompilerHost.fileExists = (fileName: string) => {
424
+ const cache = this._fileCache.getOrCreate(PathUtil.posix(fileName), {});
425
+ if (cache.exists === undefined) {
426
+ cache.exists = compilerHost.fileExists.call(cacheCompilerHost, fileName);
427
+ }
428
+ return cache.exists;
429
+ };
430
+
431
+ cacheCompilerHost.getSourceFile = (fileName: string, languageVersion: ts.ScriptTarget) => {
432
+ const cache = this._fileCache.getOrCreate(PathUtil.posix(fileName), {});
433
+ if (!cache.sourceFile) {
434
+ cache.sourceFile = compilerHost.getSourceFile.call(cacheCompilerHost, fileName, languageVersion);
435
+ }
436
+ return cache.sourceFile;
437
+ };
438
+
439
+ cacheCompilerHost.writeFile = (fileName: string,
440
+ data: string,
441
+ writeByteOrderMark: boolean,
442
+ onError?: (message: string) => void,
443
+ sourceFiles?: readonly ts.SourceFile[]) => {
444
+ if (sourceFiles) {
445
+ sourceFiles.forEach((source) => {
446
+ const cache = this._fileCache.getOrCreate(PathUtil.posix(source.fileName), {});
447
+ cache.distFilePaths = (cache.distFilePaths ?? []).concat([PathUtil.posix(fileName)]).distinct();
448
+ });
449
+ }
450
+
451
+ compilerHost.writeFile.call(cacheCompilerHost, fileName, data, writeByteOrderMark, onError, sourceFiles);
452
+ };
453
+
454
+ cacheCompilerHost.readFile = (fileName: string) => {
455
+ const cache = this._fileCache.getOrCreate(PathUtil.posix(fileName), {});
456
+ if (cache.content === undefined) {
457
+ cache.content = compilerHost.readFile.call(cacheCompilerHost, fileName);
458
+ }
459
+ return cache.content;
460
+ };
461
+
462
+ cacheCompilerHost.resolveModuleNames = (moduleNames: string[], containingFile: string) => {
463
+ return moduleNames.map((moduleName) => {
464
+ return ts.resolveModuleName(
465
+ moduleName,
466
+ containingFile,
467
+ parsedTsconfig.options,
468
+ compilerHost,
469
+ moduleResolutionCache
470
+ ).resolvedModule;
471
+ });
472
+ };
473
+
474
+ return cacheCompilerHost;
475
+ }
476
+ }
477
+
478
+ export interface ITsBuildFileCache {
479
+ exists?: boolean;
480
+ sourceFile?: ts.SourceFile;
481
+ distFilePaths?: string[];
482
+ content?: string;
483
+ }
484
+
485
+ export interface ITsGenResult {
486
+ dirtyFilePaths: string[];
487
+ result: ISdPackageBuildResult[];
488
+ }
package/src/commons.ts CHANGED
@@ -20,9 +20,7 @@ export interface ISdClientPackageConfig {
20
20
  type: "client";
21
21
  env?: Record<string, string>;
22
22
  server?: string;
23
- devServer?: {
24
- port: number;
25
- };
23
+ resolveFallback?: Record<string, string>;
26
24
  configs?: Record<string, any>;
27
25
  publish?: TSdPublishConfig;
28
26
  }
@@ -68,7 +66,17 @@ export interface ITsconfig {
68
66
  baseUrl?: string;
69
67
  paths?: Record<string, string[]>;
70
68
  };
69
+ angularCompilerOptions?: {
70
+ enableI18nLegacyMessageIdFormat?: boolean;
71
+ strictInjectionParameters?: boolean;
72
+ strictInputAccessModifiers?: boolean;
73
+ strictTemplates?: boolean;
74
+ strictInputTypes?: boolean;
75
+ strictOutputEventTypes?: boolean;
76
+ compilationMode?: "partial" | "full";
77
+ };
71
78
  files?: string[];
79
+ include?: string[];
72
80
  }
73
81
 
74
82
  export interface ISdPackageBuildResult {
@@ -20,25 +20,26 @@ export class SdCliLocalUpdater {
20
20
 
21
21
  const watcher = new SdFsWatcher();
22
22
  watcher
23
- .on("change", async (changedInfos) => {
23
+ .onChange(async (changedInfos) => {
24
24
  this._logger.log("로컬 라이브러리 변경감지...");
25
25
 
26
26
  const watchPaths2 = await this._getWatchPathsAsync(updatePathInfo.source);
27
27
  watcher.replaceWatchPaths(watchPaths2);
28
28
 
29
- for (const changedInfo of changedInfos) {
30
- if (changedInfo.filePath.includes("node_modules") || changedInfo.filePath.endsWith("package.json")) {
29
+ const changedFilePaths = changedInfos.map((item) => item.filePath).distinct();
30
+ for (const changedFilePath of changedFilePaths) {
31
+ if (changedFilePath.includes("node_modules") || changedFilePath.endsWith("package.json")) {
31
32
  continue;
32
33
  }
33
34
 
34
- const targetFilePath = path.resolve(updatePathInfo.target, path.relative(updatePathInfo.source, changedInfo.filePath));
35
+ const targetFilePath = path.resolve(updatePathInfo.target, path.relative(updatePathInfo.source, changedFilePath));
35
36
 
36
- if (FsUtil.exists(changedInfo.filePath)) {
37
- this._logger.debug(`변경파일감지(복사): ${changedInfo.filePath}`);
38
- await FsUtil.copyAsync(changedInfo.filePath, targetFilePath);
37
+ if (FsUtil.exists(changedFilePath)) {
38
+ this._logger.debug(`변경파일감지(복사): ${changedFilePath}`);
39
+ await FsUtil.copyAsync(changedFilePath, targetFilePath);
39
40
  }
40
41
  else {
41
- this._logger.debug(`변경파일감지(삭제): ${changedInfo.filePath}`);
42
+ this._logger.debug(`변경파일감지(삭제): ${changedFilePath}`);
42
43
  await FsUtil.removeAsync(targetFilePath);
43
44
  }
44
45
  }
@@ -66,12 +66,13 @@ export class SdCliProject {
66
66
  for (const pkg of pkgs) {
67
67
  pkg
68
68
  .on("change", (target) => {
69
+ this._logger.debug(`[${pkg.npmConfig.name}${target !== undefined ? `(${target})` : ""}] 빌드를 시작합니다.`);
70
+
69
71
  this._fnQueue.run(() => {
70
72
  if (changeCount === 0) {
71
73
  this._logger.log(`빌드를 시작합니다...`);
72
74
  }
73
75
  changeCount++;
74
- this._logger.debug(`[${pkg.npmConfig.name}${target !== undefined ? `(${target})` : ""}] 빌드를 시작합니다.`);
75
76
  });
76
77
  })
77
78
  .on("complete", (target, results) => {
@@ -83,12 +84,14 @@ export class SdCliProject {
83
84
  pkgName: pkg.npmConfig.name
84
85
  })));
85
86
 
86
- changeCount--;
87
- if (changeCount === 0) {
88
- this._loggingResults(totalResultMap);
89
- this._logger.info(`모든 빌드가 완료되었습니다.`);
90
- this._loggingOpenClientHrefs();
91
- }
87
+ setTimeout(() => {
88
+ changeCount--;
89
+ if (changeCount === 0) {
90
+ this._loggingResults(totalResultMap);
91
+ this._logger.info(`모든 빌드가 완료되었습니다.`);
92
+ this._loggingOpenClientHrefs();
93
+ }
94
+ }, 100);
92
95
  });
93
96
  });
94
97
 
@@ -118,10 +121,10 @@ export class SdCliProject {
118
121
 
119
122
  if (opt.watch) {
120
123
  if (pkg instanceof SdCliClientPackage) {
121
- const middleware = await pkg.watchAsync();
124
+ const middlewares = await pkg.watchAsync();
122
125
  if (!StringUtil.isNullOrEmpty(pkg.config.server)) {
123
126
  const serverInfo = this._serverInfoMap.getOrCreate(pkg.config.server, { middlewares: [], clientNames: [] });
124
- serverInfo.middlewares.push(middleware);
127
+ serverInfo.middlewares.push(...middlewares);
125
128
  serverInfo.clientNames = serverInfo.clientNames.concat([pkg.npmConfig.name.split("/").last()!]).distinct();
126
129
  }
127
130
  }
@@ -2,10 +2,10 @@ import { FsUtil } from "@simplysm/sd-core-node";
2
2
  import { ISdClientPackageConfig, ISdPackageBuildResult, ITsconfig } from "../commons";
3
3
  import * as path from "path";
4
4
  import { ObjectUtil } from "@simplysm/sd-core-common";
5
- import { SdCliNgClientBuilder } from "../build-tools/SdCliNgClientBuilder";
5
+ import { SdCliNgClientBuilder } from "../builders/SdCliNgClientBuilder";
6
6
  import { NextHandleFunction } from "connect";
7
7
  import { SdCliPackageBase } from "./SdCliPackageBase";
8
- import { SdCliPublisher } from "../build-tools/SdCliPublisher";
8
+ import { SdCliPublisher } from "../builders/SdCliPublisher";
9
9
 
10
10
  export class SdCliClientPackage extends SdCliPackageBase {
11
11
  public on(event: "change", listener: (target: "node" | "browser" | undefined) => void): this;
@@ -36,7 +36,7 @@ export class SdCliClientPackage extends SdCliPackageBase {
36
36
  .buildAsync();
37
37
  }
38
38
 
39
- public async watchAsync(): Promise<NextHandleFunction> {
39
+ public async watchAsync(): Promise<NextHandleFunction[]> {
40
40
  await this._genBuildTsconfigAsync();
41
41
 
42
42
  const buildTsconfigFilePath = path.resolve(this.rootPath, `sd-tsconfig.json`);
@@ -78,6 +78,17 @@ export class SdCliClientPackage extends SdCliPackageBase {
78
78
  buildTsconfig.compilerOptions.declaration = false;
79
79
  delete buildTsconfig.compilerOptions.declarationDir;
80
80
 
81
+ buildTsconfig.compilerOptions.baseUrl = this.rootPath;
82
+ buildTsconfig.angularCompilerOptions = {
83
+ ...buildTsconfig.angularCompilerOptions ?? {},
84
+ enableI18nLegacyMessageIdFormat: false,
85
+ strictInjectionParameters: true,
86
+ strictInputAccessModifiers: true,
87
+ strictTemplates: true,
88
+ strictInputTypes: false,
89
+ strictOutputEventTypes: false
90
+ };
91
+
81
92
  const buildTsconfigFilePath = path.resolve(this.rootPath, `sd-tsconfig.json`);
82
93
  await FsUtil.writeJsonAsync(buildTsconfigFilePath, buildTsconfig, { space: 2 });
83
94
  }