@simplysm/sd-cli 11.0.3 → 11.0.5

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 (50) hide show
  1. package/dist/build-cluster.js +1 -1
  2. package/dist/build-cluster.js.map +1 -1
  3. package/dist/build-tools/SdLinter.js +2 -2
  4. package/dist/build-tools/SdLinter.js.map +1 -1
  5. package/dist/build-tools/SdNgBundler.d.ts +23 -0
  6. package/dist/build-tools/SdNgBundler.js +331 -0
  7. package/dist/build-tools/SdNgBundler.js.map +1 -0
  8. package/dist/build-tools/SdTsBundler.d.ts +15 -0
  9. package/dist/build-tools/SdTsBundler.js +96 -0
  10. package/dist/build-tools/SdTsBundler.js.map +1 -0
  11. package/dist/build-tools/SdTsCompiler.d.ts +28 -0
  12. package/dist/build-tools/SdTsCompiler.js +212 -0
  13. package/dist/build-tools/SdTsCompiler.js.map +1 -0
  14. package/dist/builders/SdCliClientBuilder.d.ts +4 -2
  15. package/dist/builders/SdCliClientBuilder.js +60 -297
  16. package/dist/builders/SdCliClientBuilder.js.map +1 -1
  17. package/dist/builders/SdCliServerBuilder.js +79 -216
  18. package/dist/builders/SdCliServerBuilder.js.map +1 -1
  19. package/dist/builders/SdCliTsLibBuilder.d.ts +0 -1
  20. package/dist/builders/SdCliTsLibBuilder.js +35 -33
  21. package/dist/builders/SdCliTsLibBuilder.js.map +1 -1
  22. package/dist/commons.d.ts +7 -0
  23. package/dist/entry/SdCliProject.js +13 -10
  24. package/dist/entry/SdCliProject.js.map +1 -1
  25. package/dist/index.d.ts +3 -1
  26. package/dist/index.js +3 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/server-worker.js +3 -0
  29. package/dist/server-worker.js.map +1 -1
  30. package/dist/utils/SdCliBuildResultUtil.d.ts +1 -1
  31. package/dist/utils/SdCliBuildResultUtil.js +4 -4
  32. package/dist/utils/SdCliBuildResultUtil.js.map +1 -1
  33. package/package.json +14 -12
  34. package/src/build-cluster.ts +1 -1
  35. package/src/build-tools/SdLinter.ts +2 -2
  36. package/src/build-tools/SdNgBundler.ts +404 -0
  37. package/src/build-tools/SdTsBundler.ts +106 -0
  38. package/src/build-tools/SdTsCompiler.ts +304 -0
  39. package/src/builders/SdCliClientBuilder.ts +75 -322
  40. package/src/builders/SdCliServerBuilder.ts +95 -243
  41. package/src/builders/SdCliTsLibBuilder.ts +39 -40
  42. package/src/commons.ts +6 -0
  43. package/src/entry/SdCliProject.ts +17 -12
  44. package/src/index.ts +3 -1
  45. package/src/server-worker.ts +3 -0
  46. package/src/utils/SdCliBuildResultUtil.ts +4 -4
  47. package/dist/build-tools/SdTsIncrementalBuilder.d.ts +0 -31
  48. package/dist/build-tools/SdTsIncrementalBuilder.js +0 -126
  49. package/dist/build-tools/SdTsIncrementalBuilder.js.map +0 -1
  50. package/src/build-tools/SdTsIncrementalBuilder.ts +0 -207
@@ -0,0 +1,304 @@
1
+ import path from "path";
2
+ import ts from "typescript";
3
+ import {SdCliBuildResultUtil} from "../utils/SdCliBuildResultUtil";
4
+ import {FsUtil, Logger, PathUtil} from "@simplysm/sd-core-node";
5
+ import {ISdCliPackageBuildResult, ITsConfig} from "../commons";
6
+ import {NgtscProgram} from "@angular/compiler-cli";
7
+ import {createHash} from "crypto";
8
+ import {fileURLToPath, pathToFileURL} from "url";
9
+ import * as sass from "sass";
10
+
11
+ export class SdTsCompiler {
12
+ private readonly _logger = Logger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
13
+
14
+ private readonly _parsedTsConfig: ts.ParsedCommandLine;
15
+ private readonly _writeFileCache = new Map<string, string>();
16
+ private readonly _compilerHost: ts.CompilerHost;
17
+ private _program?: ts.Program;
18
+ private _builder?: ts.SemanticDiagnosticsBuilderProgram;
19
+
20
+ //-- for ng
21
+ private readonly _isForAngular: boolean;
22
+ private _ngProgram?: NgtscProgram;
23
+ private readonly _styleDepsCache = new Map<string, Set<string>>();
24
+ private _markedChanges: string[] = [];
25
+
26
+ public get program(): ts.Program {
27
+ if (!this._program) {
28
+ throw new Error("TS 프로그램 NULL");
29
+ }
30
+ return this._program;
31
+ }
32
+
33
+ public constructor(private readonly _opt: {
34
+ pkgPath: string,
35
+ emit: boolean;
36
+ emitDts: boolean;
37
+ globalStyle: boolean;
38
+ }) {
39
+ //-- tsconfig
40
+ const tsConfigFilePath = path.resolve(_opt.pkgPath, "tsconfig.json");
41
+ const tsConfig = FsUtil.readJson(tsConfigFilePath) as ITsConfig;
42
+ this._parsedTsConfig = ts.parseJsonConfigFileContent(
43
+ tsConfig,
44
+ ts.sys,
45
+ _opt.pkgPath,
46
+ {
47
+ ...tsConfig.angularCompilerOptions ?? {},
48
+ ..._opt.emitDts !== undefined ? {declaration: _opt.emitDts} : {}
49
+ }
50
+ );
51
+
52
+ //-- vars
53
+ this._isForAngular = Boolean(tsConfig.angularCompilerOptions);
54
+
55
+ //-- host
56
+ this._compilerHost = ts.createIncrementalCompilerHost(this._parsedTsConfig.options);
57
+ if (tsConfig.angularCompilerOptions) {
58
+ this._compilerHost["readResource"] = (fileName: string) => {
59
+ return this._compilerHost.readFile(fileName);
60
+ };
61
+
62
+ this._compilerHost["transformResource"] = async (data: string, context: {
63
+ type: string,
64
+ containingFile: string,
65
+ resourceFile: any
66
+ }) => {
67
+ if (context.resourceFile != null || context.type !== "style") {
68
+ return null;
69
+ }
70
+
71
+ try {
72
+ const scssResult = await sass.compileStringAsync(data, {
73
+ url: new URL((context.containingFile as string) + ".scss"),
74
+ importer: {
75
+ findFileUrl: (url) => pathToFileURL(url)
76
+ },
77
+ logger: sass.Logger.silent
78
+ });
79
+
80
+ const styleContent = scssResult.css.toString();
81
+
82
+ const deps = scssResult.loadedUrls.slice(1).map((item) => path.resolve(fileURLToPath(item.href)));
83
+ for (const dep of deps) {
84
+ const depCache = this._styleDepsCache.getOrCreate(dep, new Set<string>());
85
+ depCache.add(path.resolve(context.containingFile));
86
+ }
87
+ return {content: styleContent};
88
+ }
89
+ catch (err) {
90
+ this._logger.error("scss 파싱 에러", err);
91
+ return null;
92
+ }
93
+ };
94
+ }
95
+ }
96
+
97
+ public markChanges(changes: string[]): void {
98
+ this._markedChanges.push(...changes);
99
+ }
100
+
101
+ public async buildAsync(): Promise<{
102
+ affectedFilePaths: string[];
103
+ results: ISdCliPackageBuildResult[];
104
+ }> {
105
+ const markedChanges = this._markedChanges;
106
+ this._markedChanges = [];
107
+
108
+ const distPath = path.resolve(this._opt.pkgPath, "dist");
109
+ const srcFilePaths = await FsUtil.globAsync(path.resolve(this._opt.pkgPath, "src/**/*.{ts,tsx}"));
110
+ const srcFilePathSet = new Set<string>(srcFilePaths);
111
+
112
+ if (this._isForAngular) {
113
+ this._ngProgram = new NgtscProgram(
114
+ srcFilePaths,
115
+ this._parsedTsConfig.options,
116
+ this._compilerHost,
117
+ this._ngProgram
118
+ );
119
+ this._program = this._ngProgram.getTsProgram();
120
+
121
+ const baseGetSourceFiles = this._program.getSourceFiles;
122
+ this._program.getSourceFiles = function (...parameters) {
123
+ const files: readonly (ts.SourceFile & { version?: string })[] = baseGetSourceFiles(...parameters);
124
+
125
+ for (const file of files) {
126
+ if (file.version === undefined) {
127
+ file.version = createHash("sha256").update(file.text).digest("hex");
128
+ }
129
+ }
130
+
131
+ return files;
132
+ };
133
+ }
134
+ else {
135
+ this._program = ts.createProgram(
136
+ srcFilePaths,
137
+ this._parsedTsConfig.options,
138
+ this._compilerHost,
139
+ this._program
140
+ );
141
+ }
142
+
143
+ this._builder = ts.createSemanticDiagnosticsBuilderProgram(
144
+ this._program,
145
+ this._compilerHost,
146
+ this._builder
147
+ );
148
+
149
+ const diagnostics: ts.Diagnostic[] = [];
150
+ const affectedFilePaths: string[] = [];
151
+
152
+ if (this._ngProgram) {
153
+ diagnostics.push(...this._ngProgram.compiler.getOptionDiagnostics());
154
+ }
155
+
156
+ diagnostics.push(
157
+ ...this._builder.getOptionsDiagnostics(),
158
+ ...this._builder.getGlobalDiagnostics(),
159
+ );
160
+
161
+ if (this._ngProgram) {
162
+ await this._ngProgram.compiler.analyzeAsync();
163
+ }
164
+
165
+ this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 확인중...`);
166
+ while (true) {
167
+ let affectedSourceFile: ts.SourceFile | undefined;
168
+
169
+ const semanticResult = this._builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
170
+ //-- ngtypecheck의 org파일 포함 (ngtypecheck 파일는 무시)
171
+ if (this._ngProgram?.compiler.ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith(".ngtypecheck.ts")) {
172
+ const orgFileName = sourceFile.fileName.slice(0, -15) + ".ts";
173
+ const orgSourceFile = this._builder!.getSourceFile(orgFileName);
174
+ if (orgSourceFile) {
175
+ affectedSourceFile = orgSourceFile;
176
+ }
177
+ return true;
178
+ }
179
+
180
+ //-- 소스폴더 파일 포함
181
+ else if (srcFilePathSet.has(path.resolve(sourceFile.fileName))) {
182
+ affectedSourceFile = sourceFile;
183
+ return false;
184
+ }
185
+
186
+ //-- 나머지 무시
187
+ else {
188
+ return true;
189
+ }
190
+ });
191
+ if (!semanticResult || !affectedSourceFile) break;
192
+ diagnostics.push(...semanticResult.result);
193
+
194
+ if ("fileName" in affectedSourceFile) {
195
+ affectedFilePaths.push(path.resolve(affectedSourceFile.fileName));
196
+ }
197
+ }
198
+
199
+ if (this._isForAngular) {
200
+ for (const markedChange of markedChanges) {
201
+ const depsSet = this._styleDepsCache.get(markedChange);
202
+ if (depsSet) {
203
+ affectedFilePaths.push(...depsSet);
204
+ }
205
+ }
206
+ affectedFilePaths.distinctThis();
207
+ }
208
+
209
+ const globalStyleFilePath = path.resolve(this._opt.pkgPath, "src/styles.scss");
210
+ if (this._opt.globalStyle && FsUtil.exists(globalStyleFilePath) && markedChanges.includes(globalStyleFilePath)) {
211
+ affectedFilePaths.push(globalStyleFilePath);
212
+ affectedFilePaths.distinctThis();
213
+ }
214
+
215
+ this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 ${this._opt.emit ? "EMIT" : "CHECK"}...`);
216
+
217
+ for (const affectedFilePath of affectedFilePaths) {
218
+ if (this._opt.globalStyle && affectedFilePath === globalStyleFilePath) {
219
+ try {
220
+ const content = await FsUtil.readFileAsync(affectedFilePath);
221
+ const scssResult = await sass.compileStringAsync(content, {
222
+ url: new URL(affectedFilePath),
223
+ importer: {
224
+ findFileUrl: (url) => pathToFileURL(url)
225
+ },
226
+ logger: sass.Logger.silent
227
+ });
228
+
229
+ const deps = scssResult.loadedUrls.slice(1).map((item) => path.resolve(fileURLToPath(item.href)));
230
+ for (const dep of deps) {
231
+ const depCache = this._styleDepsCache.getOrCreate(dep, new Set<string>());
232
+ depCache.add(affectedFilePath);
233
+ }
234
+
235
+ if (this._opt.emit) {
236
+ const outFilePath = path.resolve(this._opt.pkgPath, path.basename(affectedFilePath, path.extname(affectedFilePath)) + ".css");
237
+
238
+ this._writeFile(outFilePath, scssResult.css.toString());
239
+ }
240
+ }
241
+ catch (err) {
242
+ this._logger.error(err);
243
+ }
244
+ }
245
+ else {
246
+ const affectedSourceFile = this._builder.getSourceFile(affectedFilePath);
247
+ if (!affectedSourceFile) continue;
248
+
249
+ const emitResult = this._builder.emit(affectedSourceFile,
250
+ (filePath, data) => {
251
+ let realFilePath = filePath;
252
+ let realData = data;
253
+ if (PathUtil.isChildPath(realFilePath, path.resolve(distPath, path.basename(this._opt.pkgPath), "src"))) {
254
+ realFilePath = path.resolve(distPath, path.relative(path.resolve(distPath, path.basename(this._opt.pkgPath), "src"), realFilePath));
255
+
256
+ if (filePath.endsWith(".js.map")) {
257
+ const sourceMapContents = JSON.parse(realData);
258
+ // remove "../../"
259
+ sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6);
260
+ realData = JSON.stringify(sourceMapContents);
261
+ }
262
+ }
263
+
264
+ this._writeFile(realFilePath, realData);
265
+ },
266
+ undefined,
267
+ !this._opt.emit,
268
+ {...this._ngProgram?.compiler.prepareEmit().transformers ?? {}}
269
+ );
270
+
271
+ diagnostics.push(...emitResult.diagnostics);
272
+
273
+ diagnostics.push(...this._builder.getSyntacticDiagnostics(affectedSourceFile));
274
+
275
+ if (
276
+ this._ngProgram &&
277
+ !affectedSourceFile.isDeclarationFile &&
278
+ !this._ngProgram.compiler.ignoreForEmit.has(affectedSourceFile) &&
279
+ !this._ngProgram.compiler.incrementalCompilation.safeToSkipEmit(affectedSourceFile)
280
+ ) {
281
+ diagnostics.push(
282
+ ...this._ngProgram.compiler.getDiagnosticsForFile(affectedSourceFile, 1)
283
+ );
284
+ }
285
+ }
286
+ }
287
+
288
+ this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 ${this._opt.emit ? "EMIT" : "CHECK"} 완료`, affectedFilePaths);
289
+
290
+ const buildResults = diagnostics.map((item) => SdCliBuildResultUtil.convertFromTsDiag(item, this._opt.emit ? "build" : "check"));
291
+
292
+ return {
293
+ affectedFilePaths: affectedFilePaths.filter((item) => !item.endsWith(".scss")),
294
+ results: buildResults
295
+ };
296
+ }
297
+
298
+ private _writeFile(filePath: string, data: string): void {
299
+ if (this._writeFileCache.get(filePath) !== data) {
300
+ this._compilerHost.writeFile(filePath, data, false);
301
+ }
302
+ this._writeFileCache.set(filePath, data);
303
+ }
304
+ }