@simplysm/sd-cli 12.8.18 → 12.8.21

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 (37) hide show
  1. package/dist/entry/sd-cli-cordova.d.ts +30 -0
  2. package/dist/entry/sd-cli-cordova.js +309 -249
  3. package/dist/entry/sd-cli-cordova.js.map +1 -1
  4. package/dist/entry/sd-cli-project.d.ts +1 -1
  5. package/dist/entry/sd-cli-project.js +8 -9
  6. package/dist/entry/sd-cli-project.js.map +1 -1
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/pkg-builders/client/sd-ng.bundler.d.ts +15 -1
  11. package/dist/pkg-builders/client/sd-ng.bundler.js +60 -70
  12. package/dist/pkg-builders/client/sd-ng.bundler.js.map +1 -1
  13. package/dist/pkg-builders/client/sd-ng.plugin-creator.js +49 -29
  14. package/dist/pkg-builders/client/sd-ng.plugin-creator.js.map +1 -1
  15. package/dist/pkg-builders/lib/sd-ts-lib.builder.js +7 -4
  16. package/dist/pkg-builders/lib/sd-ts-lib.builder.js.map +1 -1
  17. package/dist/ts-compiler/sd-ts-compiler.d.ts +24 -2
  18. package/dist/ts-compiler/sd-ts-compiler.js +267 -575
  19. package/dist/ts-compiler/sd-ts-compiler.js.map +1 -1
  20. package/dist/ts-compiler/sd-ts-dependency-analyzer.d.ts +10 -0
  21. package/dist/ts-compiler/sd-ts-dependency-analyzer.js +140 -0
  22. package/dist/ts-compiler/sd-ts-dependency-analyzer.js.map +1 -0
  23. package/dist/types/ts-compiler.types.d.ts +12 -7
  24. package/dist/types/worker.types.d.ts +13 -0
  25. package/dist/utils/sd-cli-performance-time.js +1 -1
  26. package/package.json +10 -12
  27. package/src/entry/sd-cli-cordova.ts +394 -281
  28. package/src/entry/sd-cli-project.ts +11 -23
  29. package/src/index.ts +1 -0
  30. package/src/pkg-builders/client/sd-ng.bundler.ts +70 -69
  31. package/src/pkg-builders/client/sd-ng.plugin-creator.ts +47 -27
  32. package/src/pkg-builders/lib/sd-ts-lib.builder.ts +14 -7
  33. package/src/ts-compiler/sd-ts-compiler.ts +334 -705
  34. package/src/ts-compiler/sd-ts-dependency-analyzer.ts +176 -0
  35. package/src/types/ts-compiler.types.ts +11 -6
  36. package/src/types/worker.types.ts +7 -6
  37. package/src/utils/sd-cli-performance-time.ts +1 -1
@@ -16,26 +16,24 @@ import { SdCliPerformanceTimer } from "../utils/sd-cli-performance-time";
16
16
  import { SdCliConvertMessageUtils } from "../utils/sd-cli-convert-message.utils";
17
17
  import {
18
18
  ISdTsCompilerResult,
19
- IStylesheetBundlingResult,
20
19
  SdTsCompilerOptions,
20
+ TStylesheetBundlingResult,
21
21
  } from "../types/ts-compiler.types";
22
22
  import { ISdBuildMessage } from "../types/build.types";
23
23
  import {
24
24
  createWorkerTransformer,
25
25
  } from "@angular/build/src/tools/angular/transformers/web-worker-transformer";
26
26
  import { ESLint } from "eslint";
27
+ import { SdTsDependencyAnalyzer } from "./sd-ts-dependency-analyzer";
27
28
 
28
29
  export class SdTsCompiler {
29
- readonly #logger = SdLogger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
30
+ private _logger = SdLogger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
30
31
 
31
- readonly #parsedTsconfig: ts.ParsedCommandLine;
32
- readonly #isForAngular: boolean;
32
+ private _isForAngular: boolean;
33
33
 
34
- // readonly #workerRevDependencyCacheMap = new Map<TNormPath, Set<TNormPath>>();
35
- readonly #revDependencyCacheMap = new Map<TNormPath, Set<TNormPath>>();
36
- readonly #resourceDependencyCacheMap = new Map<TNormPath, Set<TNormPath>>();
37
- readonly #sourceFileCacheMap = new Map<TNormPath, ts.SourceFile>();
38
- readonly #emittedFilesCacheMap = new Map<
34
+ private _revDepCacheMap = new Map<TNormPath, Set<TNormPath>>();
35
+ private _sourceFileCacheMap = new Map<TNormPath, ts.SourceFile>();
36
+ private _emittedFilesCacheMap = new Map<
39
37
  TNormPath,
40
38
  {
41
39
  outAbsPath?: TNormPath;
@@ -43,73 +41,103 @@ export class SdTsCompiler {
43
41
  }[]
44
42
  >();
45
43
 
46
- readonly #stylesheetBundler: ComponentStylesheetBundler | undefined;
47
- readonly #compilerHost: ts.CompilerHost | AngularCompilerHost;
44
+ private _stylesheetBundler: ComponentStylesheetBundler | undefined;
48
45
 
49
- #ngProgram: NgtscProgram | undefined;
50
- #program: ts.Program | undefined;
46
+ private _ngProgram: NgtscProgram | undefined;
47
+ private _program: ts.Program | undefined;
51
48
 
52
- #modifiedFileSet = new Set<TNormPath>();
53
- #affectedFileSet = new Set<TNormPath>();
49
+ private _modifiedFileSet = new Set<TNormPath>();
50
+ private _affectedFileSet = new Set<TNormPath>();
54
51
 
55
- readonly #watchFileSet = new Set<TNormPath>();
56
- readonly #stylesheetBundlingResultMap = new Map<TNormPath, IStylesheetBundlingResult>();
52
+ private _watchFileSet = new Set<TNormPath>();
53
+ private _stylesheetBundlingResultMap = new Map<TNormPath, TStylesheetBundlingResult>();
57
54
 
58
- readonly #pkgPath: TNormPath;
59
- readonly #distPath: TNormPath;
60
- readonly #globalStyleFilePath?: TNormPath;
61
- readonly #watchScopePaths: TNormPath[];
55
+ private _perf!: SdCliPerformanceTimer;
62
56
 
63
- readonly #isForBundle: boolean;
57
+ constructor(private readonly _opt: SdTsCompilerOptions) {
58
+ this._debug("초기화 중...");
64
59
 
65
- // readonly #lintWorker = new SdWorker<TSdLintWorkerType>(import.meta.resolve("../workers/lint-worker"));
66
-
67
- #perf!: SdCliPerformanceTimer;
68
-
69
- // #processWebWorker?: (workerFile: string, containingFile: string) => string;
70
-
71
- constructor(opt: SdTsCompilerOptions) {
72
- this.#pkgPath = opt.pkgPath;
73
- this.#globalStyleFilePath = opt.globalStyleFilePath;
74
- this.#isForBundle = opt.isForBundle;
75
- this.#watchScopePaths = opt.watchScopePaths;
76
- // this.#processWebWorker = opt.processWebWorker;
60
+ const tsconfigPath = path.resolve(this._opt.pkgPath, "tsconfig.json");
61
+ const tsconfig = FsUtils.readJson(tsconfigPath);
62
+ this._isForAngular = Boolean(tsconfig.angularCompilerOptions);
77
63
 
78
- this.#debug("초기화...");
64
+ if (this._isForAngular) {
65
+ //-- stylesheetBundler
66
+ this._stylesheetBundler = new ComponentStylesheetBundler(
67
+ {
68
+ workspaceRoot: this._opt.pkgPath,
69
+ optimization: !this._opt.isDevMode,
70
+ inlineFonts: true,
71
+ preserveSymlinks: false,
72
+ sourcemap: this._opt.isDevMode ? "inline" : false,
73
+ outputNames: { bundles: "[name]", media: "media/[name]" },
74
+ includePaths: [],
75
+ externalDependencies: [],
76
+ target: transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"])),
77
+ postcssConfiguration: {
78
+ plugins: [["css-has-pseudo"]],
79
+ },
80
+ tailwindConfiguration: undefined,
81
+ cacheOptions: {
82
+ enabled: true,
83
+ path: ".cache/angular",
84
+ basePath: ".cache",
85
+ },
86
+ },
87
+ "scss",
88
+ this._opt.isDevMode,
89
+ );
90
+ }
91
+ }
79
92
 
80
- //-- isForAngular / parsedTsConfig
93
+ private _parseTsConfig(): ITsConfigInfo {
94
+ const config = this._loadTsConfig();
95
+ const compilerHost = this._createCompilerHost(config.options);
96
+ return {
97
+ ...config,
98
+ compilerHost,
99
+ };
100
+ }
81
101
 
82
- const tsconfigPath = path.resolve(opt.pkgPath, "tsconfig.json");
102
+ private _loadTsConfig() {
103
+ const tsconfigPath = path.resolve(this._opt.pkgPath, "tsconfig.json");
83
104
  const tsconfig = FsUtils.readJson(tsconfigPath);
84
- this.#isForAngular = Boolean(tsconfig.angularCompilerOptions);
85
- this.#parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, opt.pkgPath, {
105
+ const parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._opt.pkgPath, {
86
106
  ...tsconfig.angularCompilerOptions,
87
- ...opt.additionalOptions,
107
+ ...this._opt.additionalOptions,
88
108
  });
89
109
 
90
- this.#distPath = PathUtils.norm(this.#parsedTsconfig.options.outDir ?? path.resolve(
91
- opt.pkgPath,
110
+ const distPath = PathUtils.norm(parsedTsconfig.options.outDir ?? path.resolve(
111
+ this._opt.pkgPath,
92
112
  "dist",
93
113
  ));
94
114
 
95
- //-- compilerHost
115
+ return {
116
+ fileNames: parsedTsconfig.fileNames,
117
+ options: parsedTsconfig.options,
118
+ distPath: distPath,
119
+ };
120
+ }
96
121
 
97
- this.#compilerHost = ts.createCompilerHost(this.#parsedTsconfig.options);
122
+ private _createCompilerHost(compilerOptions: ts.CompilerOptions) {
123
+ const compilerHost = ts.createCompilerHost(compilerOptions);
98
124
 
99
- const baseGetSourceFile = this.#compilerHost.getSourceFile;
100
- this.#compilerHost.getSourceFile = (
125
+ const baseGetSourceFile = compilerHost.getSourceFile;
126
+ compilerHost.getSourceFile = (
101
127
  fileName: string,
102
128
  languageVersionOrOptions: ts.ScriptTarget | ts.CreateSourceFileOptions,
103
129
  onError?: ((message: string) => void) | undefined,
104
130
  shouldCreateNewSourceFile?: boolean,
105
131
  ...args
106
132
  ): ts.SourceFile | undefined => {
107
- if (!shouldCreateNewSourceFile && this.#sourceFileCacheMap.has(PathUtils.norm(fileName))) {
108
- return this.#sourceFileCacheMap.get(PathUtils.norm(fileName));
133
+ const fileNPath = PathUtils.norm(fileName);
134
+
135
+ if (!shouldCreateNewSourceFile && this._sourceFileCacheMap.has(fileNPath)) {
136
+ return this._sourceFileCacheMap.get(fileNPath);
109
137
  }
110
138
 
111
139
  const sf: ts.SourceFile | undefined = baseGetSourceFile.call(
112
- this.#compilerHost,
140
+ compilerHost,
113
141
  fileName,
114
142
  languageVersionOrOptions,
115
143
  onError,
@@ -118,55 +146,27 @@ export class SdTsCompiler {
118
146
  );
119
147
 
120
148
  if (sf) {
121
- this.#sourceFileCacheMap.set(PathUtils.norm(fileName), sf);
149
+ this._sourceFileCacheMap.set(fileNPath, sf);
122
150
  }
123
151
  else {
124
- this.#sourceFileCacheMap.delete(PathUtils.norm(fileName));
152
+ this._sourceFileCacheMap.delete(fileNPath);
125
153
  }
126
154
 
127
155
  return sf;
128
156
  };
129
157
 
130
- const baseReadFile = this.#compilerHost.readFile;
131
- this.#compilerHost.readFile = (fileName) => {
132
- this.#watchFileSet.add(PathUtils.norm(fileName));
133
- return baseReadFile.call(this.#compilerHost, fileName);
158
+ const baseReadFile = compilerHost.readFile;
159
+ compilerHost.readFile = (fileName) => {
160
+ this._watchFileSet.add(PathUtils.norm(fileName));
161
+ return baseReadFile.call(compilerHost, fileName);
134
162
  };
135
163
 
136
- if (this.#isForAngular) {
137
- //-- stylesheetBundler
138
- this.#stylesheetBundler = new ComponentStylesheetBundler(
139
- {
140
- workspaceRoot: opt.pkgPath,
141
- optimization: !opt.isDevMode,
142
- inlineFonts: true,
143
- preserveSymlinks: false,
144
- sourcemap: opt.isDevMode ? "inline" : false,
145
- outputNames: { bundles: "[name]", media: "media/[name]" },
146
- includePaths: [],
147
- externalDependencies: [],
148
- target: transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"])),
149
- postcssConfiguration: {
150
- plugins: [["css-has-pseudo"]],
151
- },
152
- tailwindConfiguration: undefined,
153
- cacheOptions: {
154
- enabled: true,
155
- path: ".cache/angular",
156
- basePath: ".cache",
157
- },
158
- },
159
- "scss",
160
- opt.isDevMode,
161
- );
162
-
163
- //-- compilerHost
164
-
165
- (this.#compilerHost as AngularCompilerHost).readResource = (fileName: string) => {
166
- return this.#compilerHost.readFile(fileName) ?? "";
164
+ if (this._isForAngular) {
165
+ (compilerHost as AngularCompilerHost).readResource = (fileName: string) => {
166
+ return compilerHost.readFile(fileName) ?? "";
167
167
  };
168
168
 
169
- (this.#compilerHost as AngularCompilerHost).transformResource = async (
169
+ (compilerHost as AngularCompilerHost).transformResource = async (
170
170
  data: string,
171
171
  context: {
172
172
  type: string;
@@ -178,69 +178,88 @@ export class SdTsCompiler {
178
178
  return null;
179
179
  }
180
180
 
181
- const contents = await this.#bundleStylesheetAsync(
181
+ const stylesheetBundlingResult = await this._bundleStylesheetAsync(
182
182
  data,
183
183
  PathUtils.norm(context.containingFile),
184
184
  context.resourceFile != null ? PathUtils.norm(context.resourceFile) : undefined,
185
185
  );
186
186
 
187
- return StringUtils.isNullOrEmpty(contents) ? null : { content: contents };
187
+ return StringUtils.isNullOrEmpty(stylesheetBundlingResult.contents)
188
+ ? null
189
+ : { content: stylesheetBundlingResult.contents };
188
190
  };
189
191
 
190
- (this.#compilerHost as AngularCompilerHost).getModifiedResourceFiles = () => {
191
- return new Set(Array.from(this.#modifiedFileSet).map((item) => PathUtils.posix(item)));
192
+ (compilerHost as AngularCompilerHost).getModifiedResourceFiles = () => {
193
+ return new Set(Array.from(this._modifiedFileSet).map((item) => PathUtils.posix(item)));
192
194
  };
193
195
  }
196
+
197
+ return compilerHost;
194
198
  }
195
199
 
196
- async #bundleStylesheetAsync(
200
+ private async _bundleStylesheetAsync(
197
201
  data: string,
198
202
  containingFile: TNormPath,
199
203
  resourceFile: TNormPath | null = null,
200
- ) {
204
+ ): Promise<TStylesheetBundlingResult> {
201
205
  // containingFile: 포함된 파일 (.ts)
202
206
  // resourceFile: 외부 리소스 파일 (styleUrls로 입력하지 않고 styles에 직접 입력한 경우 null)
203
207
  // referencedFiles: import한 외부 scss 파일 혹은 woff파일등 외부 파일
204
208
 
205
209
  // this.#debug(`bundle stylesheet...(${containingFile}, ${resourceFile})`);
206
210
 
207
- return await this.#perf.run("bundle style", async () => {
208
- const stylesheetResult =
209
- resourceFile != null
210
- ? await this.#stylesheetBundler!.bundleFile(resourceFile)
211
- : await this.#stylesheetBundler!.bundleInline(data, containingFile, "scss");
212
-
213
- if (stylesheetResult.referencedFiles) {
214
- for (const referencedFile of stylesheetResult.referencedFiles) {
215
- const depCacheSet = this.#resourceDependencyCacheMap.getOrCreate(
216
- PathUtils.norm(referencedFile),
217
- new Set<TNormPath>(),
211
+ return await this._perf.run("스타일 번들링", async () => {
212
+ const fileNPath = PathUtils.norm(resourceFile ?? containingFile);
213
+ if (this._stylesheetBundlingResultMap.has(fileNPath)) {
214
+ return this._stylesheetBundlingResultMap.get(fileNPath)!;
215
+ }
216
+
217
+ try {
218
+ const result =
219
+ resourceFile != null
220
+ ? await this._stylesheetBundler!.bundleFile(resourceFile)
221
+ : await this._stylesheetBundler!.bundleInline(data, containingFile, "scss");
222
+
223
+ if (result.referencedFiles) {
224
+ for (const referencedFile of result.referencedFiles) {
225
+ const depCacheSet = this._revDepCacheMap.getOrCreate(
226
+ PathUtils.norm(referencedFile),
227
+ new Set<TNormPath>(),
228
+ );
229
+ depCacheSet.add(fileNPath);
230
+ }
231
+
232
+ this._watchFileSet.adds(
233
+ ...Array.from(result.referencedFiles.values())
234
+ .map((item) => PathUtils.norm(item)),
218
235
  );
219
- depCacheSet.add(resourceFile ?? containingFile);
220
236
  }
221
237
 
222
- this.#watchFileSet.adds(
223
- ...Array.from(stylesheetResult.referencedFiles.values())
224
- .map((item) => PathUtils.norm(item)),
225
- );
226
- }
227
-
228
- this.#stylesheetBundlingResultMap.set(PathUtils.norm(resourceFile ?? containingFile), {
229
- outputFiles: stylesheetResult.errors == null ? stylesheetResult.outputFiles : undefined,
230
- metafile: stylesheetResult.errors == null ? stylesheetResult.metafile : undefined,
231
- errors: stylesheetResult.errors,
232
- warnings: stylesheetResult.warnings,
233
- });
238
+ this._stylesheetBundlingResultMap.set(fileNPath, result);
234
239
 
235
- return stylesheetResult.contents;
240
+ return result;
241
+ }
242
+ catch (err) {
243
+ const result = {
244
+ errors: [
245
+ {
246
+ text: `스타일 번들링 실패: ${err.message ?? "알 수 없는 오류"}`,
247
+ location: { file: containingFile },
248
+ },
249
+ ],
250
+ warnings: [],
251
+ };
252
+ this._stylesheetBundlingResultMap.set(fileNPath, result);
253
+ return result;
254
+ }
236
255
  });
237
256
  }
238
257
 
239
- async compileAsync(modifiedFileSet: Set<TNormPath>): Promise<ISdTsCompilerResult> {
240
- this.#perf = new SdCliPerformanceTimer("esbuild compile");
258
+ public async compileAsync(modifiedFileSet: Set<TNormPath>): Promise<ISdTsCompilerResult> {
259
+ this._perf = new SdCliPerformanceTimer("esbuild compile");
241
260
 
242
- this.#modifiedFileSet = new Set(modifiedFileSet);
243
- this.#affectedFileSet = new Set<TNormPath>();
261
+ this._modifiedFileSet = new Set(modifiedFileSet);
262
+ this._affectedFileSet = new Set<TNormPath>();
244
263
 
245
264
  /*for (const mod of modifiedFileSet) {
246
265
  const workerImporters = this.#workerRevDependencyCacheMap.get(mod);
@@ -251,11 +270,18 @@ export class SdTsCompiler {
251
270
  }
252
271
  }*/
253
272
 
254
- const prepareResult = await this.#prepareAsync();
273
+ const tsconf = this._parseTsConfig();
255
274
 
256
- const [buildResult, lintResults] = await Promise.all([this.#buildAsync(), this.#lintAsync()]);
275
+ const prepareResult = await this._prepareAsync(tsconf);
257
276
 
258
- this.#debug(`build completed`, this.#perf.toString());
277
+ const [buildResult, lintResults] = await Promise.all([
278
+ this._buildAsync(tsconf),
279
+ this._lintAsync(),
280
+ ]);
281
+
282
+ this._debug(`빌드 완료됨`, this._perf.toString());
283
+ this._debug(`영향 받은 파일: ${this._affectedFileSet.size}개`);
284
+ this._debug(`감시 중인 파일: ${this._watchFileSet.size}개`);
259
285
 
260
286
  return {
261
287
  messages: [
@@ -263,210 +289,147 @@ export class SdTsCompiler {
263
289
  ...SdCliConvertMessageUtils.convertToBuildMessagesFromTsDiag(buildResult.diagnostics),
264
290
  ...SdCliConvertMessageUtils.convertToBuildMessagesFromEslint(lintResults),
265
291
  ],
266
- watchFileSet: this.#watchFileSet,
267
- affectedFileSet: this.#affectedFileSet,
268
- stylesheetBundlingResultMap: this.#stylesheetBundlingResultMap,
269
- emittedFilesCacheMap: this.#emittedFilesCacheMap,
292
+ watchFileSet: this._watchFileSet,
293
+ affectedFileSet: this._affectedFileSet,
294
+ stylesheetBundlingResultMap: this._stylesheetBundlingResultMap,
295
+ emittedFilesCacheMap: this._emittedFilesCacheMap,
270
296
  emitFileSet: buildResult.emitFileSet,
271
297
  };
272
298
  }
273
299
 
274
- async #prepareAsync() {
275
- if (this.#modifiedFileSet.size !== 0) {
276
- this.#debug(`get affected (old deps & old res deps)...`);
300
+ private async _prepareAsync(tsconf: ITsConfigInfo) {
301
+ if (this._modifiedFileSet.size !== 0) {
302
+ this._debug(`영향 받은 파일 추적 중... (구 의존성)`);
303
+
304
+ this._perf.run("영향 받은 파일 추적", () => {
305
+ for (const modifiedFile of this._modifiedFileSet) {
306
+ this._affectedFileSet.add(modifiedFile);
307
+
308
+ this._affectedFileSet.adds(...(this._revDepCacheMap.get(modifiedFile) ?? []));
277
309
 
278
- this.#perf.run("get affected", () => {
279
- for (const modifiedFile of this.#modifiedFileSet) {
280
- this.#affectedFileSet.add(modifiedFile);
281
- this.#affectedFileSet.adds(...(this.#revDependencyCacheMap.get(modifiedFile) ?? []));
282
- this.#affectedFileSet.adds(...(this.#resourceDependencyCacheMap.get(modifiedFile) ?? []));
310
+ // .d.ts .js 대응
311
+ if (modifiedFile.endsWith(".d.ts")) {
312
+ const jsEquivalent = PathUtils.norm(modifiedFile.replace(/\.d\.ts$/, ".js"));
313
+ this._affectedFileSet.add(jsEquivalent);
314
+ }
283
315
  }
284
316
  });
285
317
 
286
- this.#debug(`invalidate & clear cache...`);
318
+ this._debug(`캐시 무효화 초기화 중...`);
287
319
 
288
- this.#perf.run("invalidate & clear cache", () => {
289
- this.#stylesheetBundler?.invalidate(this.#affectedFileSet);
320
+ this._perf.run("캐시 무효화 초기화", () => {
321
+ this._stylesheetBundler?.invalidate(this._affectedFileSet);
290
322
 
291
- for (const affectedFile of this.#affectedFileSet) {
292
- this.#emittedFilesCacheMap.delete(affectedFile);
293
- this.#sourceFileCacheMap.delete(affectedFile);
294
- this.#stylesheetBundlingResultMap.delete(affectedFile);
295
- this.#watchFileSet.delete(affectedFile);
296
- }
323
+ for (const affectedFile of this._affectedFileSet) {
324
+ this._emittedFilesCacheMap.delete(affectedFile);
325
+ this._sourceFileCacheMap.delete(affectedFile);
326
+ this._stylesheetBundlingResultMap.delete(affectedFile);
327
+ this._watchFileSet.delete(affectedFile);
328
+
329
+ for (const [key, deps] of this._revDepCacheMap.entries()) {
330
+ if (key === affectedFile) {
331
+ this._revDepCacheMap.delete(key);
332
+ continue;
333
+ }
297
334
 
298
- this.#revDependencyCacheMap.clear();
299
- this.#resourceDependencyCacheMap.clear();
335
+ if (deps.has(affectedFile)) {
336
+ deps.delete(affectedFile);
337
+ if (deps.size === 0) {
338
+ this._revDepCacheMap.delete(key);
339
+ }
340
+ }
341
+ }
342
+ }
300
343
  });
301
344
  }
302
345
 
303
- this.#debug(`create program...`);
346
+ this._debug(`ts.Program 생성 중...`);
304
347
 
305
- this.#perf.run("create program", () => {
306
- if (this.#isForAngular) {
307
- this.#ngProgram = new NgtscProgram(
308
- this.#parsedTsconfig.fileNames,
309
- this.#parsedTsconfig.options,
310
- this.#compilerHost,
311
- this.#ngProgram,
348
+ this._perf.run("ts.Program 생성", () => {
349
+ if (this._isForAngular) {
350
+ this._ngProgram = new NgtscProgram(
351
+ tsconf.fileNames,
352
+ tsconf.options,
353
+ tsconf.compilerHost,
354
+ this._ngProgram,
312
355
  );
313
- this.#program = this.#ngProgram.getTsProgram();
356
+ this._program = this._ngProgram.getTsProgram();
314
357
  }
315
358
  else {
316
- this.#program = ts.createProgram(
317
- this.#parsedTsconfig.fileNames,
318
- this.#parsedTsconfig.options,
319
- this.#compilerHost,
320
- this.#program,
359
+ this._program = ts.createProgram(
360
+ tsconf.fileNames,
361
+ tsconf.options,
362
+ tsconf.compilerHost,
363
+ this._program,
321
364
  );
322
365
  }
323
366
  });
324
367
 
325
- if (this.#ngProgram) {
326
- await this.#perf.run("ng analyze", async () => {
327
- await this.#ngProgram!.compiler.analyzeAsync();
368
+ if (this._ngProgram) {
369
+ await this._perf.run("Angular 템플릿 분석", async () => {
370
+ await this._ngProgram!.compiler.analyzeAsync();
328
371
  });
329
372
  }
330
373
 
331
- const getOrgSourceFile = (sf: ts.SourceFile) => {
332
- if (sf.fileName.endsWith(".ngtypecheck.ts")) {
333
- const orgFileName = sf.fileName.slice(0, -15) + ".ts";
334
- return this.#program!.getSourceFile(orgFileName);
335
- }
336
-
337
- return sf;
338
- };
339
-
340
- const sourceFileSet = new Set(
341
- this.#program!.getSourceFiles()
342
- .map((sf) => getOrgSourceFile(sf))
343
- .filterExists(),
344
- );
345
-
346
- this.#debug(`get new deps...`);
374
+ this._debug(`새 의존성 분석 중...`);
347
375
 
348
376
  const messages: ISdBuildMessage[] = [];
377
+ const analysed1 = this._perf.run("새 의존성 분석", () => {
378
+ const analysed = SdTsDependencyAnalyzer.analyze(
379
+ this._program!,
380
+ tsconf.compilerHost,
381
+ this._opt.watchScopePaths,
382
+ );
383
+ messages.push(...analysed.messages);
349
384
 
350
- this.#perf.run("get new deps", () => {
351
- const depMap = new Map<
352
- TNormPath,
353
- {
354
- fileName: TNormPath;
355
- importName: string;
356
- exportName?: string;
357
- }[]
358
- >();
359
- for (const sf of sourceFileSet) {
360
- if (!this.#watchScopePaths.some((scopePath) => PathUtils.isChildPath(
361
- sf.fileName,
362
- scopePath,
363
- ))) {
364
- continue;
365
- }
366
-
367
- const refs = this.#findDeps(sf);
368
- messages.push(...refs.filter((item) => "severity" in item));
369
- depMap.set(
370
- PathUtils.norm(sf.fileName),
371
- refs
372
- .filter((item) => "fileName" in item)
373
- .filter((item) =>
374
- this.#watchScopePaths.some((scopePath) => PathUtils.isChildPath(
375
- item.fileName,
376
- scopePath,
377
- )),
378
- ),
379
- );
380
- }
381
-
382
- const allDepMap = new Map<TNormPath, Set<TNormPath>>();
383
- const getAllDeps = (fileName: TNormPath, prevSet?: Set<TNormPath>) => {
384
- if (allDepMap.has(fileName)) {
385
- return allDepMap.get(fileName)!;
386
- }
387
-
388
- const result = new Set<TNormPath>();
389
-
390
- const deps = depMap.get(fileName) ?? [];
391
- result.adds(...deps.map((item) => item.fileName));
385
+ for (const sf of analysed.sourceFileSet) {
386
+ const filePath = PathUtils.norm(sf.fileName);
387
+ const deps = analysed.allDepMap.get(filePath) ?? new Set<TNormPath>();
392
388
 
393
389
  for (const dep of deps) {
394
- const targetDeps = depMap.get(dep.fileName) ?? [];
395
-
396
- if (dep.importName === "*") {
397
- for (const targetRefItem of targetDeps.filter((item) => item.exportName != null)) {
398
- if (prevSet?.has(targetRefItem.fileName)) continue;
399
-
400
- result.add(targetRefItem.fileName);
401
- result.adds(...getAllDeps(
402
- targetRefItem.fileName,
403
- new Set<TNormPath>(prevSet).adds(...result),
404
- ));
405
- }
406
- }
407
- else {
408
- for (const targetRefItem of targetDeps.filter((item) => item.exportName
409
- === dep.importName)) {
410
- if (prevSet?.has(targetRefItem.fileName)) continue;
411
-
412
- result.add(targetRefItem.fileName);
413
- result.adds(...getAllDeps(
414
- targetRefItem.fileName,
415
- new Set<TNormPath>(prevSet).adds(...result),
416
- ));
417
- }
418
- }
390
+ const depCache = this._revDepCacheMap.getOrCreate(dep, new Set<TNormPath>());
391
+ depCache.add(filePath);
419
392
  }
420
393
 
421
- return result;
422
- };
423
-
424
- for (const sf of sourceFileSet) {
425
- const deps = getAllDeps(PathUtils.norm(sf.fileName));
426
- allDepMap.set(PathUtils.norm(sf.fileName), deps);
427
-
428
- for (const dep of getAllDeps(PathUtils.norm(sf.fileName))) {
429
- const depCache = this.#revDependencyCacheMap.getOrCreate(dep, new Set<TNormPath>());
430
- depCache.add(PathUtils.norm(sf.fileName));
431
- }
432
-
433
- if (this.#ngProgram) {
434
- if (this.#ngProgram.compiler.ignoreForEmit.has(sf)) {
394
+ if (this._ngProgram) {
395
+ if (this._ngProgram.compiler.ignoreForEmit.has(sf)) {
435
396
  continue;
436
397
  }
437
398
 
438
- for (const dep of this.#ngProgram.compiler.getResourceDependencies(sf)) {
439
- const ref = this.#resourceDependencyCacheMap.getOrCreate(
399
+ for (const dep of this._ngProgram.compiler.getResourceDependencies(sf)) {
400
+ const ref = this._revDepCacheMap.getOrCreate(
440
401
  PathUtils.norm(dep),
441
402
  new Set<TNormPath>(),
442
403
  );
443
- ref.add(PathUtils.norm(sf.fileName));
404
+ ref.add(filePath);
444
405
  }
445
406
  }
446
407
  }
408
+
409
+ return analysed;
447
410
  });
448
411
 
449
- if (this.#modifiedFileSet.size === 0) {
450
- this.#debug(`get affected (init)...`);
412
+ if (this._modifiedFileSet.size === 0) {
413
+ this._debug(`영향 받은 파일 추가 중... (새 의존성)`);
451
414
 
452
- this.#perf.run("get affected (init)", () => {
453
- for (const sf of sourceFileSet) {
454
- if (!this.#watchScopePaths.some((scopePath) => PathUtils.isChildPath(
415
+ this._perf.run("영향 받은 파일 추가 중 (새 의존성)", () => {
416
+ for (const sf of analysed1.sourceFileSet) {
417
+ if (!this._opt.watchScopePaths.some((scopePath) => PathUtils.isChildPath(
455
418
  sf.fileName,
456
419
  scopePath,
457
420
  ))) {
458
421
  continue;
459
422
  }
460
423
 
461
- this.#affectedFileSet.add(PathUtils.norm(sf.fileName));
424
+ this._affectedFileSet.add(PathUtils.norm(sf.fileName));
462
425
  }
463
426
  });
464
427
  }
465
428
 
466
- for (const dep of this.#revDependencyCacheMap.keys()) {
467
- if (this.#modifiedFileSet.has(dep)) {
468
- this.#affectedFileSet.adds(
469
- ...Array.from(this.#revDependencyCacheMap.get(dep)!).mapMany((item) =>
429
+ for (const dep of this._revDepCacheMap.keys()) {
430
+ if (this._modifiedFileSet.has(dep)) {
431
+ this._affectedFileSet.adds(
432
+ ...Array.from(this._revDepCacheMap.get(dep)!).mapMany((item) =>
470
433
  [
471
434
  item,
472
435
  // .d.ts면 .js파일도 affected에 추가
@@ -478,22 +441,8 @@ export class SdTsCompiler {
478
441
 
479
442
  // dep이 emit된적이 없으면 affected에 추가해야함.
480
443
  // dep파일이 추가된후 기존 파일에서 import하면 dep파일이 affected에 포함이 안되는 현상 때문
481
- if (!this.#emittedFilesCacheMap.has(dep)) {
482
- this.#affectedFileSet.add(dep);
483
- }
484
- }
485
-
486
- if (this.#ngProgram) {
487
- for (const dep of this.#resourceDependencyCacheMap.keys()) {
488
- if (this.#modifiedFileSet.has(dep)) {
489
- this.#affectedFileSet.adds(...this.#resourceDependencyCacheMap.get(dep)!);
490
- }
491
-
492
- // dep이 emit된적이 없으면 affected에 추가해야함.
493
- // dep파일이 추가된후 기존 파일에서 import하면 dep파일이 affected에 포함이 안되는 현상 때문
494
- if (!this.#emittedFilesCacheMap.has(dep)) {
495
- this.#affectedFileSet.add(dep);
496
- }
444
+ if (!this._emittedFilesCacheMap.has(dep)) {
445
+ this._affectedFileSet.add(dep);
497
446
  }
498
447
  }
499
448
 
@@ -502,9 +451,9 @@ export class SdTsCompiler {
502
451
  };
503
452
  }
504
453
 
505
- async #lintAsync() {
506
- const lintFilePaths = Array.from(this.#affectedFileSet)
507
- .filter((item) => PathUtils.isChildPath(item, this.#pkgPath))
454
+ private async _lintAsync() {
455
+ const lintFilePaths = Array.from(this._affectedFileSet)
456
+ .filter((item) => PathUtils.isChildPath(item, this._opt.pkgPath))
508
457
  .filter((item) => (
509
458
  (!item.endsWith(".d.ts") && item.endsWith(".ts")) ||
510
459
  item.endsWith(".js")
@@ -516,14 +465,14 @@ export class SdTsCompiler {
516
465
  }
517
466
 
518
467
  const linter = new ESLint({
519
- cwd: this.#pkgPath,
468
+ cwd: this._opt.pkgPath,
520
469
  cache: false,
521
470
  overrideConfig: {
522
471
  languageOptions: {
523
472
  parserOptions: {
524
473
  // parser: tseslint.parser,
525
474
  project: null,
526
- programs: [this.#program],
475
+ programs: [this._program],
527
476
  },
528
477
  },
529
478
  },
@@ -538,57 +487,52 @@ export class SdTsCompiler {
538
487
  // ]);
539
488
  }
540
489
 
541
- async #buildAsync() {
490
+ private async _buildAsync(tsconf: ITsConfigInfo) {
542
491
  const emitFileSet = new Set<TNormPath>();
543
492
  const diagnostics: ts.Diagnostic[] = [];
544
493
 
545
- this.#debug(`get diagnostics...`);
494
+ this._debug(`프로그램 진단 수집 중...`);
546
495
 
547
- this.#perf.run("get program diagnostics", () => {
496
+ this._perf.run("프로그램 진단 수집", () => {
548
497
  diagnostics.push(
549
- ...this.#program!.getConfigFileParsingDiagnostics(),
550
- ...this.#program!.getOptionsDiagnostics(),
551
- ...this.#program!.getGlobalDiagnostics(),
498
+ ...this._program!.getConfigFileParsingDiagnostics(),
499
+ ...this._program!.getOptionsDiagnostics(),
500
+ ...this._program!.getGlobalDiagnostics(),
552
501
  );
553
502
 
554
- if (this.#ngProgram) {
555
- diagnostics.push(...this.#ngProgram.compiler.getOptionDiagnostics());
503
+ if (this._ngProgram) {
504
+ diagnostics.push(...this._ngProgram.compiler.getOptionDiagnostics());
556
505
  }
557
506
  });
558
507
 
559
- this.#debug(`get diagnostics of files...`);
508
+ this._debug(`개별 파일 진단 수집 중...`);
560
509
 
561
- for (const affectedFile of this.#affectedFileSet) {
562
- if (!PathUtils.isChildPath(affectedFile, this.#pkgPath)) {
563
- continue;
564
- }
565
-
566
- const affectedSourceFile = this.#program!.getSourceFile(affectedFile);
510
+ for (const affectedFile of this._affectedFileSet) {
511
+ if (!PathUtils.isChildPath(affectedFile, this._opt.pkgPath)) continue;
567
512
 
513
+ const affectedSourceFile = this._program!.getSourceFile(affectedFile);
568
514
  if (
569
515
  !affectedSourceFile ||
570
- (this.#ngProgram && this.#ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))
516
+ (this._ngProgram && this._ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))
571
517
  ) {
572
518
  continue;
573
519
  }
574
520
 
575
521
  // this.#debug(`get diagnostics of file ${affectedFile}...`);
576
522
 
577
- this.#perf.run("get file diagnostics", () => {
523
+ this._perf.run("개별 파일 진단 수집", () => {
578
524
  diagnostics.push(
579
- ...this.#program!.getSyntacticDiagnostics(affectedSourceFile),
580
- ...this.#program!.getSemanticDiagnostics(affectedSourceFile),
525
+ ...this._program!.getSyntacticDiagnostics(affectedSourceFile),
526
+ ...this._program!.getSemanticDiagnostics(affectedSourceFile),
581
527
  );
582
528
  });
583
529
 
584
- if (this.#ngProgram) {
585
- this.#perf.run("get file diagnostics: ng", () => {
586
- if (affectedSourceFile.isDeclarationFile) {
587
- return;
588
- }
530
+ if (this._ngProgram) {
531
+ this._perf.run("개별 파일 진단 수집(Angular)", () => {
532
+ if (affectedSourceFile.isDeclarationFile) return;
589
533
 
590
534
  diagnostics.push(
591
- ...this.#ngProgram!.compiler.getDiagnosticsForFile(
535
+ ...this._ngProgram!.compiler.getDiagnosticsForFile(
592
536
  affectedSourceFile,
593
537
  OptimizeFor.WholeProgram,
594
538
  ),
@@ -597,163 +541,50 @@ export class SdTsCompiler {
597
541
  }
598
542
  }
599
543
 
600
- this.#perf.run("emit", () => {
601
- this.#debug(`prepare emit...`);
544
+ this._perf.run("파일 출력 (emit)", () => {
545
+ this._debug(`파일 출력 준비 중...`);
602
546
 
603
547
  let transformers: ts.CustomTransformers = {};
604
548
 
605
- if (this.#ngProgram) {
549
+ if (this._ngProgram) {
606
550
  transformers = {
607
551
  ...transformers,
608
- ...this.#ngProgram.compiler.prepareEmit().transformers,
552
+ ...this._ngProgram.compiler.prepareEmit().transformers,
609
553
  };
610
- (transformers.before ??= []).push(replaceBootstrap(() => this.#program!.getTypeChecker()));
554
+ (transformers.before ??= []).push(replaceBootstrap(() => this._program!.getTypeChecker()));
611
555
  (transformers.before ??= []).push(
612
556
  createWorkerTransformer((file, importer) => {
613
557
  const fullPath = path.resolve(path.dirname(importer), file);
614
- const relPath = path.relative(path.resolve(this.#pkgPath, "src"), fullPath);
558
+ const relPath = path.relative(path.resolve(this._opt.pkgPath, "src"), fullPath);
615
559
  return relPath.replace(/\.ts$/, "").replaceAll("\\", "/") + ".js";
616
560
  }),
617
561
  );
618
562
  }
619
- // (transformers.before ??= []).push(transformKeys(this.#program));
620
-
621
- /*const fixImportTransformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
622
- return (sf) => {
623
- const shouldAppendJs = (importText: string): string | undefined => {
624
- const resolved = ts.resolveModuleName(
625
- importText,
626
- sf.fileName,
627
- this.#program!.getCompilerOptions(),
628
- ts.sys,
629
- );
630
-
631
- const resolvedInfo = resolved.resolvedModule;
632
- if (!resolvedInfo) return undefined;
633
-
634
- const resolvedFileName = resolvedInfo.resolvedFileName;
635
-
636
- // ① .ts / .tsx / .js / .jsx 만 대상
637
- if (!/\.(d\.ts|ts|tsx|js|jsx)$/i.test(resolvedFileName)) return undefined;
638
-
639
- // ② 사용자가 .js, .mjs, .json 등 명시한 경우 무시
640
- if (/\.[mc]?js$|\.json$/i.test(importText)) return undefined;
641
-
642
- // 3. import 경로의 마지막 부분이 파일명(확장자 제외)과 같으면 → .js 붙여야 함
643
- const importLastName = importText.split("/").pop();
644
- const resolvedFileNameOnly = path.basename(resolvedFileName)
645
- .replace(/\.(d\.ts|ts|tsx|js|jsx)$/, "");
646
563
 
647
- if (importLastName === resolvedFileNameOnly) {
648
- return importText + ".js";
649
- }
650
-
651
- // 4. 그렇지 않으면 → index.ts 같은 루트 패키지 import → .js 붙이지 않음
652
- return undefined;
653
- };
654
-
655
- const visitor: ts.Visitor = (node): ts.Node => {
656
- // import { x } from "./foo"
657
- if (
658
- ts.isImportDeclaration(node) &&
659
- ts.isStringLiteral(node.moduleSpecifier)
660
- ) {
661
- const newPath = shouldAppendJs(node.moduleSpecifier.text);
662
- if (newPath != null) {
663
- return ts.factory.updateImportDeclaration(
664
- node,
665
- node.modifiers,
666
- node.importClause,
667
- ts.factory.createStringLiteral(newPath),
668
- undefined,
669
- );
670
- }
671
- }
672
-
673
- // export * from "./bar"
674
- if (
675
- ts.isExportDeclaration(node) &&
676
- node.moduleSpecifier &&
677
- ts.isStringLiteral(node.moduleSpecifier)
678
- ) {
679
- const newPath = shouldAppendJs(node.moduleSpecifier.text);
680
- if (newPath != null) {
681
- return ts.factory.updateExportDeclaration(
682
- node,
683
- node.modifiers,
684
- node.isTypeOnly,
685
- node.exportClause,
686
- ts.factory.createStringLiteral(newPath),
687
- undefined,
688
- );
689
- }
690
- }
691
-
692
- // dynamic import("./baz")
693
- if (
694
- ts.isCallExpression(node) &&
695
- node.expression.kind === ts.SyntaxKind.ImportKeyword &&
696
- node.arguments.length === 1 &&
697
- ts.isStringLiteral(node.arguments[0])
698
- ) {
699
- const newPath = shouldAppendJs(node.arguments[0].text);
700
- if (newPath != null) {
701
- return ts.factory.updateCallExpression(
702
- node,
703
- node.expression,
704
- undefined,
705
- [ts.factory.createStringLiteral(newPath)],
706
- );
707
- }
708
- }
709
-
710
- return ts.visitEachChild(node, visitor, context);
711
- };
712
-
713
- return ts.visitNode(sf, visitor) as ts.SourceFile;
714
- };
715
- };
716
- (transformers.before ??= []).push(fixImportTransformer);*/
717
-
718
- this.#debug(`emit for files...`);
564
+ this._debug(`파일 출력 중...`);
719
565
 
720
566
  // affected에 새로 추가된 파일은 포함되지 않는 현상이 있어 sourceFileSet으로 바꿈
721
567
  // 비교해보니, 딱히 getSourceFiles라서 더 느려지는것 같지는 않음
722
568
  // 그래도 affected로 다시 테스트 (조금이라도 더 빠르게)
723
- for (const affectedFile of this.#affectedFileSet) {
724
- if (this.#emittedFilesCacheMap.has(affectedFile)) {
725
- continue;
726
- }
569
+ for (const affectedFile of this._affectedFileSet) {
570
+ if (this._emittedFilesCacheMap.has(affectedFile)) continue;
727
571
 
728
- const sf = this.#program!.getSourceFile(affectedFile);
729
- if (!sf) {
730
- continue;
731
- }
572
+ const sf = this._program!.getSourceFile(affectedFile);
573
+ if (!sf || sf.isDeclarationFile) continue;
574
+ if (this._ngProgram?.compiler.ignoreForEmit.has(sf)) continue;
575
+ if (this._ngProgram?.compiler.incrementalCompilation.safeToSkipEmit(sf)) continue;
732
576
 
733
- if (sf.isDeclarationFile) {
577
+ // 번들이 아닌 외부패키지는 보통 emit안해도 됨
578
+ // but esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
579
+ if (!this._opt.isForBundle && !PathUtils.isChildPath(sf.fileName, this._opt.pkgPath)) {
734
580
  continue;
735
581
  }
736
582
 
737
- if (this.#ngProgram?.compiler.ignoreForEmit.has(sf)) {
738
- continue;
739
- }
740
-
741
- if (this.#ngProgram?.compiler.incrementalCompilation.safeToSkipEmit(sf)) {
742
- continue;
743
- }
744
-
745
- // esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
746
- if (!this.#isForBundle) {
747
- if (!PathUtils.isChildPath(sf.fileName, this.#pkgPath)) {
748
- continue;
749
- }
750
- }
751
-
752
- this.#program!.emit(
583
+ this._program!.emit(
753
584
  sf,
754
585
  (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
755
586
  if (!sourceFiles || sourceFiles.length === 0) {
756
- this.#compilerHost.writeFile(
587
+ tsconf.compilerHost.writeFile(
757
588
  fileName,
758
589
  text,
759
590
  writeByteOrderMark,
@@ -765,44 +596,22 @@ export class SdTsCompiler {
765
596
  }
766
597
 
767
598
  const sourceFile = ts.getOriginalNode(sourceFiles[0], ts.isSourceFile);
768
- if (this.#ngProgram) {
769
- if (this.#ngProgram.compiler.ignoreForEmit.has(sourceFile)) {
770
- return;
771
- }
772
- this.#ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
599
+ if (this._ngProgram) {
600
+ if (this._ngProgram.compiler.ignoreForEmit.has(sourceFile)) return;
601
+ this._ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
773
602
  }
774
603
 
775
- const emitFileInfoCaches = this.#emittedFilesCacheMap.getOrCreate(PathUtils.norm(
776
- sourceFile.fileName), []);
777
-
778
- if (PathUtils.isChildPath(sourceFile.fileName, this.#pkgPath)) {
779
- let realFilePath = PathUtils.norm(fileName);
780
- let realText = text;
781
- if (
782
- PathUtils.isChildPath(
783
- realFilePath,
784
- path.resolve(this.#distPath, path.basename(this.#pkgPath), "src"),
785
- )
786
- ) {
787
- realFilePath = PathUtils.norm(
788
- this.#distPath,
789
- path.relative(
790
- path.resolve(this.#distPath, path.basename(this.#pkgPath), "src"),
791
- realFilePath,
792
- ),
793
- );
794
-
795
- if (fileName.endsWith(".js.map")) {
796
- const sourceMapContents = JSON.parse(realText);
797
- // remove "../../"
798
- sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6);
799
- realText = JSON.stringify(sourceMapContents);
800
- }
801
- }
604
+ const emitFileInfoCaches = this._emittedFilesCacheMap.getOrCreate(
605
+ PathUtils.norm(sourceFile.fileName),
606
+ [],
607
+ );
608
+
609
+ if (PathUtils.isChildPath(sourceFile.fileName, this._opt.pkgPath)) {
610
+ const real = this._convertOutputToReal(fileName, tsconf.distPath, text);
802
611
 
803
612
  emitFileInfoCaches.push({
804
- outAbsPath: realFilePath,
805
- text: realText,
613
+ outAbsPath: real.filePath,
614
+ text: real.text,
806
615
  });
807
616
  }
808
617
  else {
@@ -820,32 +629,32 @@ export class SdTsCompiler {
820
629
 
821
630
  //-- global style
822
631
  if (
823
- this.#globalStyleFilePath != null &&
824
- FsUtils.exists(this.#globalStyleFilePath) &&
825
- !this.#emittedFilesCacheMap.has(this.#globalStyleFilePath)
632
+ this._opt.globalStyleFilePath != null &&
633
+ FsUtils.exists(this._opt.globalStyleFilePath) &&
634
+ !this._emittedFilesCacheMap.has(this._opt.globalStyleFilePath)
826
635
  ) {
827
- this.#debug(`bundle global style...`);
636
+ this._debug(`전역 스타일 번들링 중...`);
828
637
 
829
- await this.#perf.run("bundle global style", async () => {
830
- const data = FsUtils.readFile(this.#globalStyleFilePath!);
831
- const contents = await this.#bundleStylesheetAsync(
638
+ await this._perf.run("전역 스타일 번들링", async () => {
639
+ const data = FsUtils.readFile(this._opt.globalStyleFilePath!);
640
+ const stylesheetBundlingResult = await this._bundleStylesheetAsync(
832
641
  data,
833
- this.#globalStyleFilePath!,
834
- this.#globalStyleFilePath,
642
+ this._opt.globalStyleFilePath!,
643
+ this._opt.globalStyleFilePath,
835
644
  );
836
- const emitFileInfos = this.#emittedFilesCacheMap.getOrCreate(
837
- this.#globalStyleFilePath!,
645
+ const emitFileInfos = this._emittedFilesCacheMap.getOrCreate(
646
+ this._opt.globalStyleFilePath!,
838
647
  [],
839
648
  );
840
649
  emitFileInfos.push({
841
650
  outAbsPath: PathUtils.norm(
842
- this.#pkgPath,
843
- path.relative(path.resolve(this.#pkgPath, "src"), this.#globalStyleFilePath!)
651
+ this._opt.pkgPath,
652
+ path.relative(path.resolve(this._opt.pkgPath, "src"), this._opt.globalStyleFilePath!)
844
653
  .replace(/\.scss$/, ".css"),
845
654
  ),
846
- text: contents,
655
+ text: stylesheetBundlingResult.contents ?? "",
847
656
  });
848
- emitFileSet.add(this.#globalStyleFilePath!);
657
+ emitFileSet.add(this._opt.globalStyleFilePath!);
849
658
  });
850
659
  }
851
660
 
@@ -855,215 +664,35 @@ export class SdTsCompiler {
855
664
  };
856
665
  }
857
666
 
858
- #debug(...msg: any[]): void {
859
- this.#logger.debug(`[${path.basename(this.#pkgPath)}]`, ...msg);
860
- }
861
-
862
- #findDeps(sf: ts.SourceFile) {
863
- const deps: ({
864
- fileName: TNormPath;
865
- importName: string;
866
- exportName?: string;
867
- } | ISdBuildMessage)[] = [];
868
-
869
- const tc = this.#program!.getTypeChecker();
870
-
871
- const visit = (node: ts.Node) => {
872
- if (ts.isExportDeclaration(node)) {
873
- if (node.moduleSpecifier) {
874
- const moduleSymbol = tc.getSymbolAtLocation(node.moduleSpecifier);
875
- if (!moduleSymbol) {
876
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
877
- deps.push({
878
- filePath: PathUtils.norm(sf.fileName),
879
- line: pos.line,
880
- char: pos.character,
881
- code: undefined,
882
- severity: "error",
883
- message: "export moduleSymbol not found",
884
- type: "deps",
885
- });
886
- return;
887
- }
667
+ private _convertOutputToReal(filePath: string, distPath: string, text: string) {
668
+ let realFilePath = PathUtils.norm(filePath);
669
+ let realText = text;
888
670
 
889
- const decls = moduleSymbol.getDeclarations();
890
- if (!decls) {
891
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
892
- deps.push({
893
- filePath: PathUtils.norm(sf.fileName),
894
- line: pos.line,
895
- char: pos.character,
896
- code: undefined,
897
- severity: "error",
898
- message: "export decls not found",
899
- type: "deps",
900
- });
901
- return;
902
- }
671
+ const srcRelBasePath = path.resolve(distPath, path.basename(this._opt.pkgPath), "src");
903
672
 
904
- const namedBindings = node.exportClause;
905
- if (namedBindings && ts.isNamedExports(namedBindings)) {
906
- for (const el of namedBindings.elements) {
907
- for (const decl of decls) {
908
- deps.push({
909
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
910
- importName: el.name.text,
911
- exportName: el.propertyName?.text ?? el.name.text,
912
- });
913
- }
914
- }
915
- }
916
- else {
917
- if (!moduleSymbol.exports) {
918
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
919
- deps.push({
920
- filePath: PathUtils.norm(sf.fileName),
921
- line: pos.line,
922
- char: pos.character,
923
- code: undefined,
924
- severity: "error",
925
- message: "moduleSymbol exports not found",
926
- type: "deps",
927
- });
928
- return;
929
- }
673
+ if (PathUtils.isChildPath(realFilePath, srcRelBasePath)) {
674
+ realFilePath = PathUtils.norm(distPath, path.relative(srcRelBasePath, realFilePath));
930
675
 
931
- for (const decl of decls) {
932
- for (const key of moduleSymbol.exports.keys()) {
933
- deps.push({
934
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
935
- importName: key.toString(),
936
- exportName: key.toString(),
937
- });
938
- }
939
- }
940
- }
941
- }
676
+ // source map 위치 정확히 찾아가기
677
+ if (filePath.endsWith(".js.map")) {
678
+ const sourceMapContents = JSON.parse(realText);
679
+ sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6); // remove "../../"
680
+ realText = JSON.stringify(sourceMapContents);
942
681
  }
943
- else if (ts.isImportDeclaration(node)) {
944
- const moduleSymbol = tc.getSymbolAtLocation(node.moduleSpecifier);
945
- if (!moduleSymbol) {
946
- if (ts.isStringLiteral(node.moduleSpecifier)
947
- && node.moduleSpecifier.text.startsWith("./")) {
948
- deps.push({
949
- fileName: PathUtils.norm(path.resolve(
950
- path.dirname(sf.fileName),
951
- node.moduleSpecifier.text + ".ts",
952
- )),
953
- importName: "*",
954
- });
955
-
956
- // const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
957
- // deps.push({
958
- // filePath: PathUtil.norm(sf.fileName),
959
- // line: pos.line,
960
- // char: pos.character,
961
- // code: undefined,
962
- // severity: "error",
963
- // message: `import moduleSymbol not found (${node.moduleSpecifier.text})`,
964
- // type: "deps",
965
- // });
966
- // return;
967
- }
968
-
969
- /*else {
970
- throw new NeverEntryError(`import moduleSymbol: ${sf.fileName} ${node.moduleSpecifier["text"]}`);
971
- }*/
972
- }
973
- else {
974
- const decls = moduleSymbol.getDeclarations();
975
- if (!decls) {
976
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
977
- deps.push({
978
- filePath: PathUtils.norm(sf.fileName),
979
- line: pos.line,
980
- char: pos.character,
981
- code: undefined,
982
- severity: "error",
983
- message: `import decls not found (${moduleSymbol.name})`,
984
- type: "deps",
985
- });
986
- return;
987
- }
988
-
989
- const namedBindings = node.importClause?.namedBindings;
990
- if (namedBindings && ts.isNamedImports(namedBindings)) {
991
- for (const el of namedBindings.elements) {
992
- for (const decl of decls) {
993
- deps.push({
994
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
995
- importName: el.name.text,
996
- });
997
- }
998
- }
999
- }
1000
- else {
1001
- for (const decl of decls) {
1002
- deps.push({
1003
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
1004
- importName: "*",
1005
- });
1006
- }
1007
- }
1008
- }
1009
- }
1010
-
1011
- if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
1012
- if (ts.isStringLiteral(node.arguments[0]) && node.arguments[0].text.startsWith("./")) {
1013
-
1014
- const moduleSymbol = tc.getSymbolAtLocation(node.arguments[0]);
1015
- if (!moduleSymbol) {
1016
- deps.push({
1017
- fileName: PathUtils.norm(path.resolve(
1018
- path.dirname(sf.fileName),
1019
- node.arguments[0].text + ".ts",
1020
- )),
1021
- importName: "*",
1022
- });
1023
-
1024
- // const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
1025
- // deps.push({
1026
- // filePath: PathUtil.norm(sf.fileName),
1027
- // line: pos.line,
1028
- // char: pos.character,
1029
- // code: undefined,
1030
- // severity: "error",
1031
- // message: `import() moduleSymbol not found (${node.arguments[0].text})`,
1032
- // type: "deps",
1033
- // });
1034
- // return;
1035
- }
1036
- else {
1037
- const decls = moduleSymbol.getDeclarations();
1038
- if (!decls) {
1039
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
1040
- deps.push({
1041
- filePath: PathUtils.norm(sf.fileName),
1042
- line: pos.line,
1043
- char: pos.character,
1044
- code: undefined,
1045
- severity: "error",
1046
- message: `import() decls not found (${node.arguments[0].text})`,
1047
- type: "deps",
1048
- });
1049
- return;
1050
- }
1051
-
1052
- for (const decl of decls) {
1053
- deps.push({
1054
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
1055
- importName: "*",
1056
- });
1057
- }
1058
- }
1059
- }
1060
- }
1061
-
1062
- node.forEachChild(visit);
1063
- };
682
+ }
1064
683
 
1065
- sf.forEachChild(visit);
684
+ return { filePath: realFilePath, text: realText };
685
+ }
1066
686
 
1067
- return deps;
687
+ private _debug(...msg: any[]): void {
688
+ this._logger.debug(`[${path.basename(this._opt.pkgPath)}]`, ...msg);
1068
689
  }
1069
690
  }
691
+
692
+
693
+ interface ITsConfigInfo {
694
+ fileNames: string[];
695
+ options: ts.CompilerOptions;
696
+ compilerHost: ts.CompilerHost;
697
+ distPath: string;
698
+ }