@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.
- package/.eslintrc.js +1 -2
- package/dist-node/bin/sd-cli.js +25 -8
- package/dist-node/bin/sd-cli.js.map +1 -1
- package/dist-node/build-tools/SdCliIndexFileGenerator.js +51 -0
- package/dist-node/build-tools/SdCliIndexFileGenerator.js.map +1 -0
- package/dist-node/build-tools/SdCliNgModuleFilesGenerator.js +621 -0
- package/dist-node/build-tools/SdCliNgModuleFilesGenerator.js.map +1 -0
- package/dist-node/build-tools/metadata/SdAotMetadataReader.js +270 -0
- package/dist-node/build-tools/metadata/SdAotMetadataReader.js.map +1 -0
- package/dist-node/build-tools/metadata/SdIvyMetadataReader.js +101 -0
- package/dist-node/build-tools/metadata/SdIvyMetadataReader.js.map +1 -0
- package/dist-node/build-tools/metadata/SdMyMetadataReader.js +298 -0
- package/dist-node/build-tools/metadata/SdMyMetadataReader.js.map +1 -0
- package/dist-node/{build-tools → builders}/SdCliJavascriptLinter.js +1 -1
- package/dist-node/builders/SdCliJavascriptLinter.js.map +1 -0
- package/dist-node/{build-tools → builders}/SdCliNgClientBuilder.js +176 -102
- package/dist-node/builders/SdCliNgClientBuilder.js.map +1 -0
- package/dist-node/builders/SdCliNgLibraryBuilder.js +238 -0
- package/dist-node/builders/SdCliNgLibraryBuilder.js.map +1 -0
- package/dist-node/{build-tools → builders}/SdCliPublisher.js +0 -0
- package/dist-node/builders/SdCliPublisher.js.map +1 -0
- package/dist-node/{build-tools → builders}/SdCliServerBuilder.js +8 -6
- package/dist-node/builders/SdCliServerBuilder.js.map +1 -0
- package/dist-node/builders/SdCliTypescriptBuilder.js +304 -0
- package/dist-node/builders/SdCliTypescriptBuilder.js.map +1 -0
- package/dist-node/entry-points/SdCliLocalUpdater.js +9 -8
- package/dist-node/entry-points/SdCliLocalUpdater.js.map +1 -1
- package/dist-node/entry-points/SdCliProject.js +11 -9
- package/dist-node/entry-points/SdCliProject.js.map +1 -1
- package/dist-node/packages/SdCliClientPackage.js +12 -2
- package/dist-node/packages/SdCliClientPackage.js.map +1 -1
- package/dist-node/packages/SdCliLibraryPackage.js +17 -9
- package/dist-node/packages/SdCliLibraryPackage.js.map +1 -1
- package/dist-node/packages/SdCliServerPackage.js +2 -2
- package/dist-node/packages/SdCliServerPackage.js.map +1 -1
- package/dist-node/utils/SdCliFileCrypto.js +76 -0
- package/dist-node/utils/SdCliFileCrypto.js.map +1 -0
- package/dist-node/utils/SdProjectConfigUtil.js +1 -1
- package/dist-node/utils/SdProjectConfigUtil.js.map +1 -1
- package/dist-node/utils/SdWebpackUtil.js +12 -3
- package/dist-node/utils/SdWebpackUtil.js.map +1 -1
- package/dist-node/workers/lib-build-worker.js +7 -6
- package/dist-node/workers/lib-build-worker.js.map +1 -1
- package/dist-types/build-tools/SdCliIndexFileGenerator.d.ts +12 -0
- package/dist-types/build-tools/SdCliNgModuleFilesGenerator.d.ts +42 -0
- package/dist-types/build-tools/metadata/SdAotMetadataReader.d.ts +57 -0
- package/dist-types/build-tools/metadata/SdIvyMetadataReader.d.ts +41 -0
- package/dist-types/build-tools/metadata/SdMyMetadataReader.d.ts +57 -0
- package/dist-types/{build-tools → builders}/SdCliJavascriptLinter.d.ts +0 -0
- package/dist-types/{build-tools → builders}/SdCliNgClientBuilder.d.ts +1 -1
- package/dist-types/builders/SdCliNgLibraryBuilder.d.ts +27 -0
- package/dist-types/{build-tools → builders}/SdCliPublisher.d.ts +0 -0
- package/dist-types/{build-tools → builders}/SdCliServerBuilder.d.ts +0 -0
- package/dist-types/builders/SdCliTypescriptBuilder.d.ts +46 -0
- package/dist-types/commons.d.ts +11 -3
- package/dist-types/packages/SdCliClientPackage.d.ts +1 -1
- package/dist-types/utils/SdCliFileCrypto.d.ts +7 -0
- package/dist-types/utils/SdWebpackUtil.d.ts +1 -0
- package/package.json +10 -7
- package/schema/schema.json +247 -0
- package/src/bin/sd-cli.ts +37 -14
- package/src/build-tools/SdCliIndexFileGenerator.ts +61 -0
- package/src/build-tools/SdCliNgModuleFilesGenerator.ts +837 -0
- package/src/build-tools/metadata/SdAotMetadataReader.ts +368 -0
- package/src/build-tools/metadata/SdIvyMetadataReader.ts +152 -0
- package/src/build-tools/metadata/SdMyMetadataReader.ts +379 -0
- package/src/{build-tools → builders}/SdCliJavascriptLinter.ts +1 -1
- package/src/{build-tools → builders}/SdCliNgClientBuilder.ts +209 -111
- package/src/builders/SdCliNgLibraryBuilder.ts +336 -0
- package/src/{build-tools → builders}/SdCliPublisher.ts +0 -0
- package/src/{build-tools → builders}/SdCliServerBuilder.ts +14 -8
- package/src/builders/SdCliTypescriptBuilder.ts +488 -0
- package/src/commons.ts +11 -3
- package/src/entry-points/SdCliLocalUpdater.ts +9 -8
- package/src/entry-points/SdCliProject.ts +12 -9
- package/src/packages/SdCliClientPackage.ts +14 -3
- package/src/packages/SdCliLibraryPackage.ts +19 -11
- package/src/packages/SdCliServerPackage.ts +2 -2
- package/src/utils/SdCliFileCrypto.ts +87 -0
- package/src/utils/SdCliUtils.ts +56 -0
- package/src/utils/SdProjectConfigUtil.ts +1 -1
- package/src/utils/SdWebpackUtil.ts +13 -3
- package/src/workers/lib-build-worker.ts +8 -7
- package/dist-node/build-tools/SdCliJavascriptLinter.js.map +0 -1
- package/dist-node/build-tools/SdCliNgClientBuilder.js.map +0 -1
- package/dist-node/build-tools/SdCliNgLibraryBuilder.js +0 -164
- package/dist-node/build-tools/SdCliNgLibraryBuilder.js.map +0 -1
- package/dist-node/build-tools/SdCliPublisher.js.map +0 -1
- package/dist-node/build-tools/SdCliServerBuilder.js.map +0 -1
- package/dist-node/build-tools/SdCliTypescriptBuilder.js +0 -316
- package/dist-node/build-tools/SdCliTypescriptBuilder.js.map +0 -1
- package/dist-node/build-tools/SdCliTypescriptIndexFileGenerator.js +0 -75
- package/dist-node/build-tools/SdCliTypescriptIndexFileGenerator.js.map +0 -1
- package/dist-types/build-tools/SdCliNgLibraryBuilder.d.ts +0 -23
- package/dist-types/build-tools/SdCliTypescriptBuilder.d.ts +0 -37
- package/dist-types/build-tools/SdCliTypescriptIndexFileGenerator.d.ts +0 -12
- package/src/build-tools/SdCliNgLibraryBuilder.ts +0 -225
- package/src/build-tools/SdCliTypescriptBuilder.ts +0 -424
- 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
|
-
|
|
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
|
-
.
|
|
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
|
-
|
|
30
|
-
|
|
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,
|
|
35
|
+
const targetFilePath = path.resolve(updatePathInfo.target, path.relative(updatePathInfo.source, changedFilePath));
|
|
35
36
|
|
|
36
|
-
if (FsUtil.exists(
|
|
37
|
-
this._logger.debug(`변경파일감지(복사): ${
|
|
38
|
-
await FsUtil.copyAsync(
|
|
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(`변경파일감지(삭제): ${
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
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(
|
|
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 "../
|
|
5
|
+
import { SdCliNgClientBuilder } from "../builders/SdCliNgClientBuilder";
|
|
6
6
|
import { NextHandleFunction } from "connect";
|
|
7
7
|
import { SdCliPackageBase } from "./SdCliPackageBase";
|
|
8
|
-
import { SdCliPublisher } from "../
|
|
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
|
}
|