@simplysm/sd-cli 11.1.46 → 11.1.51

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