@simplysm/sd-cli 12.8.20 → 12.8.22

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/entry/sd-cli-cordova.d.ts +30 -0
  2. package/dist/entry/sd-cli-cordova.js +307 -246
  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/pkg-builders/server/sd-server.bundler.js +11 -10
  18. package/dist/pkg-builders/server/sd-server.bundler.js.map +1 -1
  19. package/dist/ts-compiler/sd-ts-compiler.d.ts +25 -2
  20. package/dist/ts-compiler/sd-ts-compiler.js +306 -575
  21. package/dist/ts-compiler/sd-ts-compiler.js.map +1 -1
  22. package/dist/ts-compiler/sd-ts-dependency-analyzer.d.ts +6 -0
  23. package/dist/ts-compiler/sd-ts-dependency-analyzer.js +141 -0
  24. package/dist/ts-compiler/sd-ts-dependency-analyzer.js.map +1 -0
  25. package/dist/types/ts-compiler.types.d.ts +12 -7
  26. package/dist/types/worker.types.d.ts +13 -0
  27. package/dist/utils/sd-cli-performance-time.js +1 -1
  28. package/package.json +6 -8
  29. package/src/entry/sd-cli-cordova.ts +393 -280
  30. package/src/entry/sd-cli-project.ts +11 -23
  31. package/src/index.ts +1 -0
  32. package/src/pkg-builders/client/sd-ng.bundler.ts +67 -69
  33. package/src/pkg-builders/client/sd-ng.plugin-creator.ts +47 -27
  34. package/src/pkg-builders/lib/sd-ts-lib.builder.ts +14 -7
  35. package/src/pkg-builders/server/sd-server.bundler.ts +22 -12
  36. package/src/ts-compiler/sd-ts-compiler.ts +379 -704
  37. package/src/ts-compiler/sd-ts-dependency-analyzer.ts +185 -0
  38. package/src/types/ts-compiler.types.ts +11 -6
  39. package/src/types/worker.types.ts +7 -6
  40. package/src/utils/sd-cli-performance-time.ts +1 -1
@@ -11,76 +11,31 @@ import { SdCliPerformanceTimer } from "../utils/sd-cli-performance-time";
11
11
  import { SdCliConvertMessageUtils } from "../utils/sd-cli-convert-message.utils";
12
12
  import { createWorkerTransformer, } from "@angular/build/src/tools/angular/transformers/web-worker-transformer";
13
13
  import { ESLint } from "eslint";
14
+ import { SdTsDependencyAnalyzer } from "./sd-ts-dependency-analyzer";
14
15
  export class SdTsCompiler {
15
- #logger = SdLogger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
16
- #parsedTsconfig;
17
- #isForAngular;
18
- // readonly #workerRevDependencyCacheMap = new Map<TNormPath, Set<TNormPath>>();
19
- #revDependencyCacheMap = new Map();
20
- #resourceDependencyCacheMap = new Map();
21
- #sourceFileCacheMap = new Map();
22
- #emittedFilesCacheMap = new Map();
23
- #stylesheetBundler;
24
- #compilerHost;
25
- #ngProgram;
26
- #program;
27
- #modifiedFileSet = new Set();
28
- #affectedFileSet = new Set();
29
- #watchFileSet = new Set();
30
- #stylesheetBundlingResultMap = new Map();
31
- #pkgPath;
32
- #distPath;
33
- #globalStyleFilePath;
34
- #watchScopePaths;
35
- #isForBundle;
36
- // readonly #lintWorker = new SdWorker<TSdLintWorkerType>(import.meta.resolve("../workers/lint-worker"));
37
- #perf;
38
- // #processWebWorker?: (workerFile: string, containingFile: string) => string;
39
- constructor(opt) {
40
- this.#pkgPath = opt.pkgPath;
41
- this.#globalStyleFilePath = opt.globalStyleFilePath;
42
- this.#isForBundle = opt.isForBundle;
43
- this.#watchScopePaths = opt.watchScopePaths;
44
- // this.#processWebWorker = opt.processWebWorker;
45
- this.#debug("초기화...");
46
- //-- isForAngular / parsedTsConfig
47
- const tsconfigPath = path.resolve(opt.pkgPath, "tsconfig.json");
16
+ constructor(_opt) {
17
+ this._opt = _opt;
18
+ this._logger = SdLogger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
19
+ this._allDepCacheMap = new Map();
20
+ this._revDepCacheMap = new Map();
21
+ this._sourceFileCacheMap = new Map();
22
+ this._emittedFilesCacheMap = new Map();
23
+ this._modifiedFileSet = new Set();
24
+ this._affectedFileSet = new Set();
25
+ this._watchFileSet = new Set();
26
+ this._stylesheetBundlingResultMap = new Map();
27
+ this._debug("초기화 중...");
28
+ const tsconfigPath = path.resolve(this._opt.pkgPath, "tsconfig.json");
48
29
  const tsconfig = FsUtils.readJson(tsconfigPath);
49
- this.#isForAngular = Boolean(tsconfig.angularCompilerOptions);
50
- this.#parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, opt.pkgPath, {
51
- ...tsconfig.angularCompilerOptions,
52
- ...opt.additionalOptions,
53
- });
54
- this.#distPath = PathUtils.norm(this.#parsedTsconfig.options.outDir ?? path.resolve(opt.pkgPath, "dist"));
55
- //-- compilerHost
56
- this.#compilerHost = ts.createCompilerHost(this.#parsedTsconfig.options);
57
- const baseGetSourceFile = this.#compilerHost.getSourceFile;
58
- this.#compilerHost.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile, ...args) => {
59
- if (!shouldCreateNewSourceFile && this.#sourceFileCacheMap.has(PathUtils.norm(fileName))) {
60
- return this.#sourceFileCacheMap.get(PathUtils.norm(fileName));
61
- }
62
- const sf = baseGetSourceFile.call(this.#compilerHost, fileName, languageVersionOrOptions, onError, true, ...args);
63
- if (sf) {
64
- this.#sourceFileCacheMap.set(PathUtils.norm(fileName), sf);
65
- }
66
- else {
67
- this.#sourceFileCacheMap.delete(PathUtils.norm(fileName));
68
- }
69
- return sf;
70
- };
71
- const baseReadFile = this.#compilerHost.readFile;
72
- this.#compilerHost.readFile = (fileName) => {
73
- this.#watchFileSet.add(PathUtils.norm(fileName));
74
- return baseReadFile.call(this.#compilerHost, fileName);
75
- };
76
- if (this.#isForAngular) {
30
+ this._isForAngular = Boolean(tsconfig.angularCompilerOptions);
31
+ if (this._isForAngular) {
77
32
  //-- stylesheetBundler
78
- this.#stylesheetBundler = new ComponentStylesheetBundler({
79
- workspaceRoot: opt.pkgPath,
80
- optimization: !opt.isDevMode,
33
+ this._stylesheetBundler = new ComponentStylesheetBundler({
34
+ workspaceRoot: this._opt.pkgPath,
35
+ optimization: !this._opt.isDevMode,
81
36
  inlineFonts: true,
82
37
  preserveSymlinks: false,
83
- sourcemap: opt.isDevMode ? "inline" : false,
38
+ sourcemap: this._opt.isDevMode ? "inline" : false,
84
39
  outputNames: { bundles: "[name]", media: "media/[name]" },
85
40
  includePaths: [],
86
41
  externalDependencies: [],
@@ -94,53 +49,116 @@ export class SdTsCompiler {
94
49
  path: ".cache/angular",
95
50
  basePath: ".cache",
96
51
  },
97
- }, "scss", opt.isDevMode);
98
- //-- compilerHost
99
- this.#compilerHost.readResource = (fileName) => {
100
- return this.#compilerHost.readFile(fileName) ?? "";
52
+ }, "scss", this._opt.isDevMode);
53
+ }
54
+ }
55
+ _parseTsConfig() {
56
+ const config = this._loadTsConfig();
57
+ const compilerHost = this._createCompilerHost(config.options);
58
+ return {
59
+ ...config,
60
+ compilerHost,
61
+ };
62
+ }
63
+ _loadTsConfig() {
64
+ const tsconfigPath = path.resolve(this._opt.pkgPath, "tsconfig.json");
65
+ const tsconfig = FsUtils.readJson(tsconfigPath);
66
+ const parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._opt.pkgPath, {
67
+ ...tsconfig.angularCompilerOptions,
68
+ ...this._opt.additionalOptions,
69
+ });
70
+ const distPath = PathUtils.norm(parsedTsconfig.options.outDir ?? path.resolve(this._opt.pkgPath, "dist"));
71
+ return {
72
+ fileNames: parsedTsconfig.fileNames,
73
+ options: parsedTsconfig.options,
74
+ distPath: distPath,
75
+ };
76
+ }
77
+ _createCompilerHost(compilerOptions) {
78
+ const compilerHost = ts.createCompilerHost(compilerOptions);
79
+ const baseGetSourceFile = compilerHost.getSourceFile;
80
+ compilerHost.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile, ...args) => {
81
+ const fileNPath = PathUtils.norm(fileName);
82
+ if (!shouldCreateNewSourceFile && this._sourceFileCacheMap.has(fileNPath)) {
83
+ return this._sourceFileCacheMap.get(fileNPath);
84
+ }
85
+ const sf = baseGetSourceFile.call(compilerHost, fileName, languageVersionOrOptions, onError, true, ...args);
86
+ if (sf) {
87
+ this._sourceFileCacheMap.set(fileNPath, sf);
88
+ }
89
+ else {
90
+ this._sourceFileCacheMap.delete(fileNPath);
91
+ }
92
+ return sf;
93
+ };
94
+ const baseReadFile = compilerHost.readFile;
95
+ compilerHost.readFile = (fileName) => {
96
+ this._watchFileSet.add(PathUtils.norm(fileName));
97
+ return baseReadFile.call(compilerHost, fileName);
98
+ };
99
+ if (this._isForAngular) {
100
+ compilerHost.readResource = (fileName) => {
101
+ return compilerHost.readFile(fileName) ?? "";
101
102
  };
102
- this.#compilerHost.transformResource = async (data, context) => {
103
+ compilerHost.transformResource = async (data, context) => {
103
104
  if (context.type !== "style") {
104
105
  return null;
105
106
  }
106
- const contents = await this.#bundleStylesheetAsync(data, PathUtils.norm(context.containingFile), context.resourceFile != null ? PathUtils.norm(context.resourceFile) : undefined);
107
- return StringUtils.isNullOrEmpty(contents) ? null : { content: contents };
107
+ const stylesheetBundlingResult = await this._bundleStylesheetAsync(data, PathUtils.norm(context.containingFile), context.resourceFile != null ? PathUtils.norm(context.resourceFile) : undefined);
108
+ return StringUtils.isNullOrEmpty(stylesheetBundlingResult.contents)
109
+ ? null
110
+ : { content: stylesheetBundlingResult.contents };
108
111
  };
109
- this.#compilerHost.getModifiedResourceFiles = () => {
110
- return new Set(Array.from(this.#modifiedFileSet).map((item) => PathUtils.posix(item)));
112
+ compilerHost.getModifiedResourceFiles = () => {
113
+ return new Set(Array.from(this._modifiedFileSet).map((item) => PathUtils.posix(item)));
111
114
  };
112
115
  }
116
+ return compilerHost;
113
117
  }
114
- async #bundleStylesheetAsync(data, containingFile, resourceFile = null) {
118
+ async _bundleStylesheetAsync(data, containingFile, resourceFile = null) {
115
119
  // containingFile: 포함된 파일 (.ts)
116
120
  // resourceFile: 외부 리소스 파일 (styleUrls로 입력하지 않고 styles에 직접 입력한 경우 null)
117
121
  // referencedFiles: import한 외부 scss 파일 혹은 woff파일등 외부 파일
118
122
  // this.#debug(`bundle stylesheet...(${containingFile}, ${resourceFile})`);
119
- return await this.#perf.run("bundle style", async () => {
120
- const stylesheetResult = resourceFile != null
121
- ? await this.#stylesheetBundler.bundleFile(resourceFile)
122
- : await this.#stylesheetBundler.bundleInline(data, containingFile, "scss");
123
- if (stylesheetResult.referencedFiles) {
124
- for (const referencedFile of stylesheetResult.referencedFiles) {
125
- const depCacheSet = this.#resourceDependencyCacheMap.getOrCreate(PathUtils.norm(referencedFile), new Set());
126
- depCacheSet.add(resourceFile ?? containingFile);
123
+ return await this._perf.run("스타일 번들링", async () => {
124
+ const fileNPath = PathUtils.norm(resourceFile ?? containingFile);
125
+ if (this._stylesheetBundlingResultMap.has(fileNPath)) {
126
+ return this._stylesheetBundlingResultMap.get(fileNPath);
127
+ }
128
+ try {
129
+ const result = resourceFile != null
130
+ ? await this._stylesheetBundler.bundleFile(resourceFile)
131
+ : await this._stylesheetBundler.bundleInline(data, containingFile, "scss");
132
+ if (result.referencedFiles) {
133
+ for (const referencedFile of result.referencedFiles) {
134
+ const depCacheSet = this._revDepCacheMap.getOrCreate(PathUtils.norm(referencedFile), new Set());
135
+ depCacheSet.add(fileNPath);
136
+ }
137
+ this._watchFileSet.adds(...Array.from(result.referencedFiles.values())
138
+ .map((item) => PathUtils.norm(item)));
127
139
  }
128
- this.#watchFileSet.adds(...Array.from(stylesheetResult.referencedFiles.values())
129
- .map((item) => PathUtils.norm(item)));
140
+ this._stylesheetBundlingResultMap.set(fileNPath, result);
141
+ return result;
142
+ }
143
+ catch (err) {
144
+ const result = {
145
+ errors: [
146
+ {
147
+ text: `스타일 번들링 실패: ${err.message ?? "알 수 없는 오류"}`,
148
+ location: { file: containingFile },
149
+ },
150
+ ],
151
+ warnings: [],
152
+ };
153
+ this._stylesheetBundlingResultMap.set(fileNPath, result);
154
+ return result;
130
155
  }
131
- this.#stylesheetBundlingResultMap.set(PathUtils.norm(resourceFile ?? containingFile), {
132
- outputFiles: stylesheetResult.errors == null ? stylesheetResult.outputFiles : undefined,
133
- metafile: stylesheetResult.errors == null ? stylesheetResult.metafile : undefined,
134
- errors: stylesheetResult.errors,
135
- warnings: stylesheetResult.warnings,
136
- });
137
- return stylesheetResult.contents;
138
156
  });
139
157
  }
140
158
  async compileAsync(modifiedFileSet) {
141
- this.#perf = new SdCliPerformanceTimer("esbuild compile");
142
- this.#modifiedFileSet = new Set(modifiedFileSet);
143
- this.#affectedFileSet = new Set();
159
+ this._perf = new SdCliPerformanceTimer("esbuild compile");
160
+ this._modifiedFileSet = new Set(modifiedFileSet);
161
+ this._affectedFileSet = new Set();
144
162
  /*for (const mod of modifiedFileSet) {
145
163
  const workerImporters = this.#workerRevDependencyCacheMap.get(mod);
146
164
  if (workerImporters) {
@@ -149,146 +167,156 @@ export class SdTsCompiler {
149
167
  this.#modifiedFileSet.add(mod);
150
168
  }
151
169
  }*/
152
- const prepareResult = await this.#prepareAsync();
153
- const [buildResult, lintResults] = await Promise.all([this.#buildAsync(), this.#lintAsync()]);
154
- this.#debug(`build completed`, this.#perf.toString());
170
+ const tsconf = this._parseTsConfig();
171
+ const prepareResult = await this._prepareAsync(tsconf);
172
+ const [buildResult, lintResults] = await Promise.all([
173
+ this._buildAsync(tsconf),
174
+ this._lintAsync(),
175
+ ]);
176
+ this._debug(`빌드 완료됨`, this._perf.toString());
177
+ this._debug(`영향 받은 파일: ${this._affectedFileSet.size}개`);
178
+ this._debug(`감시 중인 파일: ${this._watchFileSet.size}개`);
155
179
  return {
156
180
  messages: [
157
181
  ...prepareResult.messages,
158
182
  ...SdCliConvertMessageUtils.convertToBuildMessagesFromTsDiag(buildResult.diagnostics),
159
183
  ...SdCliConvertMessageUtils.convertToBuildMessagesFromEslint(lintResults),
160
184
  ],
161
- watchFileSet: this.#watchFileSet,
162
- affectedFileSet: this.#affectedFileSet,
163
- stylesheetBundlingResultMap: this.#stylesheetBundlingResultMap,
164
- emittedFilesCacheMap: this.#emittedFilesCacheMap,
185
+ watchFileSet: this._watchFileSet,
186
+ affectedFileSet: this._affectedFileSet,
187
+ stylesheetBundlingResultMap: this._stylesheetBundlingResultMap,
188
+ emittedFilesCacheMap: this._emittedFilesCacheMap,
165
189
  emitFileSet: buildResult.emitFileSet,
166
190
  };
167
191
  }
168
- async #prepareAsync() {
169
- if (this.#modifiedFileSet.size !== 0) {
170
- this.#debug(`get affected (old deps & old res deps)...`);
171
- this.#perf.run("get affected", () => {
172
- for (const modifiedFile of this.#modifiedFileSet) {
173
- this.#affectedFileSet.add(modifiedFile);
174
- this.#affectedFileSet.adds(...(this.#revDependencyCacheMap.get(modifiedFile) ?? []));
175
- this.#affectedFileSet.adds(...(this.#resourceDependencyCacheMap.get(modifiedFile) ?? []));
192
+ async _prepareAsync(tsconf) {
193
+ if (this._modifiedFileSet.size !== 0) {
194
+ this._debug(`영향 받은 파일 추적 중... (구 의존성)`);
195
+ this._perf.run("영향 받은 파일 추적", () => {
196
+ for (const modifiedFile of this._modifiedFileSet) {
197
+ this._affectedFileSet.add(modifiedFile);
198
+ this._affectedFileSet.adds(...(this._revDepCacheMap.get(modifiedFile) ?? []));
199
+ // .d.ts .js 대응
200
+ if (modifiedFile.endsWith(".d.ts")) {
201
+ const jsEquivalent = PathUtils.norm(modifiedFile.replace(/\.d\.ts$/, ".js"));
202
+ this._affectedFileSet.add(jsEquivalent);
203
+ }
176
204
  }
177
205
  });
178
- this.#debug(`invalidate & clear cache...`);
179
- this.#perf.run("invalidate & clear cache", () => {
180
- this.#stylesheetBundler?.invalidate(this.#affectedFileSet);
181
- for (const affectedFile of this.#affectedFileSet) {
182
- this.#emittedFilesCacheMap.delete(affectedFile);
183
- this.#sourceFileCacheMap.delete(affectedFile);
184
- this.#stylesheetBundlingResultMap.delete(affectedFile);
185
- this.#watchFileSet.delete(affectedFile);
206
+ this._debug(`캐시 무효화 초기화 중...`);
207
+ this._perf.run("캐시 무효화 초기화", () => {
208
+ this._stylesheetBundler?.invalidate(this._affectedFileSet);
209
+ const toDeleteSet = new Set();
210
+ // 초기: 명시적으로 수정된 파일들
211
+ for (const file of this._affectedFileSet) {
212
+ toDeleteSet.add(file);
213
+ // 역방향으로 영향을 받는 파일들도 포함
214
+ const dependents = this._revDepCacheMap.get(file);
215
+ if (dependents) {
216
+ for (const dep of dependents) {
217
+ toDeleteSet.add(dep);
218
+ }
219
+ }
220
+ }
221
+ for (const toDeleteFile of toDeleteSet) {
222
+ this._emittedFilesCacheMap.delete(toDeleteFile);
223
+ this._sourceFileCacheMap.delete(toDeleteFile);
224
+ this._stylesheetBundlingResultMap.delete(toDeleteFile);
225
+ this._watchFileSet.delete(toDeleteFile);
226
+ this._allDepCacheMap.delete(toDeleteFile);
227
+ this._revDepCacheMap.delete(toDeleteFile);
228
+ }
229
+ for (const [key, deps] of this._revDepCacheMap) {
230
+ for (const file of toDeleteSet) {
231
+ deps.delete(file);
232
+ }
233
+ if (deps.size === 0) {
234
+ this._revDepCacheMap.delete(key);
235
+ }
186
236
  }
187
- this.#revDependencyCacheMap.clear();
188
- this.#resourceDependencyCacheMap.clear();
189
237
  });
190
238
  }
191
- this.#debug(`create program...`);
192
- this.#perf.run("create program", () => {
193
- if (this.#isForAngular) {
194
- this.#ngProgram = new NgtscProgram(this.#parsedTsconfig.fileNames, this.#parsedTsconfig.options, this.#compilerHost, this.#ngProgram);
195
- this.#program = this.#ngProgram.getTsProgram();
239
+ this._debug(`ts.Program 생성 중...`);
240
+ this._perf.run("ts.Program 생성", () => {
241
+ if (this._isForAngular) {
242
+ this._ngProgram = new NgtscProgram(tsconf.fileNames, tsconf.options, tsconf.compilerHost, this._ngProgram);
243
+ this._program = this._ngProgram.getTsProgram();
196
244
  }
197
245
  else {
198
- this.#program = ts.createProgram(this.#parsedTsconfig.fileNames, this.#parsedTsconfig.options, this.#compilerHost, this.#program);
246
+ this._program = ts.createProgram(tsconf.fileNames, tsconf.options, tsconf.compilerHost, this._program);
199
247
  }
200
248
  });
201
- if (this.#ngProgram) {
202
- await this.#perf.run("ng analyze", async () => {
203
- await this.#ngProgram.compiler.analyzeAsync();
249
+ if (this._ngProgram) {
250
+ await this._perf.run("Angular 템플릿 분석", async () => {
251
+ await this._ngProgram.compiler.analyzeAsync();
204
252
  });
205
253
  }
206
- const getOrgSourceFile = (sf) => {
207
- if (sf.fileName.endsWith(".ngtypecheck.ts")) {
208
- const orgFileName = sf.fileName.slice(0, -15) + ".ts";
209
- return this.#program.getSourceFile(orgFileName);
210
- }
211
- return sf;
212
- };
213
- const sourceFileSet = new Set(this.#program.getSourceFiles()
214
- .map((sf) => getOrgSourceFile(sf))
215
- .filterExists());
216
- this.#debug(`get new deps...`);
254
+ this._debug(`새 의존성 분석 중...`);
217
255
  const messages = [];
218
- this.#perf.run("get new deps", () => {
219
- const depMap = new Map();
220
- for (const sf of sourceFileSet) {
221
- if (!this.#watchScopePaths.some((scopePath) => PathUtils.isChildPath(sf.fileName, scopePath))) {
222
- continue;
223
- }
224
- const refs = this.#findDeps(sf);
225
- messages.push(...refs.filter((item) => "severity" in item));
226
- depMap.set(PathUtils.norm(sf.fileName), refs
227
- .filter((item) => "fileName" in item)
228
- .filter((item) => this.#watchScopePaths.some((scopePath) => PathUtils.isChildPath(item.fileName, scopePath))));
229
- }
230
- const allDepMap = new Map();
231
- const getAllDeps = (fileName, prevSet) => {
232
- if (allDepMap.has(fileName)) {
233
- return allDepMap.get(fileName);
234
- }
235
- const result = new Set();
236
- const deps = depMap.get(fileName) ?? [];
237
- result.adds(...deps.map((item) => item.fileName));
256
+ this._perf.run(" 의존성 분석", () => {
257
+ const analysed = SdTsDependencyAnalyzer.analyze(this._program, tsconf.compilerHost, this._opt.watchScopePaths, this._allDepCacheMap);
258
+ messages.push(...analysed);
259
+ for (const fileNPath of this._allDepCacheMap.keys()) {
260
+ // const filePath = PathUtils.norm(sf.fileName);
261
+ const deps = this._allDepCacheMap.get(fileNPath);
238
262
  for (const dep of deps) {
239
- const targetDeps = depMap.get(dep.fileName) ?? [];
240
- if (dep.importName === "*") {
241
- for (const targetRefItem of targetDeps.filter((item) => item.exportName != null)) {
242
- if (prevSet?.has(targetRefItem.fileName))
243
- continue;
244
- result.add(targetRefItem.fileName);
245
- result.adds(...getAllDeps(targetRefItem.fileName, new Set(prevSet).adds(...result)));
246
- }
247
- }
248
- else {
249
- for (const targetRefItem of targetDeps.filter((item) => item.exportName
250
- === dep.importName)) {
251
- if (prevSet?.has(targetRefItem.fileName))
252
- continue;
253
- result.add(targetRefItem.fileName);
254
- result.adds(...getAllDeps(targetRefItem.fileName, new Set(prevSet).adds(...result)));
255
- }
256
- }
263
+ const depCache = this._revDepCacheMap.getOrCreate(dep, new Set());
264
+ depCache.add(fileNPath);
257
265
  }
258
- return result;
259
- };
260
- for (const sf of sourceFileSet) {
261
- const deps = getAllDeps(PathUtils.norm(sf.fileName));
262
- allDepMap.set(PathUtils.norm(sf.fileName), deps);
263
- for (const dep of getAllDeps(PathUtils.norm(sf.fileName))) {
264
- const depCache = this.#revDependencyCacheMap.getOrCreate(dep, new Set());
265
- depCache.add(PathUtils.norm(sf.fileName));
266
- }
267
- if (this.#ngProgram) {
268
- if (this.#ngProgram.compiler.ignoreForEmit.has(sf)) {
266
+ if (this._ngProgram) {
267
+ const sf = this._program.getSourceFile(fileNPath);
268
+ if (this._ngProgram.compiler.ignoreForEmit.has(sf)) {
269
269
  continue;
270
270
  }
271
- for (const dep of this.#ngProgram.compiler.getResourceDependencies(sf)) {
272
- const ref = this.#resourceDependencyCacheMap.getOrCreate(PathUtils.norm(dep), new Set());
273
- ref.add(PathUtils.norm(sf.fileName));
271
+ for (const dep of this._ngProgram.compiler.getResourceDependencies(sf)) {
272
+ const ref = this._revDepCacheMap.getOrCreate(PathUtils.norm(dep), new Set());
273
+ ref.add(fileNPath);
274
274
  }
275
275
  }
276
276
  }
277
277
  });
278
- if (this.#modifiedFileSet.size === 0) {
279
- this.#debug(`get affected (init)...`);
280
- this.#perf.run("get affected (init)", () => {
281
- for (const sf of sourceFileSet) {
282
- if (!this.#watchScopePaths.some((scopePath) => PathUtils.isChildPath(sf.fileName, scopePath))) {
278
+ if (this._modifiedFileSet.size === 0) {
279
+ this._debug(`영향 받은 파일 추가 중... (새 의존성)`);
280
+ this._perf.run("영향 받은 파일 추가 중 (새 의존성)", () => {
281
+ for (const fileNPath of this._allDepCacheMap.keys()) {
282
+ if (!this._opt.watchScopePaths.some((scopePath) => PathUtils.isChildPath(fileNPath, scopePath))) {
283
283
  continue;
284
284
  }
285
- this.#affectedFileSet.add(PathUtils.norm(sf.fileName));
285
+ this._affectedFileSet.add(fileNPath);
286
286
  }
287
287
  });
288
288
  }
289
- for (const dep of this.#revDependencyCacheMap.keys()) {
290
- if (this.#modifiedFileSet.has(dep)) {
291
- this.#affectedFileSet.adds(...Array.from(this.#revDependencyCacheMap.get(dep)).mapMany((item) => [
289
+ /**
290
+ * AI가 넣으라고해서 넣었지만 이유는 잘 모르겠음.
291
+ * AI측의 설명은 아래와 같음:
292
+ *
293
+ * 변경된 파일이 .d.ts일 경우, 타입 정보만을 가져다 쓰는 다수의 .ts 파일들이
294
+ * 직접적으로 import하고 있어도 revDepMap에는 기록되지 않을 수 있음.
295
+ *
296
+ * 따라서 .d.ts 파일을 참조하는 모든 파일을 allDepCacheMap에서 역추적하여
297
+ * 간접 영향 파일들을 정확하게 affectedFileSet에 포함시켜야 함.
298
+ *
299
+ * 이 블록은 정확도 보완을 위한 보증 로직이며, 특히 초기 빌드 또는 watch 시 의존성 누락을 방지함.
300
+ */
301
+ for (const modifiedFile of this._modifiedFileSet) {
302
+ // 신규 추가된 파일이 누락되지 않도록 추가하라고 하여 넣음
303
+ if (!this._revDepCacheMap.has(modifiedFile) &&
304
+ !this._allDepCacheMap.has(modifiedFile) &&
305
+ this._program.getSourceFile(modifiedFile)) {
306
+ this._affectedFileSet.add(modifiedFile);
307
+ }
308
+ // AI가 넣으라 한부분에 대한 설명은 여기서부터임
309
+ if (!modifiedFile.endsWith(".d.ts"))
310
+ continue;
311
+ for (const [importer, deps] of this._allDepCacheMap) {
312
+ if (deps.has(modifiedFile)) {
313
+ this._affectedFileSet.add(importer);
314
+ }
315
+ }
316
+ }
317
+ for (const dep of this._revDepCacheMap.keys()) {
318
+ if (this._modifiedFileSet.has(dep)) {
319
+ this._affectedFileSet.adds(...Array.from(this._revDepCacheMap.get(dep)).mapMany((item) => [
292
320
  item,
293
321
  // .d.ts면 .js파일도 affected에 추가
294
322
  item.endsWith(".d.ts") ? PathUtils.norm(item.replace(/\.d\.ts$/, ".js")) : undefined,
@@ -296,29 +324,17 @@ export class SdTsCompiler {
296
324
  }
297
325
  // dep이 emit된적이 없으면 affected에 추가해야함.
298
326
  // dep파일이 추가된후 기존 파일에서 import하면 dep파일이 affected에 포함이 안되는 현상 때문
299
- if (!this.#emittedFilesCacheMap.has(dep)) {
300
- this.#affectedFileSet.add(dep);
301
- }
302
- }
303
- if (this.#ngProgram) {
304
- for (const dep of this.#resourceDependencyCacheMap.keys()) {
305
- if (this.#modifiedFileSet.has(dep)) {
306
- this.#affectedFileSet.adds(...this.#resourceDependencyCacheMap.get(dep));
307
- }
308
- // dep이 emit된적이 없으면 affected에 추가해야함.
309
- // dep파일이 추가된후 기존 파일에서 import하면 dep파일이 affected에 포함이 안되는 현상 때문
310
- if (!this.#emittedFilesCacheMap.has(dep)) {
311
- this.#affectedFileSet.add(dep);
312
- }
327
+ if (!this._emittedFilesCacheMap.has(dep)) {
328
+ this._affectedFileSet.add(dep);
313
329
  }
314
330
  }
315
331
  return {
316
332
  messages,
317
333
  };
318
334
  }
319
- async #lintAsync() {
320
- const lintFilePaths = Array.from(this.#affectedFileSet)
321
- .filter((item) => PathUtils.isChildPath(item, this.#pkgPath))
335
+ async _lintAsync() {
336
+ const lintFilePaths = Array.from(this._affectedFileSet)
337
+ .filter((item) => PathUtils.isChildPath(item, this._opt.pkgPath))
322
338
  .filter((item) => ((!item.endsWith(".d.ts") && item.endsWith(".ts")) ||
323
339
  item.endsWith(".js")))
324
340
  .filter((item) => FsUtils.exists(item));
@@ -326,14 +342,14 @@ export class SdTsCompiler {
326
342
  return [];
327
343
  }
328
344
  const linter = new ESLint({
329
- cwd: this.#pkgPath,
345
+ cwd: this._opt.pkgPath,
330
346
  cache: false,
331
347
  overrideConfig: {
332
348
  languageOptions: {
333
349
  parserOptions: {
334
350
  // parser: tseslint.parser,
335
351
  project: null,
336
- programs: [this.#program],
352
+ programs: [this._program],
337
353
  },
338
354
  },
339
355
  },
@@ -346,206 +362,88 @@ export class SdTsCompiler {
346
362
  // },
347
363
  // ]);
348
364
  }
349
- async #buildAsync() {
365
+ async _buildAsync(tsconf) {
350
366
  const emitFileSet = new Set();
351
367
  const diagnostics = [];
352
- this.#debug(`get diagnostics...`);
353
- this.#perf.run("get program diagnostics", () => {
354
- diagnostics.push(...this.#program.getConfigFileParsingDiagnostics(), ...this.#program.getOptionsDiagnostics(), ...this.#program.getGlobalDiagnostics());
355
- if (this.#ngProgram) {
356
- diagnostics.push(...this.#ngProgram.compiler.getOptionDiagnostics());
368
+ this._debug(`프로그램 진단 수집 중...`);
369
+ this._perf.run("프로그램 진단 수집", () => {
370
+ diagnostics.push(...this._program.getConfigFileParsingDiagnostics(), ...this._program.getOptionsDiagnostics(), ...this._program.getGlobalDiagnostics());
371
+ if (this._ngProgram) {
372
+ diagnostics.push(...this._ngProgram.compiler.getOptionDiagnostics());
357
373
  }
358
374
  });
359
- this.#debug(`get diagnostics of files...`);
360
- for (const affectedFile of this.#affectedFileSet) {
361
- if (!PathUtils.isChildPath(affectedFile, this.#pkgPath)) {
375
+ this._debug(`개별 파일 진단 수집 중...`);
376
+ for (const affectedFile of this._affectedFileSet) {
377
+ if (!PathUtils.isChildPath(affectedFile, this._opt.pkgPath))
362
378
  continue;
363
- }
364
- const affectedSourceFile = this.#program.getSourceFile(affectedFile);
379
+ const affectedSourceFile = this._program.getSourceFile(affectedFile);
365
380
  if (!affectedSourceFile ||
366
- (this.#ngProgram && this.#ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))) {
381
+ (this._ngProgram && this._ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))) {
367
382
  continue;
368
383
  }
369
384
  // this.#debug(`get diagnostics of file ${affectedFile}...`);
370
- this.#perf.run("get file diagnostics", () => {
371
- diagnostics.push(...this.#program.getSyntacticDiagnostics(affectedSourceFile), ...this.#program.getSemanticDiagnostics(affectedSourceFile));
385
+ this._perf.run("개별 파일 진단 수집", () => {
386
+ diagnostics.push(...this._program.getSyntacticDiagnostics(affectedSourceFile), ...this._program.getSemanticDiagnostics(affectedSourceFile));
372
387
  });
373
- if (this.#ngProgram) {
374
- this.#perf.run("get file diagnostics: ng", () => {
375
- if (affectedSourceFile.isDeclarationFile) {
388
+ if (this._ngProgram) {
389
+ this._perf.run("개별 파일 진단 수집(Angular)", () => {
390
+ if (affectedSourceFile.isDeclarationFile)
376
391
  return;
377
- }
378
- diagnostics.push(...this.#ngProgram.compiler.getDiagnosticsForFile(affectedSourceFile, OptimizeFor.WholeProgram));
392
+ diagnostics.push(...this._ngProgram.compiler.getDiagnosticsForFile(affectedSourceFile, OptimizeFor.WholeProgram));
379
393
  });
380
394
  }
381
395
  }
382
- this.#perf.run("emit", () => {
383
- this.#debug(`prepare emit...`);
396
+ this._perf.run("파일 출력 (emit)", () => {
397
+ this._debug(`파일 출력 준비 중...`);
384
398
  let transformers = {};
385
- if (this.#ngProgram) {
399
+ if (this._ngProgram) {
386
400
  transformers = {
387
401
  ...transformers,
388
- ...this.#ngProgram.compiler.prepareEmit().transformers,
402
+ ...this._ngProgram.compiler.prepareEmit().transformers,
389
403
  };
390
- (transformers.before ??= []).push(replaceBootstrap(() => this.#program.getTypeChecker()));
404
+ (transformers.before ??= []).push(replaceBootstrap(() => this._program.getTypeChecker()));
391
405
  (transformers.before ??= []).push(createWorkerTransformer((file, importer) => {
392
406
  const fullPath = path.resolve(path.dirname(importer), file);
393
- const relPath = path.relative(path.resolve(this.#pkgPath, "src"), fullPath);
407
+ const relPath = path.relative(path.resolve(this._opt.pkgPath, "src"), fullPath);
394
408
  return relPath.replace(/\.ts$/, "").replaceAll("\\", "/") + ".js";
395
409
  }));
396
410
  }
397
- // (transformers.before ??= []).push(transformKeys(this.#program));
398
- /*const fixImportTransformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
399
- return (sf) => {
400
- const shouldAppendJs = (importText: string): string | undefined => {
401
- const resolved = ts.resolveModuleName(
402
- importText,
403
- sf.fileName,
404
- this.#program!.getCompilerOptions(),
405
- ts.sys,
406
- );
407
-
408
- const resolvedInfo = resolved.resolvedModule;
409
- if (!resolvedInfo) return undefined;
410
-
411
- const resolvedFileName = resolvedInfo.resolvedFileName;
412
-
413
- // ① .ts / .tsx / .js / .jsx 만 대상
414
- if (!/\.(d\.ts|ts|tsx|js|jsx)$/i.test(resolvedFileName)) return undefined;
415
-
416
- // ② 사용자가 .js, .mjs, .json 등 명시한 경우 무시
417
- if (/\.[mc]?js$|\.json$/i.test(importText)) return undefined;
418
-
419
- // 3. import 경로의 마지막 부분이 파일명(확장자 제외)과 같으면 → .js 붙여야 함
420
- const importLastName = importText.split("/").pop();
421
- const resolvedFileNameOnly = path.basename(resolvedFileName)
422
- .replace(/\.(d\.ts|ts|tsx|js|jsx)$/, "");
423
-
424
- if (importLastName === resolvedFileNameOnly) {
425
- return importText + ".js";
426
- }
427
-
428
- // 4. 그렇지 않으면 → index.ts 같은 루트 패키지 import → .js 붙이지 않음
429
- return undefined;
430
- };
431
-
432
- const visitor: ts.Visitor = (node): ts.Node => {
433
- // import { x } from "./foo"
434
- if (
435
- ts.isImportDeclaration(node) &&
436
- ts.isStringLiteral(node.moduleSpecifier)
437
- ) {
438
- const newPath = shouldAppendJs(node.moduleSpecifier.text);
439
- if (newPath != null) {
440
- return ts.factory.updateImportDeclaration(
441
- node,
442
- node.modifiers,
443
- node.importClause,
444
- ts.factory.createStringLiteral(newPath),
445
- undefined,
446
- );
447
- }
448
- }
449
-
450
- // export * from "./bar"
451
- if (
452
- ts.isExportDeclaration(node) &&
453
- node.moduleSpecifier &&
454
- ts.isStringLiteral(node.moduleSpecifier)
455
- ) {
456
- const newPath = shouldAppendJs(node.moduleSpecifier.text);
457
- if (newPath != null) {
458
- return ts.factory.updateExportDeclaration(
459
- node,
460
- node.modifiers,
461
- node.isTypeOnly,
462
- node.exportClause,
463
- ts.factory.createStringLiteral(newPath),
464
- undefined,
465
- );
466
- }
467
- }
468
-
469
- // dynamic import("./baz")
470
- if (
471
- ts.isCallExpression(node) &&
472
- node.expression.kind === ts.SyntaxKind.ImportKeyword &&
473
- node.arguments.length === 1 &&
474
- ts.isStringLiteral(node.arguments[0])
475
- ) {
476
- const newPath = shouldAppendJs(node.arguments[0].text);
477
- if (newPath != null) {
478
- return ts.factory.updateCallExpression(
479
- node,
480
- node.expression,
481
- undefined,
482
- [ts.factory.createStringLiteral(newPath)],
483
- );
484
- }
485
- }
486
-
487
- return ts.visitEachChild(node, visitor, context);
488
- };
489
-
490
- return ts.visitNode(sf, visitor) as ts.SourceFile;
491
- };
492
- };
493
- (transformers.before ??= []).push(fixImportTransformer);*/
494
- this.#debug(`emit for files...`);
411
+ this._debug(`파일 출력 중...`);
495
412
  // affected에 새로 추가된 파일은 포함되지 않는 현상이 있어 sourceFileSet으로 바꿈
496
413
  // 비교해보니, 딱히 getSourceFiles라서 더 느려지는것 같지는 않음
497
414
  // 그래도 affected로 다시 테스트 (조금이라도 더 빠르게)
498
- for (const affectedFile of this.#affectedFileSet) {
499
- if (this.#emittedFilesCacheMap.has(affectedFile)) {
415
+ for (const affectedFile of this._affectedFileSet) {
416
+ if (this._emittedFilesCacheMap.has(affectedFile))
500
417
  continue;
501
- }
502
- const sf = this.#program.getSourceFile(affectedFile);
503
- if (!sf) {
418
+ const sf = this._program.getSourceFile(affectedFile);
419
+ if (!sf || sf.isDeclarationFile)
504
420
  continue;
505
- }
506
- if (sf.isDeclarationFile) {
421
+ if (this._ngProgram?.compiler.ignoreForEmit.has(sf))
507
422
  continue;
508
- }
509
- if (this.#ngProgram?.compiler.ignoreForEmit.has(sf)) {
423
+ if (this._ngProgram?.compiler.incrementalCompilation.safeToSkipEmit(sf))
510
424
  continue;
511
- }
512
- if (this.#ngProgram?.compiler.incrementalCompilation.safeToSkipEmit(sf)) {
425
+ // 번들이 아닌 외부패키지는 보통 emit안해도 됨
426
+ // but esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
427
+ if (!this._opt.isForBundle && !PathUtils.isChildPath(sf.fileName, this._opt.pkgPath)) {
513
428
  continue;
514
429
  }
515
- // esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
516
- if (!this.#isForBundle) {
517
- if (!PathUtils.isChildPath(sf.fileName, this.#pkgPath)) {
518
- continue;
519
- }
520
- }
521
- this.#program.emit(sf, (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
430
+ this._program.emit(sf, (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
522
431
  if (!sourceFiles || sourceFiles.length === 0) {
523
- this.#compilerHost.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
432
+ tsconf.compilerHost.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
524
433
  return;
525
434
  }
526
435
  const sourceFile = ts.getOriginalNode(sourceFiles[0], ts.isSourceFile);
527
- if (this.#ngProgram) {
528
- if (this.#ngProgram.compiler.ignoreForEmit.has(sourceFile)) {
436
+ if (this._ngProgram) {
437
+ if (this._ngProgram.compiler.ignoreForEmit.has(sourceFile))
529
438
  return;
530
- }
531
- this.#ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
439
+ this._ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
532
440
  }
533
- const emitFileInfoCaches = this.#emittedFilesCacheMap.getOrCreate(PathUtils.norm(sourceFile.fileName), []);
534
- if (PathUtils.isChildPath(sourceFile.fileName, this.#pkgPath)) {
535
- let realFilePath = PathUtils.norm(fileName);
536
- let realText = text;
537
- if (PathUtils.isChildPath(realFilePath, path.resolve(this.#distPath, path.basename(this.#pkgPath), "src"))) {
538
- realFilePath = PathUtils.norm(this.#distPath, path.relative(path.resolve(this.#distPath, path.basename(this.#pkgPath), "src"), realFilePath));
539
- if (fileName.endsWith(".js.map")) {
540
- const sourceMapContents = JSON.parse(realText);
541
- // remove "../../"
542
- sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6);
543
- realText = JSON.stringify(sourceMapContents);
544
- }
545
- }
441
+ const emitFileInfoCaches = this._emittedFilesCacheMap.getOrCreate(PathUtils.norm(sourceFile.fileName), []);
442
+ if (PathUtils.isChildPath(sourceFile.fileName, this._opt.pkgPath)) {
443
+ const real = this._convertOutputToReal(fileName, tsconf.distPath, text);
546
444
  emitFileInfoCaches.push({
547
- outAbsPath: realFilePath,
548
- text: realText,
445
+ outAbsPath: real.filePath,
446
+ text: real.text,
549
447
  });
550
448
  }
551
449
  else {
@@ -556,20 +454,20 @@ export class SdTsCompiler {
556
454
  }
557
455
  });
558
456
  //-- global style
559
- if (this.#globalStyleFilePath != null &&
560
- FsUtils.exists(this.#globalStyleFilePath) &&
561
- !this.#emittedFilesCacheMap.has(this.#globalStyleFilePath)) {
562
- this.#debug(`bundle global style...`);
563
- await this.#perf.run("bundle global style", async () => {
564
- const data = FsUtils.readFile(this.#globalStyleFilePath);
565
- const contents = await this.#bundleStylesheetAsync(data, this.#globalStyleFilePath, this.#globalStyleFilePath);
566
- const emitFileInfos = this.#emittedFilesCacheMap.getOrCreate(this.#globalStyleFilePath, []);
457
+ if (this._opt.globalStyleFilePath != null &&
458
+ FsUtils.exists(this._opt.globalStyleFilePath) &&
459
+ !this._emittedFilesCacheMap.has(this._opt.globalStyleFilePath)) {
460
+ this._debug(`전역 스타일 번들링 중...`);
461
+ await this._perf.run("전역 스타일 번들링", async () => {
462
+ const data = FsUtils.readFile(this._opt.globalStyleFilePath);
463
+ const stylesheetBundlingResult = await this._bundleStylesheetAsync(data, this._opt.globalStyleFilePath, this._opt.globalStyleFilePath);
464
+ const emitFileInfos = this._emittedFilesCacheMap.getOrCreate(this._opt.globalStyleFilePath, []);
567
465
  emitFileInfos.push({
568
- outAbsPath: PathUtils.norm(this.#pkgPath, path.relative(path.resolve(this.#pkgPath, "src"), this.#globalStyleFilePath)
466
+ outAbsPath: PathUtils.norm(this._opt.pkgPath, path.relative(path.resolve(this._opt.pkgPath, "src"), this._opt.globalStyleFilePath)
569
467
  .replace(/\.scss$/, ".css")),
570
- text: contents,
468
+ text: stylesheetBundlingResult.contents ?? "",
571
469
  });
572
- emitFileSet.add(this.#globalStyleFilePath);
470
+ emitFileSet.add(this._opt.globalStyleFilePath);
573
471
  });
574
472
  }
575
473
  return {
@@ -577,190 +475,23 @@ export class SdTsCompiler {
577
475
  diagnostics,
578
476
  };
579
477
  }
580
- #debug(...msg) {
581
- this.#logger.debug(`[${path.basename(this.#pkgPath)}]`, ...msg);
582
- }
583
- #findDeps(sf) {
584
- const deps = [];
585
- const tc = this.#program.getTypeChecker();
586
- const visit = (node) => {
587
- if (ts.isExportDeclaration(node)) {
588
- if (node.moduleSpecifier) {
589
- const moduleSymbol = tc.getSymbolAtLocation(node.moduleSpecifier);
590
- if (!moduleSymbol) {
591
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
592
- deps.push({
593
- filePath: PathUtils.norm(sf.fileName),
594
- line: pos.line,
595
- char: pos.character,
596
- code: undefined,
597
- severity: "error",
598
- message: "export moduleSymbol not found",
599
- type: "deps",
600
- });
601
- return;
602
- }
603
- const decls = moduleSymbol.getDeclarations();
604
- if (!decls) {
605
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
606
- deps.push({
607
- filePath: PathUtils.norm(sf.fileName),
608
- line: pos.line,
609
- char: pos.character,
610
- code: undefined,
611
- severity: "error",
612
- message: "export decls not found",
613
- type: "deps",
614
- });
615
- return;
616
- }
617
- const namedBindings = node.exportClause;
618
- if (namedBindings && ts.isNamedExports(namedBindings)) {
619
- for (const el of namedBindings.elements) {
620
- for (const decl of decls) {
621
- deps.push({
622
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
623
- importName: el.name.text,
624
- exportName: el.propertyName?.text ?? el.name.text,
625
- });
626
- }
627
- }
628
- }
629
- else {
630
- if (!moduleSymbol.exports) {
631
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
632
- deps.push({
633
- filePath: PathUtils.norm(sf.fileName),
634
- line: pos.line,
635
- char: pos.character,
636
- code: undefined,
637
- severity: "error",
638
- message: "moduleSymbol exports not found",
639
- type: "deps",
640
- });
641
- return;
642
- }
643
- for (const decl of decls) {
644
- for (const key of moduleSymbol.exports.keys()) {
645
- deps.push({
646
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
647
- importName: key.toString(),
648
- exportName: key.toString(),
649
- });
650
- }
651
- }
652
- }
653
- }
478
+ _convertOutputToReal(filePath, distPath, text) {
479
+ let realFilePath = PathUtils.norm(filePath);
480
+ let realText = text;
481
+ const srcRelBasePath = path.resolve(distPath, path.basename(this._opt.pkgPath), "src");
482
+ if (PathUtils.isChildPath(realFilePath, srcRelBasePath)) {
483
+ realFilePath = PathUtils.norm(distPath, path.relative(srcRelBasePath, realFilePath));
484
+ // source map 위치 정확히 찾아가기
485
+ if (filePath.endsWith(".js.map")) {
486
+ const sourceMapContents = JSON.parse(realText);
487
+ sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6); // remove "../../"
488
+ realText = JSON.stringify(sourceMapContents);
654
489
  }
655
- else if (ts.isImportDeclaration(node)) {
656
- const moduleSymbol = tc.getSymbolAtLocation(node.moduleSpecifier);
657
- if (!moduleSymbol) {
658
- if (ts.isStringLiteral(node.moduleSpecifier)
659
- && node.moduleSpecifier.text.startsWith("./")) {
660
- deps.push({
661
- fileName: PathUtils.norm(path.resolve(path.dirname(sf.fileName), node.moduleSpecifier.text + ".ts")),
662
- importName: "*",
663
- });
664
- // const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
665
- // deps.push({
666
- // filePath: PathUtil.norm(sf.fileName),
667
- // line: pos.line,
668
- // char: pos.character,
669
- // code: undefined,
670
- // severity: "error",
671
- // message: `import moduleSymbol not found (${node.moduleSpecifier.text})`,
672
- // type: "deps",
673
- // });
674
- // return;
675
- }
676
- /*else {
677
- throw new NeverEntryError(`import moduleSymbol: ${sf.fileName} ${node.moduleSpecifier["text"]}`);
678
- }*/
679
- }
680
- else {
681
- const decls = moduleSymbol.getDeclarations();
682
- if (!decls) {
683
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
684
- deps.push({
685
- filePath: PathUtils.norm(sf.fileName),
686
- line: pos.line,
687
- char: pos.character,
688
- code: undefined,
689
- severity: "error",
690
- message: `import decls not found (${moduleSymbol.name})`,
691
- type: "deps",
692
- });
693
- return;
694
- }
695
- const namedBindings = node.importClause?.namedBindings;
696
- if (namedBindings && ts.isNamedImports(namedBindings)) {
697
- for (const el of namedBindings.elements) {
698
- for (const decl of decls) {
699
- deps.push({
700
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
701
- importName: el.name.text,
702
- });
703
- }
704
- }
705
- }
706
- else {
707
- for (const decl of decls) {
708
- deps.push({
709
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
710
- importName: "*",
711
- });
712
- }
713
- }
714
- }
715
- }
716
- if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
717
- if (ts.isStringLiteral(node.arguments[0]) && node.arguments[0].text.startsWith("./")) {
718
- const moduleSymbol = tc.getSymbolAtLocation(node.arguments[0]);
719
- if (!moduleSymbol) {
720
- deps.push({
721
- fileName: PathUtils.norm(path.resolve(path.dirname(sf.fileName), node.arguments[0].text + ".ts")),
722
- importName: "*",
723
- });
724
- // const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
725
- // deps.push({
726
- // filePath: PathUtil.norm(sf.fileName),
727
- // line: pos.line,
728
- // char: pos.character,
729
- // code: undefined,
730
- // severity: "error",
731
- // message: `import() moduleSymbol not found (${node.arguments[0].text})`,
732
- // type: "deps",
733
- // });
734
- // return;
735
- }
736
- else {
737
- const decls = moduleSymbol.getDeclarations();
738
- if (!decls) {
739
- const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
740
- deps.push({
741
- filePath: PathUtils.norm(sf.fileName),
742
- line: pos.line,
743
- char: pos.character,
744
- code: undefined,
745
- severity: "error",
746
- message: `import() decls not found (${node.arguments[0].text})`,
747
- type: "deps",
748
- });
749
- return;
750
- }
751
- for (const decl of decls) {
752
- deps.push({
753
- fileName: PathUtils.norm(decl.getSourceFile().fileName),
754
- importName: "*",
755
- });
756
- }
757
- }
758
- }
759
- }
760
- node.forEachChild(visit);
761
- };
762
- sf.forEachChild(visit);
763
- return deps;
490
+ }
491
+ return { filePath: realFilePath, text: realText };
492
+ }
493
+ _debug(...msg) {
494
+ this._logger.debug(`[${path.basename(this._opt.pkgPath)}]`, ...msg);
764
495
  }
765
496
  }
766
497
  //# sourceMappingURL=sd-ts-compiler.js.map