@simplysm/sd-cli 14.0.8 → 14.0.9

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 (254) hide show
  1. package/dist/angular/client-transform-stylesheet.d.ts.map +1 -1
  2. package/dist/angular/client-transform-stylesheet.js.map +1 -1
  3. package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
  4. package/dist/angular/vite-angular-plugin.js +15 -8
  5. package/dist/angular/vite-angular-plugin.js.map +1 -1
  6. package/dist/angular/vite-postcss-inline-plugin.d.ts.map +1 -1
  7. package/dist/angular/vite-postcss-inline-plugin.js.map +1 -1
  8. package/dist/capacitor/capacitor.d.ts.map +1 -1
  9. package/dist/capacitor/capacitor.js +41 -41
  10. package/dist/capacitor/capacitor.js.map +1 -1
  11. package/dist/commands/build.d.ts.map +1 -1
  12. package/dist/commands/build.js.map +1 -1
  13. package/dist/commands/check.d.ts.map +1 -1
  14. package/dist/commands/check.js.map +1 -1
  15. package/dist/commands/dev.d.ts.map +1 -1
  16. package/dist/commands/dev.js.map +1 -1
  17. package/dist/commands/lint.d.ts.map +1 -1
  18. package/dist/commands/lint.js.map +1 -1
  19. package/dist/commands/publish.d.ts.map +1 -1
  20. package/dist/commands/publish.js.map +1 -1
  21. package/dist/commands/replace-deps.d.ts.map +1 -1
  22. package/dist/commands/replace-deps.js.map +1 -1
  23. package/dist/commands/typecheck.d.ts.map +1 -1
  24. package/dist/commands/typecheck.js +12 -12
  25. package/dist/commands/typecheck.js.map +1 -1
  26. package/dist/commands/watch.d.ts.map +1 -1
  27. package/dist/commands/watch.js.map +1 -1
  28. package/dist/electron/electron.d.ts.map +1 -1
  29. package/dist/electron/electron.js +26 -27
  30. package/dist/electron/electron.js.map +1 -1
  31. package/dist/engines/BaseEngine.d.ts +1 -5
  32. package/dist/engines/BaseEngine.d.ts.map +1 -1
  33. package/dist/engines/BaseEngine.js +7 -16
  34. package/dist/engines/BaseEngine.js.map +1 -1
  35. package/dist/engines/NgtscEngine.d.ts.map +1 -1
  36. package/dist/engines/NgtscEngine.js +10 -11
  37. package/dist/engines/NgtscEngine.js.map +1 -1
  38. package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
  39. package/dist/engines/ServerEsbuildEngine.js +10 -11
  40. package/dist/engines/ServerEsbuildEngine.js.map +1 -1
  41. package/dist/engines/TscEngine.d.ts.map +1 -1
  42. package/dist/engines/TscEngine.js +10 -11
  43. package/dist/engines/TscEngine.js.map +1 -1
  44. package/dist/engines/ViteEngine.d.ts.map +1 -1
  45. package/dist/engines/ViteEngine.js +3 -13
  46. package/dist/engines/ViteEngine.js.map +1 -1
  47. package/dist/engines/index.d.ts.map +1 -1
  48. package/dist/engines/index.js.map +1 -1
  49. package/dist/engines/types.d.ts +3 -6
  50. package/dist/engines/types.d.ts.map +1 -1
  51. package/dist/engines/types.js.map +1 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/infra/ResultCollector.d.ts +1 -1
  55. package/dist/infra/ResultCollector.d.ts.map +1 -1
  56. package/dist/infra/ResultCollector.js.map +1 -1
  57. package/dist/infra/SignalHandler.d.ts.map +1 -1
  58. package/dist/infra/SignalHandler.js.map +1 -1
  59. package/dist/infra/WorkerManager.d.ts.map +1 -1
  60. package/dist/infra/WorkerManager.js.map +1 -1
  61. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  62. package/dist/orchestrators/BuildOrchestrator.js +30 -61
  63. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  64. package/dist/orchestrators/DevWatchOrchestrator.d.ts +2 -0
  65. package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
  66. package/dist/orchestrators/DevWatchOrchestrator.js +40 -44
  67. package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
  68. package/dist/sd-cli-entry.d.ts.map +1 -1
  69. package/dist/sd-cli-entry.js +2 -13
  70. package/dist/sd-cli-entry.js.map +1 -1
  71. package/dist/sd-cli.d.ts.map +1 -1
  72. package/dist/sd-cli.js.map +1 -1
  73. package/dist/sd-config.types.d.ts.map +1 -1
  74. package/dist/sd-config.types.js.map +1 -1
  75. package/dist/utils/SdCliReporter.d.ts +18 -0
  76. package/dist/utils/SdCliReporter.d.ts.map +1 -0
  77. package/dist/utils/SdCliReporter.js +144 -0
  78. package/dist/utils/SdCliReporter.js.map +1 -0
  79. package/dist/utils/angular-build.d.ts.map +1 -1
  80. package/dist/utils/angular-build.js.map +1 -1
  81. package/dist/utils/angular-compiler.d.ts.map +1 -1
  82. package/dist/utils/angular-compiler.js +11 -4
  83. package/dist/utils/angular-compiler.js.map +1 -1
  84. package/dist/utils/build-env.d.ts.map +1 -1
  85. package/dist/utils/build-env.js +2 -1
  86. package/dist/utils/build-env.js.map +1 -1
  87. package/dist/utils/concurrency.d.ts.map +1 -1
  88. package/dist/utils/concurrency.js.map +1 -1
  89. package/dist/utils/copy-public.d.ts.map +1 -1
  90. package/dist/utils/copy-public.js +21 -21
  91. package/dist/utils/copy-public.js.map +1 -1
  92. package/dist/utils/copy-src.d.ts.map +1 -1
  93. package/dist/utils/copy-src.js +12 -12
  94. package/dist/utils/copy-src.js.map +1 -1
  95. package/dist/utils/diagnostic-utils.d.ts.map +1 -1
  96. package/dist/utils/diagnostic-utils.js +3 -2
  97. package/dist/utils/diagnostic-utils.js.map +1 -1
  98. package/dist/utils/engine-stop.d.ts.map +1 -1
  99. package/dist/utils/engine-stop.js.map +1 -1
  100. package/dist/utils/esbuild-config.d.ts.map +1 -1
  101. package/dist/utils/esbuild-config.js +2 -0
  102. package/dist/utils/esbuild-config.js.map +1 -1
  103. package/dist/utils/generate-pwa-icons.d.ts.map +1 -1
  104. package/dist/utils/generate-pwa-icons.js.map +1 -1
  105. package/dist/utils/hmr-candidates.d.ts.map +1 -1
  106. package/dist/utils/hmr-candidates.js.map +1 -1
  107. package/dist/utils/lint-utils.d.ts.map +1 -1
  108. package/dist/utils/lint-utils.js.map +1 -1
  109. package/dist/utils/lint-with-program.d.ts.map +1 -1
  110. package/dist/utils/lint-with-program.js +7 -3
  111. package/dist/utils/lint-with-program.js.map +1 -1
  112. package/dist/utils/ngtsc-build-core.d.ts +2 -10
  113. package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
  114. package/dist/utils/ngtsc-build-core.js +16 -15
  115. package/dist/utils/ngtsc-build-core.js.map +1 -1
  116. package/dist/utils/orchestrator-utils.d.ts.map +1 -1
  117. package/dist/utils/orchestrator-utils.js.map +1 -1
  118. package/dist/utils/output-path-rewriter.d.ts.map +1 -1
  119. package/dist/utils/output-path-rewriter.js +7 -7
  120. package/dist/utils/output-path-rewriter.js.map +1 -1
  121. package/dist/utils/output-utils.d.ts.map +1 -1
  122. package/dist/utils/output-utils.js +1 -1
  123. package/dist/utils/output-utils.js.map +1 -1
  124. package/dist/utils/package-utils.d.ts +4 -0
  125. package/dist/utils/package-utils.d.ts.map +1 -1
  126. package/dist/utils/package-utils.js +34 -13
  127. package/dist/utils/package-utils.js.map +1 -1
  128. package/dist/utils/rebuild-manager.d.ts +1 -1
  129. package/dist/utils/rebuild-manager.d.ts.map +1 -1
  130. package/dist/utils/rebuild-manager.js +3 -1
  131. package/dist/utils/rebuild-manager.js.map +1 -1
  132. package/dist/utils/replace-deps.d.ts.map +1 -1
  133. package/dist/utils/replace-deps.js +10 -10
  134. package/dist/utils/replace-deps.js.map +1 -1
  135. package/dist/utils/scss-compiler.d.ts.map +1 -1
  136. package/dist/utils/scss-compiler.js.map +1 -1
  137. package/dist/utils/sd-config.d.ts.map +1 -1
  138. package/dist/utils/sd-config.js +2 -3
  139. package/dist/utils/sd-config.js.map +1 -1
  140. package/dist/utils/tsc-build.d.ts +3 -1
  141. package/dist/utils/tsc-build.d.ts.map +1 -1
  142. package/dist/utils/tsc-build.js +7 -4
  143. package/dist/utils/tsc-build.js.map +1 -1
  144. package/dist/utils/tsconfig.d.ts.map +1 -1
  145. package/dist/utils/tsconfig.js.map +1 -1
  146. package/dist/utils/typecheck-non-package.d.ts.map +1 -1
  147. package/dist/utils/typecheck-non-package.js +10 -5
  148. package/dist/utils/typecheck-non-package.js.map +1 -1
  149. package/dist/utils/typecheck-serialization.d.ts.map +1 -1
  150. package/dist/utils/typecheck-serialization.js.map +1 -1
  151. package/dist/utils/vite-config.d.ts.map +1 -1
  152. package/dist/utils/vite-config.js +2 -1
  153. package/dist/utils/vite-config.js.map +1 -1
  154. package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
  155. package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
  156. package/dist/utils/worker-events.d.ts +1 -1
  157. package/dist/utils/worker-events.d.ts.map +1 -1
  158. package/dist/utils/worker-events.js +1 -0
  159. package/dist/utils/worker-events.js.map +1 -1
  160. package/dist/utils/worker-utils.d.ts +1 -1
  161. package/dist/utils/worker-utils.d.ts.map +1 -1
  162. package/dist/utils/worker-utils.js +3 -1
  163. package/dist/utils/worker-utils.js.map +1 -1
  164. package/dist/vitest-plugin.d.ts.map +1 -1
  165. package/dist/vitest-plugin.js.map +1 -1
  166. package/dist/workers/client.worker.d.ts.map +1 -1
  167. package/dist/workers/client.worker.js +4 -0
  168. package/dist/workers/client.worker.js.map +1 -1
  169. package/dist/workers/library-build.worker.d.ts +2 -10
  170. package/dist/workers/library-build.worker.d.ts.map +1 -1
  171. package/dist/workers/library-build.worker.js +38 -14
  172. package/dist/workers/library-build.worker.js.map +1 -1
  173. package/dist/workers/lint.worker.d.ts.map +1 -1
  174. package/dist/workers/lint.worker.js.map +1 -1
  175. package/dist/workers/ngtsc-build.worker.d.ts.map +1 -1
  176. package/dist/workers/ngtsc-build.worker.js +40 -14
  177. package/dist/workers/ngtsc-build.worker.js.map +1 -1
  178. package/dist/workers/server-build.worker.d.ts +2 -10
  179. package/dist/workers/server-build.worker.d.ts.map +1 -1
  180. package/dist/workers/server-build.worker.js +28 -19
  181. package/dist/workers/server-build.worker.js.map +1 -1
  182. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  183. package/dist/workers/server-runtime.worker.js.map +1 -1
  184. package/package.json +4 -4
  185. package/src/angular/vite-angular-plugin.ts +18 -9
  186. package/src/capacitor/capacitor.ts +41 -41
  187. package/src/commands/typecheck.ts +12 -12
  188. package/src/electron/electron.ts +26 -27
  189. package/src/engines/BaseEngine.ts +8 -19
  190. package/src/engines/NgtscEngine.ts +11 -11
  191. package/src/engines/ServerEsbuildEngine.ts +11 -11
  192. package/src/engines/TscEngine.ts +11 -11
  193. package/src/engines/ViteEngine.ts +3 -14
  194. package/src/engines/types.ts +3 -6
  195. package/src/infra/ResultCollector.ts +1 -1
  196. package/src/orchestrators/BuildOrchestrator.ts +31 -62
  197. package/src/orchestrators/DevWatchOrchestrator.ts +41 -44
  198. package/src/sd-cli-entry.ts +2 -12
  199. package/src/utils/SdCliReporter.ts +177 -0
  200. package/src/utils/angular-compiler.ts +11 -4
  201. package/src/utils/build-env.ts +2 -1
  202. package/src/utils/copy-public.ts +21 -21
  203. package/src/utils/copy-src.ts +12 -12
  204. package/src/utils/diagnostic-utils.ts +3 -2
  205. package/src/utils/esbuild-config.ts +2 -0
  206. package/src/utils/lint-with-program.ts +7 -3
  207. package/src/utils/ngtsc-build-core.ts +18 -18
  208. package/src/utils/output-path-rewriter.ts +7 -7
  209. package/src/utils/output-utils.ts +1 -1
  210. package/src/utils/package-utils.ts +37 -13
  211. package/src/utils/rebuild-manager.ts +4 -2
  212. package/src/utils/replace-deps.ts +10 -10
  213. package/src/utils/sd-config.ts +2 -3
  214. package/src/utils/tsc-build.ts +9 -4
  215. package/src/utils/typecheck-non-package.ts +10 -5
  216. package/src/utils/vite-config.ts +2 -1
  217. package/src/utils/worker-events.ts +2 -1
  218. package/src/utils/worker-utils.ts +3 -1
  219. package/src/workers/client.worker.ts +4 -0
  220. package/src/workers/library-build.worker.ts +48 -18
  221. package/src/workers/ngtsc-build.worker.ts +48 -13
  222. package/src/workers/server-build.worker.ts +30 -23
  223. package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +11 -7
  224. package/tests/angular/vite-angular-plugin-lint.spec.ts +6 -1
  225. package/tests/capacitor/capacitor-build.spec.ts +5 -0
  226. package/tests/capacitor/capacitor-icon.spec.ts +5 -0
  227. package/tests/capacitor/capacitor-init.spec.ts +5 -0
  228. package/tests/capacitor/capacitor-run.spec.ts +5 -0
  229. package/tests/capacitor/capacitor-workspace.spec.ts +5 -0
  230. package/tests/commands/typecheck.spec.ts +20 -31
  231. package/tests/electron/electron.spec.ts +5 -0
  232. package/tests/engines/base-engine.spec.ts +15 -21
  233. package/tests/engines/engine-lint-integration.spec.ts +5 -10
  234. package/tests/engines/ngtsc-engine.spec.ts +27 -41
  235. package/tests/engines/server-esbuild-engine.spec.ts +18 -29
  236. package/tests/engines/tsc-engine.spec.ts +14 -23
  237. package/tests/engines/vite-engine.spec.ts +10 -15
  238. package/tests/infra/result-collector.spec.ts +2 -2
  239. package/tests/orchestrators/build-orchestrator.spec.ts +19 -29
  240. package/tests/orchestrators/dev-watch-orchestrator.spec.ts +110 -95
  241. package/tests/utils/copy-src.spec.ts +25 -19
  242. package/tests/utils/diagnostic-utils.spec.ts +72 -0
  243. package/tests/utils/ngtsc-build-core-angular-compiler.spec.ts +2 -3
  244. package/tests/utils/output-path-rewriter.spec.ts +7 -6
  245. package/tests/utils/output-utils.spec.ts +5 -5
  246. package/tests/utils/rebuild-manager.spec.ts +1 -1
  247. package/tests/utils/sd-config.spec.ts +4 -0
  248. package/tests/utils/tsc-build.spec.ts +23 -5
  249. package/tests/workers/library-build-worker.spec.ts +113 -20
  250. package/tests/workers/ngtsc-build-lint.spec.ts +3 -6
  251. package/tests/workers/ngtsc-build-worker.spec.ts +11 -13
  252. package/tests/workers/server-build-lint.spec.ts +4 -1
  253. package/tests/workers/server-build-worker.spec.ts +19 -22
  254. package/tests/angular/migration-cleanup.spec.ts +0 -59
@@ -16,22 +16,12 @@ import fs from "fs";
16
16
  import { fileURLToPath } from "url";
17
17
  import { EventEmitter } from "node:events";
18
18
  import { consola, LogLevels } from "consola";
19
+ import { SdCliReporter } from "./utils/SdCliReporter";
19
20
 
20
21
  Error.stackTraceLimit = Infinity;
21
22
  EventEmitter.defaultMaxListeners = 100;
22
23
 
23
- // consola 타임스탬프에 밀리초 포함
24
- for (const reporter of consola.options.reporters) {
25
- const r = reporter as { formatDate?: (date: Date, opts: { date?: boolean }) => string };
26
- if (typeof r.formatDate === "function") {
27
- const orig = r.formatDate.bind(r);
28
- r.formatDate = (date: Date, opts: { date?: boolean }) => {
29
- const base = orig(date, opts);
30
- if (base === "") return "";
31
- return `${base}.${String(date.getMilliseconds()).padStart(3, "0")}`;
32
- };
33
- }
34
- }
24
+ consola.options.reporters = [new SdCliReporter()];
35
25
 
36
26
  const COMMAND_NAMES = ["check", "watch", "dev", "build", "publish", "replace-deps"];
37
27
 
@@ -0,0 +1,177 @@
1
+ import type { ConsolaReporter, LogObject, ConsolaOptions } from "consola";
2
+ import { formatWithOptions } from "node:util";
3
+ import { sep } from "node:path";
4
+
5
+ // -- Constants ----------------------------------------------------------------
6
+
7
+ const TYPE_ICONS: Record<string, string> = {
8
+ error: "✖",
9
+ fatal: "✖",
10
+ ready: "✔",
11
+ warn: "⚠",
12
+ info: "ℹ",
13
+ success: "✔",
14
+ debug: "⚙",
15
+ trace: "→",
16
+ fail: "✖",
17
+ start: "◐",
18
+ log: "",
19
+ };
20
+
21
+ type AnsiColor = "gray" | "red" | "green" | "yellow" | "cyan" | "magenta";
22
+
23
+ const TYPE_COLORS: Record<string, AnsiColor | undefined> = {
24
+ info: "cyan",
25
+ fail: "red",
26
+ success: "green",
27
+ ready: "green",
28
+ start: "magenta",
29
+ };
30
+
31
+ const LEVEL_COLORS: Record<number, AnsiColor | undefined> = {
32
+ 0: "red",
33
+ 1: "yellow",
34
+ };
35
+
36
+ const ANSI_CODES: Record<AnsiColor, string> = {
37
+ gray: "\x1b[90m",
38
+ red: "\x1b[31m",
39
+ green: "\x1b[32m",
40
+ yellow: "\x1b[33m",
41
+ cyan: "\x1b[36m",
42
+ magenta: "\x1b[35m",
43
+ };
44
+
45
+ const ANSI_RESET = "\x1b[0m";
46
+
47
+ // -- Helpers ------------------------------------------------------------------
48
+
49
+ function colorize(color: AnsiColor, text: string, enabled: boolean): string {
50
+ if (!enabled) return text;
51
+ return `${ANSI_CODES[color]}${text}${ANSI_RESET}`;
52
+ }
53
+
54
+ function writeStream(data: string, stream: NodeJS.WritableStream): void {
55
+ const s = stream as NodeJS.WritableStream & { __write?: typeof stream.write };
56
+ const write = s.__write ?? s.write;
57
+ write.call(stream, data);
58
+ }
59
+
60
+ function detectColorSupport(): boolean {
61
+ if (process.env["NO_COLOR"] != null) return false;
62
+ if (process.env["FORCE_COLOR"] != null) return true;
63
+ if (process.stdout.isTTY === true) return true;
64
+ // 워커 스레드에서는 process.stdout.isTTY가 없음. Windows 터미널은 ANSI 지원.
65
+ return process.platform === "win32";
66
+ }
67
+
68
+ // -- Reporter -----------------------------------------------------------------
69
+
70
+ interface FormatOpts {
71
+ date?: boolean;
72
+ colors: boolean;
73
+ compact?: boolean | number;
74
+ errorLevel?: number;
75
+ }
76
+
77
+ /**
78
+ * sd-cli 전용 consola reporter.
79
+ * 모든 로그를 `[tag] icon message time` 형식으로 통일한다.
80
+ */
81
+ export class SdCliReporter implements ConsolaReporter {
82
+ log(logObj: LogObject, ctx: { options: ConsolaOptions }): void {
83
+ const opts: FormatOpts = {
84
+ ...ctx.options.formatOptions,
85
+ colors: detectColorSupport(),
86
+ };
87
+
88
+ const line = this._formatLogObj(logObj, opts);
89
+ const stream =
90
+ logObj.level < 2
91
+ ? ctx.options.stderr ?? process.stderr
92
+ : ctx.options.stdout ?? process.stdout;
93
+
94
+ writeStream(line + "\n", stream);
95
+ }
96
+
97
+ private _formatLogObj(logObj: LogObject, opts: FormatOpts): string {
98
+ const formattedArgs = this._formatArgs(logObj.args, opts);
99
+ const [message, ...additional] = formattedArgs.split("\n");
100
+
101
+ if (logObj.type === "box") {
102
+ return this._formatBox(logObj, formattedArgs);
103
+ }
104
+
105
+ // Build: [tag] icon message time
106
+ const tag = logObj.tag !== "" ? colorize("gray", `[${logObj.tag}]`, opts.colors) : "";
107
+ const icon = this._formatIcon(logObj, opts.colors);
108
+ const date = this._formatDate(logObj.date, opts);
109
+ const coloredDate = date !== "" ? colorize("gray", date, opts.colors) : "";
110
+
111
+ let fullLine = [tag, icon, message, coloredDate].filter(Boolean).join(" ");
112
+
113
+ if (additional.length > 0) {
114
+ fullLine += "\n" + additional.join("\n");
115
+ }
116
+
117
+ if (logObj.type === "trace") {
118
+ const err = new Error("Trace: " + logObj.message);
119
+ fullLine += this._formatStack(err.stack ?? "", err.message);
120
+ }
121
+
122
+ const isBadge = (logObj as LogObject & { badge?: boolean }).badge ?? logObj.level < 2;
123
+ return isBadge ? "\n" + fullLine + "\n" : fullLine;
124
+ }
125
+
126
+ private _formatArgs(args: unknown[], opts: FormatOpts): string {
127
+ const processed = args.map((arg) => {
128
+ if (arg != null && typeof arg === "object" && typeof (arg as Error).stack === "string") {
129
+ return this._formatError(arg as Error, opts);
130
+ }
131
+ return arg;
132
+ });
133
+ return formatWithOptions({ colors: opts.colors, compact: opts.compact }, ...processed);
134
+ }
135
+
136
+ private _formatError(err: Error, opts: FormatOpts): string {
137
+ const message = err.message;
138
+ const stack = err.stack != null ? this._formatStack(err.stack, message, opts) : "";
139
+ const level = opts.errorLevel ?? 0;
140
+ const prefix = level > 0 ? `${" ".repeat(level)}[cause]: ` : "";
141
+ const cause =
142
+ err.cause instanceof Error
143
+ ? "\n\n" + this._formatError(err.cause, { ...opts, errorLevel: level + 1 })
144
+ : "";
145
+ return prefix + message + "\n" + stack + cause;
146
+ }
147
+
148
+ private _formatStack(stack: string, message: string, opts?: FormatOpts): string {
149
+ const cwd = process.cwd() + sep;
150
+ const indent = " ".repeat((opts?.errorLevel ?? 0) + 1);
151
+ const lines = stack
152
+ .split("\n")
153
+ .splice(message.split("\n").length)
154
+ .map((l) => l.trim().replace("file://", "").replace(cwd, ""));
155
+ return `\n${indent}` + lines.map((l) => ` ${l}`).join(`\n${indent}`);
156
+ }
157
+
158
+ private _formatIcon(logObj: LogObject, useColors: boolean): string {
159
+ const icon = TYPE_ICONS[logObj.type] ?? "";
160
+ if (icon === "") return "";
161
+ const color: AnsiColor = TYPE_COLORS[logObj.type] ?? LEVEL_COLORS[logObj.level] ?? "gray";
162
+ return colorize(color, icon, useColors);
163
+ }
164
+
165
+ private _formatDate(date: Date, opts: FormatOpts): string {
166
+ if (!opts.date) return "";
167
+ const base = date.toLocaleTimeString();
168
+ return `${base}.${String(date.getMilliseconds()).padStart(3, "0")}`;
169
+ }
170
+
171
+ private _formatBox(logObj: LogObject, message: string): string {
172
+ const tag = logObj.tag !== "" ? `[${logObj.tag}]` : "";
173
+ const title = (logObj as LogObject & { title?: string }).title;
174
+ const lines = [tag, title, ...message.split("\n")].filter(Boolean);
175
+ return "\n" + lines.map((l) => ` > ${l}`).join("\n") + "\n";
176
+ }
177
+ }
@@ -2,6 +2,7 @@ import path from "path";
2
2
  import { createHash } from "crypto";
3
3
  import ts from "typescript";
4
4
  import { consola } from "consola";
5
+ import { pathx } from "@simplysm/core-node";
5
6
  import { NgtscProgram, OptimizeFor } from "./angular-build";
6
7
  import { collectHmrCandidates } from "./hmr-candidates.js";
7
8
 
@@ -67,7 +68,7 @@ export class AngularSourceFileCache extends Map<string, ts.SourceFile> {
67
68
 
68
69
  invalidate(files: Iterable<string>): void {
69
70
  for (const file of files) {
70
- const normalized = file.replace(/\\/g, "/");
71
+ const normalized = pathx.posix(file);
71
72
  this.delete(normalized);
72
73
  this.modifiedFiles.add(normalized);
73
74
  }
@@ -312,7 +313,7 @@ export class AngularCompiler {
312
313
  }
313
314
  const sf = node.getSourceFile();
314
315
  let relativePath = path.relative(host.getCurrentDirectory(), sf.fileName);
315
- relativePath = relativePath.replace(/\\/g, "/");
316
+ relativePath = pathx.posix(relativePath);
316
317
 
317
318
  const updateId = encodeURIComponent(
318
319
  `${host.getCanonicalFileName(relativePath)}@${node.name?.text}`,
@@ -352,6 +353,7 @@ export class AngularCompiler {
352
353
  angularCompiler: NgCompiler,
353
354
  sourceFileCache?: AngularSourceFileCache,
354
355
  ): Set<ts.SourceFile> {
356
+ logger.debug("affected 파일 탐색 시작");
355
357
  const affectedFiles = new Set<ts.SourceFile>();
356
358
 
357
359
  while (true) {
@@ -393,7 +395,7 @@ export class AngularCompiler {
393
395
  // modifiedFiles의 경로를 정규화한 Set 생성 (Windows backslash 대응)
394
396
  const normalizedModifiedFiles = new Set<string>();
395
397
  for (const f of sourceFileCache.modifiedFiles) {
396
- normalizedModifiedFiles.add(f.replace(/\\/g, "/"));
398
+ normalizedModifiedFiles.add(pathx.posix(f));
397
399
  }
398
400
 
399
401
  for (const sourceFile of builderProgram.getSourceFiles()) {
@@ -402,7 +404,7 @@ export class AngularCompiler {
402
404
  }
403
405
  const resourceDependencies = angularCompiler.getResourceDependencies(sourceFile);
404
406
  for (const resourceDep of resourceDependencies) {
405
- if (normalizedModifiedFiles.has(resourceDep.replace(/\\/g, "/"))) {
407
+ if (normalizedModifiedFiles.has(pathx.posix(resourceDep))) {
406
408
  this._diagnosticCache.delete(sourceFile);
407
409
  affectedFiles.add(sourceFile);
408
410
  break;
@@ -411,6 +413,7 @@ export class AngularCompiler {
411
413
  }
412
414
  }
413
415
 
416
+ logger.debug(`affected 파일 탐색 완료 (${affectedFiles.size}개)`);
414
417
  return affectedFiles;
415
418
  }
416
419
 
@@ -420,6 +423,7 @@ export class AngularCompiler {
420
423
  affectedFiles: ReadonlySet<ts.SourceFile>;
421
424
  templateUpdates?: Map<string, string>;
422
425
  }> {
426
+ logger.debug("증분 업데이트 시작");
423
427
  const sourceFileCache = this._options.sourceFileCache;
424
428
  if (sourceFileCache == null) {
425
429
  throw new Error("sourceFileCache가 없으면 incremental rebuild를 수행할 수 없습니다");
@@ -432,6 +436,7 @@ export class AngularCompiler {
432
436
  if (this._ngtscProgram == null || this._builderProgram == null) {
433
437
  throw new Error("initialize()를 먼저 호출해야 합니다");
434
438
  }
439
+ logger.debug("emitAffectedFiles 시작");
435
440
 
436
441
  const compilerOptions = this._builderProgram.getCompilerOptions();
437
442
 
@@ -519,6 +524,7 @@ export class AngularCompiler {
519
524
  }
520
525
 
521
526
  // 7. sourceFilter 적용 후 yield
527
+ logger.debug(`emitAffectedFiles 완료 (${emitResults.length}개 파일)`);
522
528
  for (const result of emitResults) {
523
529
  if (options?.sourceFilter != null && !options.sourceFilter(result.sourceFileName)) {
524
530
  continue;
@@ -531,6 +537,7 @@ export class AngularCompiler {
531
537
  if (this._ngtscProgram == null || this._builderProgram == null) {
532
538
  throw new Error("initialize()를 먼저 호출해야 합니다");
533
539
  }
540
+ logger.debug("진단 수집 시작");
534
541
 
535
542
  const angularCompiler = this._ngtscProgram.compiler;
536
543
  const builderProgram = this._builderProgram;
@@ -1,11 +1,12 @@
1
1
  import path from "path";
2
2
  import fs from "fs/promises";
3
+ import { pathx } from "@simplysm/core-node";
3
4
 
4
5
  /**
5
6
  * Get version from root package.json
6
7
  */
7
8
  export async function getVersion(cwd: string): Promise<string> {
8
- const pkgJsonPath = path.join(cwd, "package.json");
9
+ const pkgJsonPath = pathx.posix(path.join(cwd, "package.json"));
9
10
  const pkgJsonContent = await fs.readFile(pkgJsonPath, "utf-8");
10
11
  const pkgJson = JSON.parse(pkgJsonContent) as { version?: string };
11
12
  return pkgJson.version ?? "0.0.0";
@@ -13,18 +13,18 @@ import {
13
13
  * @param includeDev Whether to include public-dev/ (true only in dev mode)
14
14
  */
15
15
  export async function copyPublicFiles(pkgDir: string, includeDev: boolean): Promise<void> {
16
- const distDir = path.join(pkgDir, "dist");
16
+ const distDir = pathx.posix(path.join(pkgDir, "dist"));
17
17
  await fsx.mkdir(distDir);
18
18
 
19
19
  // Copy public/
20
- const publicDir = path.join(pkgDir, "public");
20
+ const publicDir = pathx.posix(path.join(pkgDir, "public"));
21
21
  if (await fsx.exists(publicDir)) {
22
22
  const files = await fsx.glob("**/*", { cwd: publicDir, absolute: true });
23
23
  await Promise.all(
24
24
  files.map(async (file) => {
25
- const relativePath = path.relative(publicDir, file);
26
- const distPath = path.join(distDir, relativePath);
27
- await fsx.mkdir(path.dirname(distPath));
25
+ const relativePath = pathx.posix(path.relative(publicDir, file));
26
+ const distPath = pathx.posix(path.join(distDir, relativePath));
27
+ await fsx.mkdir(pathx.posix(path.dirname(distPath)));
28
28
  await fsx.copy(file, distPath);
29
29
  }),
30
30
  );
@@ -32,14 +32,14 @@ export async function copyPublicFiles(pkgDir: string, includeDev: boolean): Prom
32
32
 
33
33
  // Copy public-dev/ (overlay: overwrites public/)
34
34
  if (includeDev) {
35
- const publicDevDir = path.join(pkgDir, "public-dev");
35
+ const publicDevDir = pathx.posix(path.join(pkgDir, "public-dev"));
36
36
  if (await fsx.exists(publicDevDir)) {
37
37
  const files = await fsx.glob("**/*", { cwd: publicDevDir, absolute: true });
38
38
  await Promise.all(
39
39
  files.map(async (file) => {
40
- const relativePath = path.relative(publicDevDir, file);
41
- const distPath = path.join(distDir, relativePath);
42
- await fsx.mkdir(path.dirname(distPath));
40
+ const relativePath = pathx.posix(path.relative(publicDevDir, file));
41
+ const distPath = pathx.posix(path.join(distDir, relativePath));
42
+ await fsx.mkdir(pathx.posix(path.dirname(distPath)));
43
43
  await fsx.copy(file, distPath);
44
44
  }),
45
45
  );
@@ -59,9 +59,9 @@ export async function watchPublicFiles(
59
59
  pkgDir: string,
60
60
  includeDev: boolean,
61
61
  ): Promise<FsWatcher | undefined> {
62
- const distDir = path.join(pkgDir, "dist");
63
- const publicDir = path.join(pkgDir, "public");
64
- const publicDevDir = path.join(pkgDir, "public-dev");
62
+ const distDir = pathx.posix(path.join(pkgDir, "dist"));
63
+ const publicDir = pathx.posix(path.join(pkgDir, "public"));
64
+ const publicDevDir = pathx.posix(path.join(pkgDir, "public-dev"));
65
65
 
66
66
  // Initial copy
67
67
  await copyPublicFiles(pkgDir, includeDev);
@@ -69,10 +69,10 @@ export async function watchPublicFiles(
69
69
  // Collect watch target paths
70
70
  const watchPaths: string[] = [];
71
71
  if (await fsx.exists(publicDir)) {
72
- watchPaths.push(path.join(publicDir, "**/*"));
72
+ watchPaths.push(pathx.posix(path.join(publicDir, "**/*")));
73
73
  }
74
74
  if (includeDev && (await fsx.exists(publicDevDir))) {
75
- watchPaths.push(path.join(publicDevDir, "**/*"));
75
+ watchPaths.push(pathx.posix(path.join(publicDevDir, "**/*")));
76
76
  }
77
77
 
78
78
  if (watchPaths.length === 0) {
@@ -91,22 +91,22 @@ export async function watchPublicFiles(
91
91
  sourceDir = publicDir;
92
92
  }
93
93
 
94
- const relPath = path.relative(sourceDir, filePath);
95
- const distPath = path.join(distDir, relPath);
94
+ const relPath = pathx.posix(path.relative(sourceDir, filePath));
95
+ const distPath = pathx.posix(path.join(distDir, relPath));
96
96
 
97
97
  if (event === "unlink") {
98
98
  // If deleted from public, don't delete if same file exists in public-dev
99
99
  if (sourceDir === publicDir && includeDev) {
100
- const devOverride = path.join(publicDevDir, relPath);
100
+ const devOverride = pathx.posix(path.join(publicDevDir, relPath));
101
101
  if (await fsx.exists(devOverride)) {
102
102
  continue;
103
103
  }
104
104
  }
105
105
  // If deleted from public-dev, restore from public if it exists (fallback restore)
106
106
  if (sourceDir === publicDevDir && includeDev) {
107
- const publicFallback = path.join(publicDir, relPath);
107
+ const publicFallback = pathx.posix(path.join(publicDir, relPath));
108
108
  if (await fsx.exists(publicFallback)) {
109
- await fsx.mkdir(path.dirname(distPath));
109
+ await fsx.mkdir(pathx.posix(path.dirname(distPath)));
110
110
  await fsx.copy(publicFallback, distPath);
111
111
  continue;
112
112
  }
@@ -115,12 +115,12 @@ export async function watchPublicFiles(
115
115
  } else if (event === "add" || event === "change") {
116
116
  // If changed in public, skip if same file exists in public-dev (overlay takes priority)
117
117
  if (sourceDir === publicDir && includeDev) {
118
- const devOverride = path.join(publicDevDir, relPath);
118
+ const devOverride = pathx.posix(path.join(publicDevDir, relPath));
119
119
  if (await fsx.exists(devOverride)) {
120
120
  continue;
121
121
  }
122
122
  }
123
- await fsx.mkdir(path.dirname(distPath));
123
+ await fsx.mkdir(pathx.posix(path.dirname(distPath)));
124
124
  await fsx.copy(filePath, distPath);
125
125
  }
126
126
  }
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { fsx, FsWatcher } from "@simplysm/core-node";
2
+ import { fsx, pathx, FsWatcher } from "@simplysm/core-node";
3
3
 
4
4
  /**
5
5
  * Copy files matching glob patterns from src/ to dist/
@@ -9,16 +9,16 @@ import { fsx, FsWatcher } from "@simplysm/core-node";
9
9
  * @param patterns Array of glob patterns (relative to src/)
10
10
  */
11
11
  export async function copySrcFiles(pkgDir: string, patterns: string[]): Promise<void> {
12
- const srcDir = path.join(pkgDir, "src");
13
- const distDir = path.join(pkgDir, "dist");
12
+ const srcDir = pathx.posix(path.join(pkgDir, "src"));
13
+ const distDir = pathx.posix(path.join(pkgDir, "dist"));
14
14
 
15
15
  for (const pattern of patterns) {
16
16
  const files = await fsx.glob(pattern, { cwd: srcDir, absolute: true });
17
17
  await Promise.all(
18
18
  files.map(async (file) => {
19
- const relativePath = path.relative(srcDir, file);
20
- const distPath = path.join(distDir, relativePath);
21
- await fsx.mkdir(path.dirname(distPath));
19
+ const relativePath = pathx.posix(path.relative(srcDir, file));
20
+ const distPath = pathx.posix(path.join(distDir, relativePath));
21
+ await fsx.mkdir(pathx.posix(path.dirname(distPath)));
22
22
  await fsx.copy(file, distPath);
23
23
  }),
24
24
  );
@@ -34,25 +34,25 @@ export async function copySrcFiles(pkgDir: string, patterns: string[]): Promise<
34
34
  * @returns FsWatcher instance (call close() on shutdown)
35
35
  */
36
36
  export async function watchCopySrcFiles(pkgDir: string, patterns: string[]): Promise<FsWatcher> {
37
- const srcDir = path.join(pkgDir, "src");
38
- const distDir = path.join(pkgDir, "dist");
37
+ const srcDir = pathx.posix(path.join(pkgDir, "src"));
38
+ const distDir = pathx.posix(path.join(pkgDir, "dist"));
39
39
 
40
40
  // Initial copy
41
41
  await copySrcFiles(pkgDir, patterns);
42
42
 
43
43
  // Start watch
44
- const watchPaths = patterns.map((p) => path.join(srcDir, p));
44
+ const watchPaths = patterns.map((p) => pathx.posix(path.join(srcDir, p)));
45
45
  const watcher = await FsWatcher.watch(watchPaths);
46
46
 
47
47
  watcher.onChange({ delay: 300 }, async (changes) => {
48
48
  for (const { event, path: filePath } of changes) {
49
- const relPath = path.relative(srcDir, filePath);
50
- const distPath = path.join(distDir, relPath);
49
+ const relPath = pathx.posix(path.relative(srcDir, filePath));
50
+ const distPath = pathx.posix(path.join(distDir, relPath));
51
51
 
52
52
  if (event === "unlink") {
53
53
  await fsx.rm(distPath);
54
54
  } else if (event === "add" || event === "change") {
55
- await fsx.mkdir(path.dirname(distPath));
55
+ await fsx.mkdir(pathx.posix(path.dirname(distPath)));
56
56
  await fsx.copy(filePath, distPath);
57
57
  }
58
58
  }
@@ -1,4 +1,5 @@
1
1
  import ts from "typescript";
2
+ import { pathx } from "@simplysm/core-node";
2
3
 
3
4
  /**
4
5
  * 워크스페이스 스코프 진단 필터.
@@ -8,8 +9,8 @@ import ts from "typescript";
8
9
  export function isWorkspaceDiagnostic(diagnostic: ts.Diagnostic, cwd: string): boolean {
9
10
  if (diagnostic.file == null) return true;
10
11
 
11
- const normalized = diagnostic.file.fileName.replace(/\\/g, "/");
12
- const normalizedCwd = cwd.replace(/\\/g, "/").replace(/\/$/, "");
12
+ const normalized = pathx.posix(diagnostic.file.fileName);
13
+ const normalizedCwd = pathx.posix(cwd).replace(/\/$/, "");
13
14
  return normalized.startsWith(normalizedCwd + "/") && !normalized.includes("/node_modules/");
14
15
  }
15
16
 
@@ -15,6 +15,7 @@ const logger = consola.withTag("sd:cli:esbuild-config");
15
15
  * - Skip writing if content matches existing file to preserve timestamps
16
16
  */
17
17
  export async function writeChangedOutputFiles(outputFiles: esbuild.OutputFile[]): Promise<boolean> {
18
+ logger.debug(`변경된 출력 파일 쓰기 시작 (${outputFiles.length}개)`);
18
19
  let hasChanges = false;
19
20
  await Promise.all(
20
21
  outputFiles.map(async (file) => {
@@ -40,6 +41,7 @@ export async function writeChangedOutputFiles(outputFiles: esbuild.OutputFile[])
40
41
  await fs.writeFile(file.path, finalText);
41
42
  }),
42
43
  );
44
+ logger.debug(`변경된 출력 파일 쓰기 완료 (변경: ${String(hasChanges)})`);
43
45
  return hasChanges;
44
46
  }
45
47
 
@@ -1,6 +1,7 @@
1
1
  import path from "path";
2
2
  import type ts from "typescript";
3
3
  import { ESLint } from "eslint";
4
+ import { pathx } from "@simplysm/core-node";
4
5
  import { consola } from "consola";
5
6
 
6
7
  const logger = consola.withTag("sd:cli:lint-with-program");
@@ -58,11 +59,12 @@ export class LintWithProgramRunner {
58
59
  * Includes all workspace source files (cwd scope), excludes .d.ts, node_modules, and Angular shims.
59
60
  */
60
61
  private _extractFiles(program: ts.Program): string[] {
61
- const normalizedCwd = this._cwd.replace(/\\/g, "/");
62
+ logger.debug(`[${this._pkgName}] 린트 대상 파일 추출 시작`);
63
+ const normalizedCwd = pathx.posix(this._cwd);
62
64
  const files: string[] = [];
63
65
 
64
66
  for (const sf of program.getSourceFiles()) {
65
- const fileName = sf.fileName.replace(/\\/g, "/");
67
+ const fileName = pathx.posix(sf.fileName);
66
68
 
67
69
  // Must be within workspace root
68
70
  if (!fileName.startsWith(normalizedCwd + "/")) {
@@ -87,6 +89,7 @@ export class LintWithProgramRunner {
87
89
  files.push(sf.fileName);
88
90
  }
89
91
 
92
+ logger.debug(`[${this._pkgName}] 린트 대상 파일 추출 완료 (${files.length}개)`);
90
93
  return files;
91
94
  }
92
95
 
@@ -103,7 +106,7 @@ export class LintWithProgramRunner {
103
106
 
104
107
  // When affectedFiles is provided, intersect with extracted files
105
108
  if (affectedFiles != null) {
106
- files = files.filter((f) => affectedFiles.has(f.replace(/\\/g, "/")));
109
+ files = files.filter((f) => affectedFiles.has(pathx.posix(f)));
107
110
  }
108
111
 
109
112
  if (files.length === 0) {
@@ -127,6 +130,7 @@ export class LintWithProgramRunner {
127
130
 
128
131
  // Create new ESLint instance when cache policy changes or on first call
129
132
  if (this._eslint == null || this._lastUseCache !== useCache) {
133
+ logger.debug(`[${this._pkgName}] ESLint 인스턴스 생성 (cache: ${String(useCache)})`);
130
134
  // ESLint Flat Config serializes languageOptions via languageOptionsToJSON(),
131
135
  // which recurses into parserOptions and throws on ts.Program methods.
132
136
  // Adding toJSON() to parserOptions returns a serializable representation