@simplysm/sd-cli 11.1.46 → 11.1.52

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 (43) hide show
  1. package/dist/build-tools/SdLinter.js +4 -4
  2. package/dist/build-tools/SdLinter.js.map +1 -1
  3. package/dist/build-tools/SdNgBundler.d.ts +10 -19
  4. package/dist/build-tools/SdNgBundler.js +115 -104
  5. package/dist/build-tools/SdNgBundler.js.map +1 -1
  6. package/dist/build-tools/SdNgBundlerContext.d.ts +1 -0
  7. package/dist/build-tools/SdNgBundlerContext.js +11 -3
  8. package/dist/build-tools/SdNgBundlerContext.js.map +1 -1
  9. package/dist/build-tools/SdTsCompiler.d.ts +23 -10
  10. package/dist/build-tools/SdTsCompiler.js +262 -221
  11. package/dist/build-tools/SdTsCompiler.js.map +1 -1
  12. package/dist/build-tools/SdTsLibBundler.d.ts +13 -0
  13. package/dist/build-tools/SdTsLibBundler.js +51 -0
  14. package/dist/build-tools/SdTsLibBundler.js.map +1 -0
  15. package/dist/builders/SdCliTsLibBuilder.d.ts +2 -6
  16. package/dist/builders/SdCliTsLibBuilder.js +26 -21
  17. package/dist/builders/SdCliTsLibBuilder.js.map +1 -1
  18. package/dist/bundle-plugins/sdNgPlugin.js +2 -2
  19. package/dist/bundle-plugins/sdNgPlugin.js.map +1 -1
  20. package/dist/bundle-plugins/sdServerPlugin.js +2 -2
  21. package/dist/bundle-plugins/sdServerPlugin.js.map +1 -1
  22. package/dist/entry/SdCliProject.js +4 -1
  23. package/dist/entry/SdCliProject.js.map +1 -1
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/sd-cli.d.ts +1 -1
  27. package/dist/sd-cli.js +1 -1
  28. package/package.json +5 -5
  29. package/src/build-tools/SdLinter.ts +4 -4
  30. package/src/build-tools/SdNgBundler.ts +133 -114
  31. package/src/build-tools/SdNgBundlerContext.ts +14 -3
  32. package/src/build-tools/SdTsCompiler.ts +375 -214
  33. package/src/build-tools/SdTsLibBundler.ts +70 -0
  34. package/src/builders/SdCliTsLibBuilder.ts +29 -22
  35. package/src/bundle-plugins/sdNgPlugin.ts +2 -2
  36. package/src/bundle-plugins/sdServerPlugin.ts +2 -2
  37. package/src/entry/SdCliProject.ts +4 -1
  38. package/src/index.ts +1 -1
  39. package/src/sd-cli.ts +1 -1
  40. package/dist/build-tools2/SdTsCompiler2.d.ts +0 -26
  41. package/dist/build-tools2/SdTsCompiler2.js +0 -280
  42. package/dist/build-tools2/SdTsCompiler2.js.map +0 -1
  43. package/src/build-tools2/SdTsCompiler2.ts +0 -427
@@ -1,286 +1,447 @@
1
- import {SdCliBuildResultUtil} from "../utils/SdCliBuildResultUtil";
2
- import {ISdCliPackageBuildResult} from "../commons";
3
- import {SdTsCompiler2} from "../build-tools2/SdTsCompiler2";
4
- import ts from "typescript";
1
+ import ts, {CompilerOptions} from "typescript";
5
2
  import path from "path";
6
- import {FsUtil, PathUtil} from "@simplysm/sd-core-node";
3
+ import {FsUtil, Logger, PathUtil} from "@simplysm/sd-core-node";
4
+ import {transformSupportedBrowsersToTargets} from "@angular-devkit/build-angular/src/tools/esbuild/utils";
5
+ import browserslist from "browserslist";
6
+ import {
7
+ ComponentStylesheetBundler
8
+ } from "@angular-devkit/build-angular/src/tools/esbuild/angular/component-stylesheets";
9
+ import {AngularCompilerHost} from "@angular-devkit/build-angular/src/tools/esbuild/angular/angular-host";
10
+ import {StringUtil} from "@simplysm/sd-core-common";
11
+ import esbuild from "esbuild";
12
+ import {NgtscProgram, OptimizeFor} from "@angular/compiler-cli";
13
+ import {createHash} from "crypto";
7
14
 
8
15
  export class SdTsCompiler {
9
- /*private readonly _logger = Logger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
16
+ readonly #logger = Logger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
10
17
 
11
- private readonly _parsedTsConfig: ts.ParsedCommandLine;
12
- private readonly _writeFileCache = new Map<string, string>();
13
- private readonly _compilerHost: ts.CompilerHost;
14
- private _program?: ts.Program;
15
- private _builder?: ts.EmitAndSemanticDiagnosticsBuilderProgram;
18
+ readonly #parsedTsconfig: ts.ParsedCommandLine;
19
+ readonly #isForAngular: boolean;
16
20
 
17
- //-- for ng
18
- private readonly _isForAngular: boolean;
19
- private _ngProgram?: NgtscProgram;
20
- private readonly _styleDepsCache = new Map<string, Set<string>>();
21
- private _markedChanges: string[] = [];*/
21
+ readonly #resourceDependencyCacheMap = new Map<string, Set<string>>();
22
+ readonly #sourceFileCacheMap = new Map<string, ts.SourceFile>();
23
+ readonly #emittedFilesCacheMap = new Map<string, {
24
+ outRelPath?: string;
25
+ text: string;
26
+ }[]>();
22
27
 
23
- program?: ts.Program;
28
+ readonly #stylesheetBundler: ComponentStylesheetBundler | undefined;
29
+ readonly #compilerHost: ts.CompilerHost | AngularCompilerHost;
24
30
 
25
- readonly #compiler: SdTsCompiler2;
31
+ #ngProgram: NgtscProgram | undefined;
32
+ #program: ts.Program | undefined;
33
+ #builder: ts.EmitAndSemanticDiagnosticsBuilderProgram | undefined;
26
34
 
27
- readonly #pkgPath: string;
35
+ readonly #modifiedFileSet = new Set<string>();
28
36
 
29
- /*public get program(): ts.Program {
30
- if (!this._program) {
31
- throw new Error("TS 프로그램 NULL");
32
- }
33
- return this._program;
34
- }*/
37
+ readonly #watchFileSet = new Set<string>();
38
+ readonly #stylesheetResultMap = new Map<string, IStylesheetResult>();
39
+ readonly #affectedFileSet = new Set<string>();
40
+
41
+ readonly #pkgPath: string;
42
+ readonly #distPath: string;
43
+ readonly #globalStyleFilePath?: string;
35
44
 
36
- public constructor(pkgPath: string, dev: boolean) {
45
+ constructor(pkgPath: string,
46
+ additionalOptions: CompilerOptions,
47
+ isDevMode: boolean,
48
+ globalStyleFilePath?: string) {
37
49
  this.#pkgPath = pkgPath;
38
- this.#compiler = new SdTsCompiler2(
39
- pkgPath,
40
- {declaration: true},
41
- dev,
42
- path.resolve(pkgPath, "src/styles.scss")
43
- );
44
- }
50
+ this.#globalStyleFilePath = globalStyleFilePath != null ? path.normalize(globalStyleFilePath) : undefined;
45
51
 
46
- public markChanges(modifiedFileSet: Set<string>): void {
47
- this.#compiler.invalidate(modifiedFileSet);
48
- }
49
52
 
50
- public async buildAsync(): Promise<{
51
- watchFileSet: Set<string>;
52
- affectedFileSet: Set<string>;
53
- results: ISdCliPackageBuildResult[];
54
- }> {
55
- const buildResult = await this.#compiler.buildAsync();
56
- this.program = buildResult.program;
57
-
58
- for (const affectedFilePath of buildResult.affectedFileSet) {
59
- const emittedFiles = buildResult.emittedFilesCacheMap.get(affectedFilePath) ?? [];
60
- for (const emittedFile of emittedFiles) {
61
- if (emittedFile.outRelPath != null) {
62
- const distPath = path.resolve(this.#pkgPath, "dist", emittedFile.outRelPath);
63
- if (PathUtil.isChildPath(distPath, path.resolve(this.#pkgPath, "dist"))) {
64
- await FsUtil.writeFileAsync(distPath, emittedFile.text);
65
- }
66
- }
53
+ this.#debug("초기화...");
54
+
55
+ //-- isForAngular / parsedTsConfig
56
+
57
+ const tsconfigPath = path.resolve(pkgPath, "tsconfig.json");
58
+ const tsconfig = FsUtil.readJson(tsconfigPath);
59
+ this.#isForAngular = Boolean(tsconfig.angularCompilerOptions);
60
+ this.#parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, pkgPath, {
61
+ ...tsconfig.angularCompilerOptions,
62
+ ...additionalOptions
63
+ });
64
+
65
+ this.#distPath = this.#parsedTsconfig.options.outDir ?? path.resolve(pkgPath, "dist");
66
+
67
+ //-- compilerHost
68
+
69
+ this.#compilerHost = ts.createIncrementalCompilerHost(this.#parsedTsconfig.options);
70
+
71
+ const baseGetSourceFile = this.#compilerHost.getSourceFile;
72
+ this.#compilerHost.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile, ...args) => {
73
+ if (!shouldCreateNewSourceFile && this.#sourceFileCacheMap.has(path.normalize(fileName))) {
74
+ return this.#sourceFileCacheMap.get(path.normalize(fileName));
67
75
  }
68
76
 
69
- const globalStylesheetResult = buildResult.stylesheetResultMap.get(affectedFilePath);
70
- if (globalStylesheetResult) {
71
- for (const outputFile of globalStylesheetResult.outputFiles) {
72
- const distPath = path.resolve(this.#pkgPath, "dist", path.relative(this.#pkgPath, outputFile.path));
73
- if (PathUtil.isChildPath(distPath, path.resolve(this.#pkgPath, "dist"))) {
74
- await FsUtil.writeFileAsync(distPath, outputFile.text);
77
+ const sf = baseGetSourceFile.call(
78
+ this.#compilerHost,
79
+ fileName,
80
+ languageVersionOrOptions,
81
+ onError,
82
+ true,
83
+ ...args,
84
+ );
85
+
86
+ if (sf) {
87
+ this.#sourceFileCacheMap.set(path.normalize(fileName), sf);
88
+ }
89
+ else {
90
+ this.#sourceFileCacheMap.delete(path.normalize(fileName));
91
+ }
92
+
93
+ return sf;
94
+ };
95
+
96
+ if (this.#isForAngular) {
97
+ //-- stylesheetBundler
98
+
99
+ const browserTarget = transformSupportedBrowsersToTargets(browserslist("defaults and fully supports es6-module"));
100
+ this.#stylesheetBundler = new ComponentStylesheetBundler(
101
+ {
102
+ workspaceRoot: pkgPath,
103
+ optimization: !isDevMode,
104
+ inlineFonts: true,
105
+ preserveSymlinks: false,
106
+ sourcemap: 'inline', //conf.dev ? 'inline' : false,
107
+ outputNames: {bundles: '[name]', media: 'media/[name]'},
108
+ includePaths: [],
109
+ externalDependencies: [],
110
+ target: browserTarget,
111
+ tailwindConfiguration: undefined,
112
+ cacheOptions: {
113
+ enabled: true,
114
+ path: ".cache/angular",
115
+ basePath: ".cache"
75
116
  }
117
+ },
118
+ isDevMode
119
+ );
120
+
121
+ //-- compilerHost
122
+
123
+ (this.#compilerHost as AngularCompilerHost).readResource = (fileName: string) => {
124
+ return this.#compilerHost.readFile(fileName) ?? "";
125
+ };
126
+
127
+ (this.#compilerHost as AngularCompilerHost).transformResource = async (data: string, context: {
128
+ type: string,
129
+ containingFile: string,
130
+ resourceFile: string | null
131
+ }) => {
132
+ if (context.type !== "style") {
133
+ return null;
134
+ }
135
+
136
+ const contents = await this.#bundleStylesheetAsync(data, context.containingFile, context.resourceFile);
137
+
138
+ return StringUtil.isNullOrEmpty(contents) ? null : {content: contents};
139
+ };
140
+
141
+ (this.#compilerHost as AngularCompilerHost).getModifiedResourceFiles = () => {
142
+ return this.#modifiedFileSet;
143
+ };
144
+ }
145
+ }
146
+
147
+ async #bundleStylesheetAsync(data: string, containingFile: string, resourceFile: string | null = null) {
148
+ this.#debug(`스타일시트 번들링...(${containingFile}, ${resourceFile})`);
149
+
150
+ const stylesheetResult = resourceFile != null
151
+ ? await this.#stylesheetBundler!.bundleFile(resourceFile)
152
+ : await this.#stylesheetBundler!.bundleInline(
153
+ data,
154
+ containingFile,
155
+ "scss",
156
+ );
157
+
158
+ this.#watchFileSet.add(path.normalize(containingFile));
159
+ if (resourceFile != null) {
160
+ this.#watchFileSet.add(path.normalize(resourceFile));
161
+ }
162
+
163
+ if (stylesheetResult.referencedFiles) {
164
+ for (const referencedFile of stylesheetResult.referencedFiles) {
165
+ const referencingMapValSet = this.#resourceDependencyCacheMap.getOrCreate(path.normalize(referencedFile), new Set<string>());
166
+ referencingMapValSet.add(path.normalize(containingFile));
167
+ if (resourceFile != null) {
168
+ referencingMapValSet.add(path.normalize(resourceFile));
76
169
  }
77
170
  }
171
+
172
+ this.#watchFileSet.adds(...Array.from(stylesheetResult.referencedFiles.values()).map(item => path.normalize(item)));
78
173
  }
79
174
 
80
- /*const markedChanges = this._markedChanges;
81
- this._markedChanges = [];
175
+ this.#stylesheetResultMap.set(path.normalize(resourceFile ?? containingFile), {
176
+ outputFiles: stylesheetResult.outputFiles,
177
+ metafile: stylesheetResult.metafile,
178
+ errors: stylesheetResult.errors,
179
+ warnings: stylesheetResult.warnings
180
+ });
82
181
 
83
- const distPath = path.resolve(this._opt.pkgPath, "dist");
84
- const srcFilePaths = await FsUtil.globAsync(path.resolve(this._opt.pkgPath, "src/!**!/!*.{ts,tsx}"));
85
- const srcFilePathSet = new Set<string>(srcFilePaths);
182
+ return stylesheetResult.contents;
183
+ }
86
184
 
87
- if (this._isForAngular) {
88
- this._ngProgram = new NgtscProgram(
89
- srcFilePaths,
90
- this._parsedTsConfig.options,
91
- this._compilerHost,
92
- this._ngProgram
93
- );
94
- this._program = this._ngProgram.getTsProgram();
185
+ invalidate(modifiedFileSet: Set<string>) {
186
+ this.#stylesheetBundler?.invalidate(modifiedFileSet);
95
187
 
96
- const baseGetSourceFiles = this._program.getSourceFiles;
97
- this._program.getSourceFiles = function (...parameters) {
98
- const files: readonly (ts.SourceFile & { version?: string })[] = baseGetSourceFiles(...parameters);
188
+ for (const modifiedFile of modifiedFileSet) {
189
+ this.#stylesheetResultMap.delete(path.normalize(modifiedFile));
190
+ this.#sourceFileCacheMap.delete(path.normalize(modifiedFile));
191
+ this.#emittedFilesCacheMap.delete(path.normalize(modifiedFile));
99
192
 
100
- for (const file of files) {
101
- if (file.version === undefined) {
102
- file.version = createHash("sha256").update(file.text).digest("hex");
103
- }
193
+ if (this.#resourceDependencyCacheMap.has(path.normalize(modifiedFile))) {
194
+ for (const referencingFile of this.#resourceDependencyCacheMap.get(path.normalize(modifiedFile))!) {
195
+ this.#stylesheetResultMap.delete(path.normalize(referencingFile));
196
+ this.#sourceFileCacheMap.delete(path.normalize(referencingFile));
197
+ this.#emittedFilesCacheMap.delete(path.normalize(referencingFile));
104
198
  }
199
+ }
200
+ }
105
201
 
106
- return files;
107
- };
202
+ this.#modifiedFileSet.adds(...modifiedFileSet);
203
+ }
108
204
 
109
- this._builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
110
- this._program,
111
- this._compilerHost,
112
- this._builder
205
+ async buildAsync(): Promise<ISdTsCompiler2Result> {
206
+ this.#resourceDependencyCacheMap.clear();
207
+ this.#watchFileSet.clear();
208
+ this.#stylesheetResultMap.clear();
209
+ this.#affectedFileSet.clear();
210
+
211
+ this.#debug(`create program...`);
212
+
213
+ if (this.#isForAngular) {
214
+ this.#ngProgram = new NgtscProgram(
215
+ this.#parsedTsconfig.fileNames,
216
+ this.#parsedTsconfig.options,
217
+ this.#compilerHost,
218
+ this.#ngProgram
113
219
  );
220
+ this.#program = this.#ngProgram.getTsProgram();
114
221
  }
115
222
  else {
116
- /!*this._program = ts.createProgram(
117
- srcFilePaths,
118
- this._parsedTsConfig.options,
119
- this._compilerHost,
120
- this._program
121
- );*!/
122
-
123
- this._builder = ts.createIncrementalProgram({
124
- rootNames: srcFilePaths,
125
- host: this._compilerHost,
126
- options: this._parsedTsConfig.options,
127
- createProgram: ts.createEmitAndSemanticDiagnosticsBuilderProgram
128
- });
129
- this._program = this._builder.getProgram();
223
+ // noinspection UnnecessaryLocalVariableJS
224
+ this.#program = ts.createProgram(
225
+ this.#parsedTsconfig.fileNames,
226
+ this.#parsedTsconfig.options,
227
+ this.#compilerHost,
228
+ this.#program
229
+ );
130
230
  }
131
231
 
132
- const diagnostics: ts.Diagnostic[] = [];
133
- const affectedFileSet = new Set<string>();
232
+ const baseGetSourceFiles = this.#program.getSourceFiles;
233
+ this.#program.getSourceFiles = function (...parameters) {
234
+ const files: readonly (ts.SourceFile & { version?: string })[] = baseGetSourceFiles(...parameters);
134
235
 
135
- if (this._ngProgram) {
136
- diagnostics.push(...this._ngProgram.compiler.getOptionDiagnostics());
137
- }
236
+ for (const file of files) {
237
+ if (file.version === undefined) {
238
+ file.version = createHash("sha256").update(file.text).digest("hex");
239
+ }
240
+ }
138
241
 
139
- diagnostics.push(
140
- ...this._builder.getOptionsDiagnostics(),
141
- ...this._builder.getGlobalDiagnostics()
242
+ return files;
243
+ };
244
+
245
+ this.#debug(`create builder...`);
246
+
247
+ this.#builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
248
+ this.#program,
249
+ this.#compilerHost,
250
+ this.#builder
142
251
  );
143
252
 
144
- if (this._ngProgram) {
145
- await this._ngProgram.compiler.analyzeAsync();
253
+ if (this.#ngProgram) {
254
+ await this.#ngProgram.compiler.analyzeAsync();
146
255
  }
147
256
 
148
- this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 확인중...`);
257
+ this.#debug(`get affected...`);
258
+
149
259
  while (true) {
150
- let affectedSourceFile: ts.SourceFile | undefined;
151
-
152
- const semanticResult = this._builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
153
- //-- ngtypecheck의 org파일 포함 (ngtypecheck 파일는 무시)
154
- if (this._ngProgram?.compiler.ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith(".ngtypecheck.ts")) {
155
- const orgFileName = sourceFile.fileName.slice(0, -15) + ".ts";
156
- const orgSourceFile = this._builder!.getSourceFile(orgFileName);
157
- if (orgSourceFile) {
158
- affectedSourceFile = orgSourceFile;
260
+ const result = this.#builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
261
+ if (
262
+ this.#ngProgram
263
+ && this.#ngProgram.compiler.ignoreForDiagnostics.has(sourceFile)
264
+ && sourceFile.fileName.endsWith('.ngtypecheck.ts')
265
+ ) {
266
+ const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
267
+ // const originalSourceFile = this.#sourceFileCacheMap.get(originalFilename);
268
+ const originalSourceFile = this.#builder!.getSourceFile(originalFilename);
269
+ if (originalSourceFile) {
270
+ this.#affectedFileSet.add(path.normalize(originalSourceFile.fileName));
159
271
  }
272
+
160
273
  return true;
161
274
  }
162
275
 
163
- //-- 소스폴더 파일 포함
164
- else if (srcFilePathSet.has(path.resolve(sourceFile.fileName))) {
165
- affectedSourceFile = sourceFile;
166
- return false;
167
- }
276
+ return false;
277
+ });
168
278
 
169
- //-- 나머지 무시
170
- else {
171
- return true;
279
+ if (!result) {
280
+ break;
281
+ }
282
+
283
+ this.#affectedFileSet.add(path.normalize((result.affected as ts.SourceFile).fileName));
284
+ }
285
+
286
+ this.#debug(`get resource ref...`);
287
+
288
+ this.#builder.getSourceFiles().filter(sf => !this.#ngProgram || !this.#ngProgram.compiler.ignoreForEmit.has(sf))
289
+ .forEach(sf => {
290
+ this.#watchFileSet.add(path.normalize(sf.fileName));
291
+
292
+ if (this.#ngProgram) {
293
+ const deps = this.#ngProgram.compiler.getResourceDependencies(sf);
294
+ for (const dep of deps) {
295
+ const ref = this.#resourceDependencyCacheMap.getOrCreate(path.normalize(dep), new Set<string>());
296
+ ref.add(path.normalize(sf.fileName));
297
+
298
+ this.#watchFileSet.add(path.normalize(dep));
299
+ }
172
300
  }
173
301
  });
174
- if (!semanticResult || !affectedSourceFile) break;
175
- diagnostics.push(...semanticResult.result);
176
302
 
177
- if ("fileName" in affectedSourceFile) {
178
- affectedFileSet.add(path.normalize(affectedSourceFile.fileName));
179
- }
303
+ this.#debug(`get diagnostics...`);
304
+
305
+ const diagnostics: ts.Diagnostic[] = [];
306
+
307
+ diagnostics.push(
308
+ ...this.#builder.getConfigFileParsingDiagnostics(),
309
+ ...this.#builder.getOptionsDiagnostics(),
310
+ ...this.#builder.getGlobalDiagnostics()
311
+ );
312
+
313
+ if (this.#ngProgram) {
314
+ diagnostics.push(...this.#ngProgram.compiler.getOptionDiagnostics());
180
315
  }
181
316
 
182
- if (this._isForAngular) {
183
- for (const markedChange of markedChanges) {
184
- const depsSet = this._styleDepsCache.get(markedChange);
185
- if (depsSet) {
186
- affectedFileSet.adds(...depsSet);
317
+ for (const affectedFile of this.#affectedFileSet) {
318
+ const affectedSourceFile = this.#sourceFileCacheMap.get(affectedFile);
319
+ if (!affectedSourceFile || (this.#ngProgram && this.#ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))) {
320
+ continue;
321
+ }
322
+
323
+ diagnostics.push(
324
+ ...this.#builder.getSyntacticDiagnostics(affectedSourceFile),
325
+ ...this.#builder.getSemanticDiagnostics(affectedSourceFile)
326
+ );
327
+
328
+ if (this.#ngProgram) {
329
+ if (affectedSourceFile.isDeclarationFile) {
330
+ continue;
187
331
  }
332
+
333
+ diagnostics.push(
334
+ ...this.#ngProgram.compiler.getDiagnosticsForFile(affectedSourceFile, OptimizeFor.WholeProgram),
335
+ );
188
336
  }
189
337
  }
190
338
 
191
- const globalStyleFilePath = path.resolve(this._opt.pkgPath, "src/styles.scss");
192
- if (this._opt.globalStyle && FsUtil.exists(globalStyleFilePath) && markedChanges.includes(globalStyleFilePath)) {
193
- affectedFileSet.add(globalStyleFilePath);
194
- }
339
+ this.#debug(`prepare emit...`);
195
340
 
196
- this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 ${this._opt.emit ? "EMIT" : "CHECK"}...`);
197
-
198
- for (const affectedFilePath of affectedFileSet) {
199
- if (this._opt.globalStyle && affectedFilePath === globalStyleFilePath) {
200
- try {
201
- const content = await FsUtil.readFileAsync(affectedFilePath);
202
- const scssResult = await sass.compileStringAsync(content, {
203
- url: new URL(affectedFilePath),
204
- importer: {
205
- findFileUrl: (url) => pathToFileURL(url)
206
- },
207
- logger: sass.Logger.silent
208
- });
341
+ while (true) {
342
+ const affectedFileResult = this.#builder.emitNextAffectedFile((fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
343
+ if (!sourceFiles || sourceFiles.length === 0) {
344
+ this.#compilerHost.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
345
+ return;
346
+ }
209
347
 
210
- const deps = scssResult.loadedUrls.slice(1).map((item) => path.resolve(fileURLToPath(item.href)));
211
- for (const dep of deps) {
212
- const depCache = this._styleDepsCache.getOrCreate(dep, new Set<string>());
213
- depCache.add(affectedFilePath);
214
- }
348
+ const sourceFile = ts.getOriginalNode(sourceFiles[0], ts.isSourceFile);
215
349
 
216
- if (this._opt.emit) {
217
- const outFilePath = path.resolve(this._opt.pkgPath, path.basename(affectedFilePath, path.extname(affectedFilePath)) + ".css");
350
+ if (this.#ngProgram) {
351
+ if (this.#ngProgram.compiler.ignoreForEmit.has(sourceFile)) {
352
+ return;
353
+ }
354
+ this.#ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
355
+ }
218
356
 
219
- this._writeFile(outFilePath, scssResult.css.toString());
357
+ const emittedFiles = this.#emittedFilesCacheMap.getOrCreate(path.normalize(sourceFile.fileName), []);
358
+ if (PathUtil.isChildPath(sourceFile.fileName, this.#pkgPath)) {
359
+ let realFilePath = fileName;
360
+ let realText = text;
361
+ if (PathUtil.isChildPath(realFilePath, path.resolve(this.#distPath, path.basename(this.#pkgPath), "src"))) {
362
+ realFilePath = path.resolve(this.#distPath, path.relative(path.resolve(this.#distPath, path.basename(this.#pkgPath), "src"), realFilePath));
363
+
364
+ if (fileName.endsWith(".js.map")) {
365
+ const sourceMapContents = JSON.parse(realText);
366
+ // remove "../../"
367
+ sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6);
368
+ realText = JSON.stringify(sourceMapContents);
369
+ }
220
370
  }
371
+
372
+ emittedFiles.push({
373
+ outRelPath: path.relative(this.#distPath, realFilePath),
374
+ text: realText
375
+ });
221
376
  }
222
- catch (err) {
223
- this._logger.error(err);
377
+ else {
378
+ emittedFiles.push({text});
224
379
  }
380
+ }, undefined, undefined, this.#ngProgram?.compiler.prepareEmit().transformers);
381
+
382
+ if (!affectedFileResult) {
383
+ break;
225
384
  }
226
- else {
227
- const affectedSourceFile = this._builder.getSourceFile(affectedFilePath);
228
- if (!affectedSourceFile) continue;
229
-
230
- const emitResult = this._builder.emit(affectedSourceFile,
231
- (filePath, data) => {
232
- let realFilePath = filePath;
233
- let realData = data;
234
- if (PathUtil.isChildPath(realFilePath, path.resolve(distPath, path.basename(this._opt.pkgPath), "src"))) {
235
- realFilePath = path.resolve(distPath, path.relative(path.resolve(distPath, path.basename(this._opt.pkgPath), "src"), realFilePath));
236
-
237
- if (filePath.endsWith(".js.map")) {
238
- const sourceMapContents = JSON.parse(realData);
239
- // remove "../../"
240
- sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6);
241
- realData = JSON.stringify(sourceMapContents);
242
- }
243
- }
244
385
 
245
- this._ngProgram?.compiler.incrementalCompilation.recordSuccessfulEmit(affectedSourceFile);
246
- this._writeFile(realFilePath, realData);
247
- },
248
- undefined,
249
- !this._opt.emit,
250
- {...this._ngProgram?.compiler.prepareEmit().transformers ?? {}}
251
- );
386
+ diagnostics.push(...affectedFileResult.result.diagnostics);
387
+ }
252
388
 
253
- diagnostics.push(...emitResult.diagnostics);
389
+ //-- global style
390
+ if (
391
+ this.#globalStyleFilePath != null
392
+ && !this.#stylesheetResultMap.has(this.#globalStyleFilePath)
393
+ && FsUtil.exists(this.#globalStyleFilePath)
394
+ ) {
395
+ this.#debug(`bundle global style...`);
396
+
397
+ const data = await FsUtil.readFileAsync(this.#globalStyleFilePath);
398
+ const contents = await this.#bundleStylesheetAsync(data, this.#globalStyleFilePath, this.#globalStyleFilePath);
399
+ const emittedFiles = this.#emittedFilesCacheMap.getOrCreate(path.normalize(this.#globalStyleFilePath), []);
400
+ emittedFiles.push({
401
+ outRelPath: path.relative(path.resolve(this.#pkgPath, "src"), this.#globalStyleFilePath).replace(/\.scss$/, ".css"),
402
+ text: contents
403
+ });
404
+ this.#affectedFileSet.add(this.#globalStyleFilePath);
405
+ }
254
406
 
255
- diagnostics.push(...this._builder.getSyntacticDiagnostics(affectedSourceFile));
407
+ //-- init
256
408
 
257
- if (
258
- this._ngProgram &&
259
- !affectedSourceFile.isDeclarationFile &&
260
- !this._ngProgram.compiler.ignoreForEmit.has(affectedSourceFile) &&
261
- !this._ngProgram.compiler.incrementalCompilation.safeToSkipEmit(affectedSourceFile)
262
- ) {
263
- diagnostics.push(
264
- ...this._ngProgram.compiler.getDiagnosticsForFile(affectedSourceFile, OptimizeFor.WholeProgram)
265
- );
266
- }
267
- }
268
- }
409
+ this.#modifiedFileSet.clear();
269
410
 
270
- this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 ${this._opt.emit ? "EMIT" : "CHECK"} 완료`, affectedFileSet);
411
+ this.#debug(`build completed`);
271
412
 
272
- const buildResults = diagnostics.map((item) => SdCliBuildResultUtil.convertFromTsDiag(item, this._opt.emit ? "build" : "check"));*/
413
+ //-- result
273
414
 
274
415
  return {
275
- watchFileSet: buildResult.watchFileSet,
276
- affectedFileSet: buildResult.affectedFileSet,
277
- results: [
278
- ...buildResult.typescriptDiagnostics.map((item) => SdCliBuildResultUtil.convertFromTsDiag(item, "build")),
279
- ...Array.from(buildResult.stylesheetResultMap.values()).mapMany(item => item.errors!)
280
- .map(err => SdCliBuildResultUtil.convertFromEsbuildResult(err, "build", "error")),
281
- /*...Array.from(buildResult.stylesheetResultMap.values()).mapMany(item => item.warnings!)
282
- .map(warn => SdCliBuildResultUtil.convertFromEsbuildResult(warn, "build", "warning"))*/
283
- ]
416
+ program: this.#program,
417
+ typescriptDiagnostics: diagnostics,
418
+ stylesheetResultMap: this.#stylesheetResultMap,
419
+ emittedFilesCacheMap: this.#emittedFilesCacheMap,
420
+ watchFileSet: this.#watchFileSet,
421
+ affectedFileSet: this.#affectedFileSet
284
422
  };
285
423
  }
424
+
425
+ #debug(...msg: any[]): void {
426
+ this.#logger.debug(`[${path.basename(this.#pkgPath)}]`, ...msg);
427
+ }
286
428
  }
429
+
430
+ export interface ISdTsCompiler2Result {
431
+ program: ts.Program,
432
+ typescriptDiagnostics: ts.Diagnostic[],
433
+ stylesheetResultMap: Map<string, IStylesheetResult>,
434
+ emittedFilesCacheMap: Map<string, {
435
+ outRelPath?: string;
436
+ text: string;
437
+ }[]>,
438
+ watchFileSet: Set<string>,
439
+ affectedFileSet: Set<string>
440
+ }
441
+
442
+ interface IStylesheetResult {
443
+ outputFiles: esbuild.OutputFile[];
444
+ metafile?: esbuild.Metafile;
445
+ errors?: esbuild.PartialMessage[];
446
+ warnings?: esbuild.PartialMessage[];
447
+ }