@kubb/cli 5.3.7 → 5.3.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 (98) hide show
  1. package/dist/{Telemetry-CvHQevSK.cjs → Telemetry-B3kc9gFh.cjs} +3 -2
  2. package/dist/Telemetry-B3kc9gFh.cjs.map +1 -0
  3. package/dist/{Telemetry-CJcQkQOT.js → Telemetry-Dw5s6abr.js} +3 -2
  4. package/dist/Telemetry-Dw5s6abr.js.map +1 -0
  5. package/dist/connect-DFd7vS7_.js +11 -0
  6. package/dist/connect-DFd7vS7_.js.map +1 -0
  7. package/dist/connect-bCESn4L8.cjs +11 -0
  8. package/dist/connect-bCESn4L8.cjs.map +1 -0
  9. package/dist/{generate-CXozTFxt.cjs → generate-Cl5hFNdC.cjs} +5 -18
  10. package/dist/generate-Cl5hFNdC.cjs.map +1 -0
  11. package/dist/{generate-BhfKNqK4.js → generate-Del-D4hj.js} +5 -18
  12. package/dist/generate-Del-D4hj.js.map +1 -0
  13. package/dist/index.cjs +9 -18
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.js +9 -18
  16. package/dist/index.js.map +1 -1
  17. package/dist/{init-lhSbdp7u.cjs → init-C5trdwre.cjs} +3 -3
  18. package/dist/{init-lhSbdp7u.cjs.map → init-C5trdwre.cjs.map} +1 -1
  19. package/dist/{init-DrITcv-T.js → init-CBAmaPkc.js} +3 -3
  20. package/dist/{init-DrITcv-T.js.map → init-CBAmaPkc.js.map} +1 -1
  21. package/dist/login-5QXmUaQN.cjs +11 -0
  22. package/dist/login-5QXmUaQN.cjs.map +1 -0
  23. package/dist/login-D-1xSRxT.js +11 -0
  24. package/dist/login-D-1xSRxT.js.map +1 -0
  25. package/dist/logout-B2vHFyac.cjs +14 -0
  26. package/dist/logout-B2vHFyac.cjs.map +1 -0
  27. package/dist/logout-XB0v_SY-.js +14 -0
  28. package/dist/logout-XB0v_SY-.js.map +1 -0
  29. package/dist/output-CdnsdD1b.js +342 -0
  30. package/dist/output-CdnsdD1b.js.map +1 -0
  31. package/dist/output-DBBTlqPw.cjs +438 -0
  32. package/dist/output-DBBTlqPw.cjs.map +1 -0
  33. package/dist/{package-B955x8sl.cjs → package-BwIh-jWZ.cjs} +2 -2
  34. package/dist/package-BwIh-jWZ.cjs.map +1 -0
  35. package/dist/package-CDw1YdJ8.js +6 -0
  36. package/dist/package-CDw1YdJ8.js.map +1 -0
  37. package/dist/{run-DDgF_Z4A.js → run-Bd-IwEch.js} +48 -301
  38. package/dist/run-Bd-IwEch.js.map +1 -0
  39. package/dist/{run-DUzjWd_N.js → run-C5HkzCUt.js} +27 -23
  40. package/dist/run-C5HkzCUt.js.map +1 -0
  41. package/dist/{run-3DJ8CszL.cjs → run-CIonyF_t.cjs} +99 -299
  42. package/dist/run-CIonyF_t.cjs.map +1 -0
  43. package/dist/{run-BI8Fbzpd.js → run-CLhU86LM.js} +3 -3
  44. package/dist/{run-BI8Fbzpd.js.map → run-CLhU86LM.js.map} +1 -1
  45. package/dist/{run-kdxqOL8u.js → run-CeTu1eSk.js} +2 -2
  46. package/dist/{run-kdxqOL8u.js.map → run-CeTu1eSk.js.map} +1 -1
  47. package/dist/{run-CALspAGk.cjs → run-CxglY4u6.cjs} +27 -23
  48. package/dist/run-CxglY4u6.cjs.map +1 -0
  49. package/dist/{run-T7GVVdoF.cjs → run-DPuKGV20.cjs} +3 -3
  50. package/dist/{run-T7GVVdoF.cjs.map → run-DPuKGV20.cjs.map} +1 -1
  51. package/dist/{run-C5cKBTCX.js → run-Df4x0iX_.js} +3 -3
  52. package/dist/{run-C5cKBTCX.js.map → run-Df4x0iX_.js.map} +1 -1
  53. package/dist/{run-Ckw7BXJp.cjs → run-SybIXc6w.cjs} +3 -3
  54. package/dist/{run-Ckw7BXJp.cjs.map → run-SybIXc6w.cjs.map} +1 -1
  55. package/dist/{run-VK50TiB7.cjs → run-ZPt0Sw9r.cjs} +2 -2
  56. package/dist/{run-VK50TiB7.cjs.map → run-ZPt0Sw9r.cjs.map} +1 -1
  57. package/dist/shared-C5doIqKD.js +64 -0
  58. package/dist/shared-C5doIqKD.js.map +1 -0
  59. package/dist/shared-CaRlFkWL.cjs +87 -0
  60. package/dist/shared-CaRlFkWL.cjs.map +1 -0
  61. package/dist/snapshot-DWNiluEm.cjs +261 -0
  62. package/dist/snapshot-DWNiluEm.cjs.map +1 -0
  63. package/dist/snapshot-Dhwz_MpL.js +260 -0
  64. package/dist/snapshot-Dhwz_MpL.js.map +1 -0
  65. package/dist/status-Cc3wqN4l.js +11 -0
  66. package/dist/status-Cc3wqN4l.js.map +1 -0
  67. package/dist/status-CxMC5vAa.cjs +11 -0
  68. package/dist/status-CxMC5vAa.cjs.map +1 -0
  69. package/dist/studio-CQ24bwGz.cjs +155 -0
  70. package/dist/studio-CQ24bwGz.cjs.map +1 -0
  71. package/dist/studio-jqKsQP5_.js +155 -0
  72. package/dist/studio-jqKsQP5_.js.map +1 -0
  73. package/dist/{utils-s6yetN6W.js → utils-B1_SWa35.js} +356 -525
  74. package/dist/utils-B1_SWa35.js.map +1 -0
  75. package/dist/{utils-CMU1rLfY.cjs → utils-BP0xq5AK.cjs} +345 -508
  76. package/dist/utils-BP0xq5AK.cjs.map +1 -0
  77. package/package.json +9 -9
  78. package/dist/Telemetry-CJcQkQOT.js.map +0 -1
  79. package/dist/Telemetry-CvHQevSK.cjs.map +0 -1
  80. package/dist/generate-BhfKNqK4.js.map +0 -1
  81. package/dist/generate-CXozTFxt.cjs.map +0 -1
  82. package/dist/output-DH2Uoy4x.js +0 -118
  83. package/dist/output-DH2Uoy4x.js.map +0 -1
  84. package/dist/output-kmklyYIv.cjs +0 -172
  85. package/dist/output-kmklyYIv.cjs.map +0 -1
  86. package/dist/package-B955x8sl.cjs.map +0 -1
  87. package/dist/package-CE0Mgpd_.js +0 -6
  88. package/dist/package-CE0Mgpd_.js.map +0 -1
  89. package/dist/run-3DJ8CszL.cjs.map +0 -1
  90. package/dist/run-CALspAGk.cjs.map +0 -1
  91. package/dist/run-DDgF_Z4A.js.map +0 -1
  92. package/dist/run-DUzjWd_N.js.map +0 -1
  93. package/dist/studio-B33RIU3-.cjs +0 -111
  94. package/dist/studio-B33RIU3-.cjs.map +0 -1
  95. package/dist/studio-Cq_th0uM.js +0 -112
  96. package/dist/studio-Cq_th0uM.js.map +0 -1
  97. package/dist/utils-CMU1rLfY.cjs.map +0 -1
  98. package/dist/utils-s6yetN6W.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-qbf5tadS.cjs");
2
2
  const require_errors = require("./errors-C-wEou02.cjs");
3
3
  const require_agent = require("./agent-BPqs9VRj.cjs");
4
- const require_Telemetry = require("./Telemetry-CvHQevSK.cjs");
5
- const require_output = require("./output-kmklyYIv.cjs");
4
+ const require_Telemetry = require("./Telemetry-B3kc9gFh.cjs");
5
+ const require_output = require("./output-DBBTlqPw.cjs");
6
6
  let node_util = require("node:util");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_process = require("node:process");
@@ -123,608 +123,444 @@ function tokenize(command) {
123
123
  return (command.match(/[^\s"']+|"([^"]*)"|'([^']*)'/g) ?? []).map((token) => token.replace(/^["']|["']$/g, ""));
124
124
  }
125
125
  //#endregion
126
- //#region ../../internals/utils/src/time.ts
126
+ //#region src/loggers/createLogger.ts
127
127
  /**
128
- * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
129
- * Rounds to 2 decimal places for sub-millisecond precision without noise.
130
- *
131
- * @example
132
- * ```ts
133
- * const start = process.hrtime()
134
- * doWork()
135
- * getElapsedMs(start) // 42.35
136
- * ```
137
- */
138
- function getElapsedMs(hrStart) {
139
- const [seconds, nanoseconds] = process.hrtime(hrStart);
140
- const ms = seconds * 1e3 + nanoseconds / 1e6;
141
- return Math.round(ms * 100) / 100;
142
- }
143
- /**
144
- * Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
145
- *
146
- * @example
147
- * ```ts
148
- * formatMs(250) // '250ms'
149
- * formatMs(1500) // '1.50s'
150
- * formatMs(90000) // '1m 30.0s'
151
- * ```
152
- */
153
- function formatMs(ms) {
154
- if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
155
- if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
156
- return `${Math.round(ms)}ms`;
157
- }
158
- //#endregion
159
- //#region src/loggers/banner.ts
160
- /**
161
- * Parses a CSS hex color string (`#RGB`) into its RGB channels.
162
- * Falls back to `255` for any channel that cannot be parsed.
163
- */
164
- function parseHex(color) {
165
- const int = Number.parseInt(color.replace("#", ""), 16);
166
- return Number.isNaN(int) ? {
167
- r: 255,
168
- g: 255,
169
- b: 255
170
- } : {
171
- r: int >> 16 & 255,
172
- g: int >> 8 & 255,
173
- b: int & 255
174
- };
175
- }
176
- /**
177
- * Returns a function that wraps a string in a 24-bit ANSI true-color escape sequence
178
- * for the given hex color.
179
- */
180
- function hex(color) {
181
- const { r, g, b } = parseHex(color);
182
- return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
183
- }
184
- function gradient(colorStops, text) {
185
- const chars = text.split("");
186
- return chars.map((char, i) => {
187
- const t = chars.length <= 1 ? 0 : i / (chars.length - 1);
188
- const seg = Math.min(Math.floor(t * (colorStops.length - 1)), colorStops.length - 2);
189
- const lt = t * (colorStops.length - 1) - seg;
190
- const from = parseHex(colorStops[seg]);
191
- const to = parseHex(colorStops[seg + 1]);
192
- return `\x1b[38;2;${Math.round(from.r + (to.r - from.r) * lt)};${Math.round(from.g + (to.g - from.g) * lt)};${Math.round(from.b + (to.b - from.b) * lt)}m${char}\x1b[0m`;
193
- }).join("");
194
- }
195
- /**
196
- * ANSI color functions for each part of the Kubb mascot illustration.
128
+ * The three output phases, in the order the generate runner runs them.
197
129
  */
198
- const palette = {
199
- /**
200
- * Top cap of the skittle.
201
- */
202
- lid: hex("#F55A17"),
203
- /**
204
- * Upper wood body.
205
- */
206
- woodTop: hex("#F5A217"),
207
- /**
208
- * Middle wood body.
209
- */
210
- woodMid: hex("#F58517"),
211
- /**
212
- * Base wood body.
213
- */
214
- woodBase: hex("#B45309"),
215
- /**
216
- * Eye whites.
217
- */
218
- eye: hex("#FFFFFF"),
219
- /**
220
- * Highlight accent.
221
- */
222
- highlight: hex("#adadc6"),
223
- /**
224
- * Cheek blush.
225
- */
226
- blush: hex("#FDA4AF")
130
+ const PHASES = {
131
+ format: {
132
+ running: "Formatting",
133
+ success: "Formatted",
134
+ failure: "Formatting failed"
135
+ },
136
+ lint: {
137
+ running: "Linting",
138
+ success: "Linted",
139
+ failure: "Linting failed"
140
+ },
141
+ hooks: {
142
+ running: "Running post-generate hooks",
143
+ success: "Post-generate hooks completed",
144
+ failure: "Post-generate hooks failed",
145
+ showHookResults: true
146
+ }
227
147
  };
228
148
  /**
229
- * Generates the Kubb mascot welcome banner as an ANSI-colored string.
230
- *
231
- * @example
232
- * ```ts
233
- * console.log(getIntro({ title: 'kubb.config.ts', description: 'generating…', version: '5.0.0', areEyesOpen: true }))
234
- * ```
149
+ * Drops the blank lines a subprocess leaves at either end of its output, so its block does not open
150
+ * or close on an empty row.
235
151
  */
236
- function getIntro({ title, description, version, areEyesOpen }) {
237
- const kubbVersion = gradient([
238
- "#F58517",
239
- "#F5A217",
240
- "#F55A17"
241
- ], `KUBB v${version}`);
242
- const eyeTop = areEyesOpen ? palette.eye("█▀█") : palette.eye("───");
243
- const eyeBottom = areEyesOpen ? palette.eye("▀▀▀") : palette.eye("───");
244
- return `
245
- ${palette.lid("▄▄▄▄▄▄▄▄▄▄▄▄▄")}
246
- ${palette.woodTop("█ ")}${palette.highlight("▄▄")}${palette.woodTop(" ")}${palette.highlight("▄▄")}${palette.woodTop(" █")} ${kubbVersion}
247
- ${palette.woodMid("█ ")}${eyeTop}${palette.woodMid(" ")}${eyeTop}${palette.woodMid(" █")} ${(0, node_util.styleText)("gray", title)}
248
- ${palette.woodMid("█ ")}${eyeBottom}${palette.woodMid(" ")}${palette.blush("◡")}${palette.woodMid(" ")}${eyeBottom}${palette.woodMid(" █")} ${(0, node_util.styleText)("yellow", "➜")} ${(0, node_util.styleText)("white", description)}
249
- ${palette.woodBase("▀▀▀▀▀▀▀▀▀▀▀▀▀")}
250
- `;
152
+ function trimBlankEdges(lines) {
153
+ const start = lines.findIndex((line) => line.trim());
154
+ if (start === -1) return [];
155
+ return lines.slice(start, lines.findLastIndex((line) => line.trim()) + 1);
251
156
  }
252
157
  /**
253
- * Formats a millisecond duration with a threshold-based ANSI color.
254
- * `≤ 500 ms` → green · `≤ 1 000 ms` → yellow · `> 1 000 ms` → red.
158
+ * Wires a {@link LoggerWriter} onto the run's hooks. This owns what a run says and in what order:
159
+ * one group per config, a step over the plugin work, a progress bar for the writes, one step per
160
+ * output phase, then the summary and the group's closing result. The writer only decides how each
161
+ * line looks, so every logger reports the same run the same way.
255
162
  *
256
163
  * @example
257
164
  * ```ts
258
- * formatMsWithColor(200) // '\x1b[32m200ms\x1b[39m'
259
- * formatMsWithColor(800) // '\x1b[33m800ms\x1b[39m'
260
- * formatMsWithColor(1500) // '\x1b[31m1.50s\x1b[39m'
165
+ * export const plainLogger = { name: 'plain', install: (context, options) => createLogger(writer)(context, options) }
261
166
  * ```
262
167
  */
263
- function formatMsWithColor(ms) {
264
- const formatted = formatMs(ms);
265
- if (ms <= 500) return (0, node_util.styleText)("green", formatted);
266
- if (ms <= 1e3) return (0, node_util.styleText)("yellow", formatted);
267
- return (0, node_util.styleText)("red", formatted);
268
- }
269
- //#endregion
270
- //#region src/loggers/clackLogger.ts
271
- /**
272
- * TTY logger for local development, with spinners and progress bars.
273
- */
274
- const clackLogger = {
275
- name: "clack",
276
- install(context, options) {
168
+ function createLogger(writer) {
169
+ return function install(context, options) {
277
170
  const logLevel = options?.logLevel ?? _kubb_core.logLevel.info;
171
+ const silent = logLevel <= _kubb_core.logLevel.silent;
278
172
  const state = {
279
173
  ...createProgressCounters(),
280
- spinner: _clack_prompts.spinner(),
281
- isSpinning: false,
282
- runningPlugins: /* @__PURE__ */ new Set(),
283
- activeProgress: /* @__PURE__ */ new Map(),
284
- activeHookLogs: /* @__PURE__ */ new Map()
174
+ spinner: null,
175
+ progress: null,
176
+ hooks: /* @__PURE__ */ new Map(),
177
+ /**
178
+ * Plugin results held back while the generation step owns the line.
179
+ */
180
+ pluginLines: [],
181
+ phase: null,
182
+ groupOpen: false
285
183
  };
286
- function stopActiveProgress() {
287
- for (const [, active] of state.activeProgress) {
288
- if (active.interval) clearInterval(active.interval);
289
- active.progressBar?.stop();
184
+ function text(message) {
185
+ return formatMessage(message, logLevel);
186
+ }
187
+ function startSpinner(message) {
188
+ if (silent) return;
189
+ state.spinner = writer.spinner();
190
+ state.spinner.start(text(message));
191
+ }
192
+ function stopSpinner(message, status = "success") {
193
+ const spinner = state.spinner;
194
+ if (!spinner) return;
195
+ state.spinner = null;
196
+ if (message === void 0) {
197
+ spinner.clear();
198
+ return;
199
+ }
200
+ if (status === "failed") {
201
+ spinner.error(text(message));
202
+ return;
290
203
  }
291
- state.activeProgress.clear();
204
+ spinner.stop(text(message));
292
205
  }
293
- function reset() {
294
- stopActiveProgress();
295
- resetProgressCounters(state);
296
- state.spinner = _clack_prompts.spinner();
297
- state.isSpinning = false;
298
- state.runningPlugins.clear();
299
- state.activeHookLogs.clear();
206
+ function stopProgress(message) {
207
+ const progress = state.progress;
208
+ if (!progress) return;
209
+ state.progress = null;
210
+ progress.stop(message === void 0 ? "" : text(message));
300
211
  }
301
- function pluginProgressText() {
302
- const running = [...state.runningPlugins].map((name) => (0, node_util.styleText)("bold", name));
303
- return getMessage(running.length > 0 ? `Generating ${running.join(", ")}` : "Generating plugins");
212
+ function startPhase({ running, success, failure, showHookResults = false }) {
213
+ state.phase = {
214
+ successLabel: success,
215
+ failureLabel: failure,
216
+ showHookResults,
217
+ failed: false,
218
+ hrStart: node_process.default.hrtime(),
219
+ lines: [],
220
+ output: []
221
+ };
222
+ startSpinner(running);
304
223
  }
305
- function showProgressStep() {
306
- if (logLevel <= _kubb_core.logLevel.silent) return;
307
- const line = buildProgressLine(state);
308
- if (line) _clack_prompts.log.step(getMessage(line));
224
+ function endPhase() {
225
+ const phase = state.phase;
226
+ state.phase = null;
227
+ if (!phase || silent) return;
228
+ const duration = require_output.formatMsWithColor(require_output.getElapsedMs(phase.hrStart));
229
+ stopSpinner(phase.failed ? `${phase.failureLabel} after ${duration}` : `${phase.successLabel} in ${duration}`, phase.failed ? "failed" : "success");
230
+ if (phase.lines.length) writer.block(phase.lines);
231
+ writeOutput(phase.output);
309
232
  }
310
- function getMessage(message) {
311
- return formatMessage(message, logLevel);
233
+ function writeOutput(lines) {
234
+ const output = trimBlankEdges(lines);
235
+ if (output.length) writer.raw(output);
312
236
  }
313
- function onStep(hook, message) {
314
- context.hook(hook, () => {
315
- if (logLevel <= _kubb_core.logLevel.silent) return;
316
- _clack_prompts.log.step(getMessage(message));
317
- });
237
+ function closeGroup(status) {
238
+ if (!state.groupOpen) return;
239
+ state.groupOpen = false;
240
+ stopSpinner();
241
+ stopProgress();
242
+ writer.groupEnd(status === "failed" ? (0, node_util.styleText)("red", "✗ Generation failed") : (0, node_util.styleText)("green", "✓ Generation succeeded"));
318
243
  }
319
- function stopSpinner(text) {
320
- if (!state.isSpinning) return;
321
- state.spinner.stop(text);
322
- state.isSpinning = false;
244
+ function reset() {
245
+ stopSpinner();
246
+ stopProgress();
247
+ resetProgressCounters(state);
248
+ state.hooks.clear();
249
+ state.pluginLines = [];
250
+ state.phase = null;
251
+ state.groupOpen = false;
323
252
  }
324
- context.hook("kubb:info", ({ message, info = "" }) => {
325
- if (logLevel <= _kubb_core.logLevel.silent) return;
326
- const text = getMessage([
253
+ context.hook("kubb:info", ({ message, info }) => {
254
+ if (silent) return;
255
+ const line = text([
327
256
  (0, node_util.styleText)("blue", "ℹ"),
328
257
  message,
329
258
  info ? (0, node_util.styleText)("dim", info) : void 0
330
259
  ].filter(Boolean).join(" "));
331
- if (state.isSpinning) {
332
- state.spinner.message(text);
260
+ if (state.phase) {
261
+ state.phase.lines.push(line);
262
+ return;
263
+ }
264
+ if (state.spinner) {
265
+ state.spinner.message(line);
333
266
  return;
334
267
  }
335
- _clack_prompts.log.info(text);
268
+ writer.info(line);
336
269
  });
337
- context.hook("kubb:success", ({ message, info = "" }) => {
338
- if (logLevel <= _kubb_core.logLevel.silent) return;
339
- const text = getMessage([
340
- (0, node_util.styleText)("blue", "✓"),
270
+ context.hook("kubb:success", ({ message, info }) => {
271
+ if (silent) return;
272
+ const line = text([
273
+ (0, node_util.styleText)("green", "✓"),
341
274
  message,
342
- logLevel >= _kubb_core.logLevel.info ? (0, node_util.styleText)("dim", info) : void 0
275
+ logLevel >= _kubb_core.logLevel.info && info ? (0, node_util.styleText)("dim", info) : void 0
343
276
  ].filter(Boolean).join(" "));
344
- if (state.isSpinning) {
345
- stopSpinner(text);
277
+ if (state.spinner) {
278
+ state.spinner.message(line);
346
279
  return;
347
280
  }
348
- _clack_prompts.log.success(text);
281
+ writer.success(line);
349
282
  });
350
283
  context.hook("kubb:warn", ({ message, info }) => {
351
284
  if (logLevel < _kubb_core.logLevel.warn) return;
352
- const text = getMessage([
285
+ writer.warn(text([
353
286
  (0, node_util.styleText)("yellow", "⚠"),
354
287
  message,
355
288
  logLevel >= _kubb_core.logLevel.info && info ? (0, node_util.styleText)("dim", info) : void 0
356
- ].filter(Boolean).join(" "));
357
- _clack_prompts.log.warn(text);
289
+ ].filter(Boolean).join(" ")));
358
290
  });
359
291
  context.hook("kubb:error", ({ error }) => {
360
- const text = [(0, node_util.styleText)("red", "✗"), error.message].join(" ");
361
- if (state.isSpinning) {
362
- stopSpinner(getMessage(text));
363
- return;
364
- }
365
- _clack_prompts.log.error(getMessage(text));
292
+ if (state.phase) state.phase.failed = true;
293
+ const line = [(0, node_util.styleText)("red", "✗"), error.message].join(" ");
294
+ if (state.spinner) stopSpinner(line, "failed");
295
+ else writer.error(text(line));
366
296
  const frames = logLevel >= _kubb_core.logLevel.verbose ? formatErrorFrames(error) : null;
367
- if (frames) {
368
- for (const frame of frames.frames) _clack_prompts.log.message(getMessage((0, node_util.styleText)("dim", frame)));
369
- if (frames.cause) {
370
- _clack_prompts.log.message((0, node_util.styleText)("dim", frames.cause.header));
371
- for (const frame of frames.cause.frames) _clack_prompts.log.message(getMessage(` ${(0, node_util.styleText)("dim", frame)}`));
372
- }
373
- }
297
+ if (!frames) return;
298
+ writer.block(frames.frames.map((frame) => text((0, node_util.styleText)("dim", frame))));
299
+ if (frames.cause) writer.block([(0, node_util.styleText)("dim", frames.cause.header), ...frames.cause.frames.map((frame) => text(` ${(0, node_util.styleText)("dim", frame)}`))]);
374
300
  });
375
301
  context.hook("kubb:diagnostic", ({ diagnostic }) => {
376
- if (logLevel <= _kubb_core.logLevel.silent && diagnostic.severity !== "error") return;
302
+ if (silent && diagnostic.severity !== "error") return;
377
303
  stopSpinner();
378
- stopActiveProgress();
304
+ stopProgress();
379
305
  if (_kubb_core.Diagnostics.isUpdate(diagnostic)) {
380
- _clack_prompts.box(`\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\`
381
- Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
382
- width: "auto",
383
- formatBorder: (s) => (0, node_util.styleText)("yellow", s),
384
- rounded: true,
385
- withGuide: false,
386
- contentAlign: "center",
387
- titleAlign: "center"
388
- });
306
+ writer.update([`\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\``, "Run `npm install -g @kubb/cli` to update"], "Update available for `Kubb`");
389
307
  return;
390
308
  }
391
309
  const { headline, details } = _kubb_core.Diagnostics.format(diagnostic);
392
- _clack_prompts.log.message([headline, ...details], {
393
- symbol: "",
394
- secondarySymbol: ""
395
- });
310
+ writer.diagnostic([headline, ...details]);
396
311
  });
397
312
  context.hook("studio:connecting", ({ url }) => {
398
- if (logLevel <= _kubb_core.logLevel.silent) return;
399
- state.spinner = _clack_prompts.spinner();
400
- state.spinner.start(getMessage(`Connecting to ${(0, node_util.styleText)("cyan", url)}`));
401
- state.isSpinning = true;
313
+ startSpinner(`Connecting to ${(0, node_util.styleText)("cyan", url)}`);
402
314
  });
403
315
  context.hook("studio:connected", ({ url, versions }) => {
404
- if (logLevel <= _kubb_core.logLevel.silent) return;
405
- const text = getMessage(`Connected to ${(0, node_util.styleText)("cyan", url)} ${(0, node_util.styleText)("dim", `(${formatVersions(versions)})`)}`);
406
- if (state.isSpinning) {
407
- stopSpinner(text);
316
+ if (silent) return;
317
+ const line = `Connected to ${(0, node_util.styleText)("cyan", url)} ${(0, node_util.styleText)("dim", `(${formatVersions(versions)})`)}`;
318
+ if (state.spinner) {
319
+ stopSpinner(line);
408
320
  return;
409
321
  }
410
- _clack_prompts.log.success(text);
322
+ writer.success(text(`✓ ${line}`));
411
323
  });
412
324
  context.hook("studio:ready", () => {
413
- if (logLevel <= _kubb_core.logLevel.silent) return;
414
- _clack_prompts.log.success(getMessage("Ready to receive jobs"));
325
+ if (silent) return;
326
+ startSpinner("✓ Ready to receive jobs");
415
327
  });
416
328
  context.hook("studio:disconnected", ({ reason }) => {
417
329
  if (logLevel < _kubb_core.logLevel.warn) return;
418
330
  stopSpinner();
419
- _clack_prompts.log.warn(getMessage(`Kubb Studio ended the session (${reason})`));
331
+ writer.warn(text(`⚠ Kubb Studio ended the session (${reason})`));
420
332
  });
421
333
  context.hook("studio:command:start", ({ command }) => {
422
- if (logLevel <= _kubb_core.logLevel.silent) return;
423
- _clack_prompts.log.info(getMessage(`Kubb Studio asked to ${(0, node_util.styleText)("bold", command)}`));
334
+ if (silent) return;
335
+ stopSpinner();
336
+ writer.info(text(`Kubb Studio asked to ${(0, node_util.styleText)("bold", command)}`));
424
337
  });
425
338
  context.hook("studio:command:end", ({ command, info }) => {
426
- if (logLevel <= _kubb_core.logLevel.silent) return;
427
- _clack_prompts.log.success(getMessage(`Finished ${command}${info ? ` ${(0, node_util.styleText)("dim", `(${info})`)}` : ""}`));
339
+ if (silent) return;
340
+ writer.success(text(`✓ Finished ${command}${info ? ` ${(0, node_util.styleText)("dim", `(${info})`)}` : ""}`));
341
+ startSpinner("✓ Ready to receive jobs");
428
342
  });
429
343
  context.hook("studio:warn", ({ message }) => {
430
344
  if (logLevel < _kubb_core.logLevel.warn) return;
431
- _clack_prompts.log.warn(getMessage(message));
345
+ writer.warn(text(`⚠ ${message}`));
432
346
  });
433
347
  context.hook("studio:error", ({ error }) => {
434
- const text = getMessage([(0, node_util.styleText)("red", "✗"), error.message].join(" "));
435
- if (state.isSpinning) {
436
- stopSpinner(text);
348
+ const line = `✗ ${error.message}`;
349
+ if (state.spinner) {
350
+ stopSpinner(line, "failed");
437
351
  return;
438
352
  }
439
- _clack_prompts.log.error(text);
353
+ writer.error(text(line));
440
354
  });
441
- context.hook("kubb:lifecycle:start", async ({ version }) => {
442
- console.log(`\n${getIntro({
443
- title: "The meta framework for code generation",
444
- description: "Ready to start",
445
- version,
446
- areEyesOpen: true
447
- })}\n`);
355
+ context.hook("kubb:lifecycle:start", () => {
448
356
  reset();
449
357
  });
450
358
  context.hook("kubb:generation:start", ({ config }) => {
359
+ closeGroup("failed");
451
360
  reset();
452
361
  state.totalPlugins = config.plugins?.length ?? 0;
453
- if (logLevel <= _kubb_core.logLevel.silent) return;
454
- const text = getMessage([
455
- "Generation started",
456
- config.name ? `for ${(0, node_util.styleText)("dim", config.name)}` : void 0,
457
- getInputPath(config)
458
- ].filter(Boolean).join(" "));
459
- _clack_prompts.intro(text);
362
+ if (silent) return;
363
+ const inputPath = getInputPath(config);
364
+ writer.group(text([(0, node_util.styleText)("bold", config.name ?? "kubb"), inputPath ? (0, node_util.styleText)("dim", inputPath) : void 0].filter(Boolean).join(" ")));
365
+ state.groupOpen = true;
366
+ startSpinner("Generating");
460
367
  });
461
368
  context.hook("kubb:plugin:start", ({ plugin }) => {
462
- if (logLevel <= _kubb_core.logLevel.silent) return;
463
- stopSpinner();
464
- state.runningPlugins.add(plugin.name);
465
- const active = state.activeProgress.get("plugins");
466
- if (active) {
467
- active.progressBar.advance(0, pluginProgressText());
468
- return;
469
- }
470
- const progressBar = _clack_prompts.progress({
471
- style: "block",
472
- max: Math.max(state.totalPlugins, 1),
473
- size: 30
474
- });
475
- progressBar.start(pluginProgressText());
476
- progressBar.advance(state.completedPlugins + state.failedPlugins, pluginProgressText());
477
- state.activeProgress.set("plugins", { progressBar });
369
+ if (silent) return;
370
+ state.spinner?.message(text(`Generating ${(0, node_util.styleText)("dim", plugin.name)}`));
478
371
  });
479
- context.hook("kubb:plugin:end", ({ plugin, success }) => {
480
- stopSpinner();
481
- const active = state.activeProgress.get("plugins");
482
- if (!active || logLevel <= _kubb_core.logLevel.silent) return;
483
- state.runningPlugins.delete(plugin.name);
372
+ context.hook("kubb:plugin:end", ({ plugin, duration, success }) => {
373
+ if (silent) return;
484
374
  recordPluginResult(state, success);
485
- active.progressBar.advance(1, pluginProgressText());
486
- if (state.runningPlugins.size === 0) {
487
- active.progressBar.stop(getMessage("Plugins generated"));
488
- state.activeProgress.delete("plugins");
489
- showProgressStep();
375
+ state.pluginLines.push(text(`${plugin.name} ${success ? "completed" : "failed"} in ${require_output.formatMsWithColor(duration)}`));
376
+ });
377
+ context.hook("kubb:plugins:end", () => {
378
+ if (silent) return;
379
+ const failed = state.failedPlugins > 0;
380
+ stopSpinner(buildProgressLine(state) ?? "No plugins ran", failed ? "failed" : "success");
381
+ if (state.pluginLines.length) {
382
+ writer.block(state.pluginLines);
383
+ state.pluginLines = [];
490
384
  }
491
385
  });
492
386
  context.hook("kubb:files:processing:start", ({ files }) => {
493
- if (logLevel <= _kubb_core.logLevel.silent) return;
387
+ if (silent) return;
494
388
  stopSpinner();
495
389
  state.totalFiles = files.length;
496
390
  state.processedFiles = 0;
497
- const text = `Writing ${files.length} files`;
498
- const progressBar = _clack_prompts.progress({
499
- style: "block",
500
- max: files.length,
501
- size: 30
502
- });
503
- context.callHook("kubb:info", { message: text });
504
- progressBar.start(getMessage(text));
505
- state.activeProgress.set("files", { progressBar });
391
+ state.progress = writer.progress(files.length);
392
+ state.progress.start(text(`Writing ${pluralize(files.length, "file")}`));
506
393
  });
507
394
  context.hook("kubb:files:processing:update", ({ files }) => {
508
- if (logLevel <= _kubb_core.logLevel.silent) return;
509
- stopSpinner();
510
- const active = state.activeProgress.get("files");
395
+ if (silent) return;
511
396
  for (const { file, config } of files) {
512
397
  state.processedFiles++;
513
- if (active) active.progressBar.advance(void 0, `Writing ${(0, node_path.relative)(config.root, file.path)}`);
398
+ state.progress?.advance(`Writing ${(0, node_path.relative)(config.root, file.path)}`);
514
399
  }
515
400
  });
516
401
  context.hook("kubb:files:processing:end", () => {
517
- if (logLevel <= _kubb_core.logLevel.silent) return;
518
- stopSpinner();
519
- const text = getMessage("Files written successfully");
520
- const active = state.activeProgress.get("files");
521
- if (!active) return;
522
- active.progressBar.stop(text);
523
- state.activeProgress.delete("files");
524
- showProgressStep();
525
- });
526
- context.hook("kubb:generation:end", ({ config }) => {
527
- stopSpinner();
528
- const text = getMessage(config.name ? `Generation completed for ${(0, node_util.styleText)("dim", config.name)}` : "Generation completed");
529
- _clack_prompts.outro(text);
402
+ if (silent) return;
403
+ stopProgress(`Wrote ${pluralize(state.processedFiles, "file")}`);
530
404
  });
531
- onStep("kubb:format:start", "Formatting");
532
- onStep("kubb:lint:start", "Linting");
533
- onStep("kubb:hooks:start", "Running hooks");
534
- context.hook("kubb:hook:start", ({ id, command, name, args }) => {
535
- if (logLevel <= _kubb_core.logLevel.silent || !id) return;
536
- stopSpinner();
537
- const commandWithArgs = formatCommandWithArgs(command, args);
538
- const title = getMessage(`Running ${(0, node_util.styleText)("dim", name ?? commandWithArgs)}`);
539
- const taskLog = _clack_prompts.taskLog({ title });
540
- state.activeHookLogs.set(id, {
541
- taskLog,
542
- hrStart: node_process.default.hrtime()
405
+ context.hook("kubb:format:start", () => startPhase(PHASES.format));
406
+ context.hook("kubb:format:end", endPhase);
407
+ context.hook("kubb:lint:start", () => startPhase(PHASES.lint));
408
+ context.hook("kubb:lint:end", endPhase);
409
+ context.hook("kubb:hooks:start", () => startPhase(PHASES.hooks));
410
+ context.hook("kubb:hooks:end", endPhase);
411
+ context.hook("kubb:hook:start", ({ id }) => {
412
+ if (silent || !id) return;
413
+ state.hooks.set(id, {
414
+ hrStart: node_process.default.hrtime(),
415
+ lines: []
543
416
  });
544
417
  });
545
- if (logLevel > _kubb_core.logLevel.silent) context.hook("kubb:hook:line", ({ id, line }) => {
546
- state.activeHookLogs.get(id)?.taskLog.message((0, node_util.styleText)("dim", line));
418
+ if (!silent) context.hook("kubb:hook:line", ({ id, line }) => {
419
+ const active = state.hooks.get(id);
420
+ if (!active) return;
421
+ if (state.phase) {
422
+ active.lines.push(line);
423
+ return;
424
+ }
425
+ writer.raw([line]);
547
426
  });
548
427
  context.hook("kubb:hook:end", ({ id, command, name, args, success, error, stdout, stderr }) => {
549
428
  if (!id) return;
550
- if (logLevel <= _kubb_core.logLevel.silent) {
429
+ if (!success && state.phase) state.phase.failed = true;
430
+ if (silent) {
551
431
  if (!success) {
552
432
  if (stdout) console.log(stdout);
553
433
  if (stderr) console.error(stderr);
554
434
  }
555
435
  return;
556
436
  }
557
- const active = state.activeHookLogs.get(id);
437
+ const active = state.hooks.get(id);
558
438
  if (!active) return;
559
- state.activeHookLogs.delete(id);
560
- const commandWithArgs = formatCommandWithArgs(command, args);
561
- const duration = formatMsWithColor(getElapsedMs(active.hrStart));
562
- if (success) active.taskLog.success(getMessage(`${(0, node_util.styleText)("dim", name ?? commandWithArgs)} completed in ${duration}`));
563
- else {
564
- const reason = error?.message ? ` (${error.message})` : "";
565
- active.taskLog.error(getMessage(`${(0, node_util.styleText)("dim", name ?? commandWithArgs)} failed${reason}`), { showLog: true });
439
+ state.hooks.delete(id);
440
+ const label = (0, node_util.styleText)("dim", name ?? formatCommandWithArgs(command, args));
441
+ const reason = error?.message ? ` (${error.message})` : "";
442
+ const result = success ? `${(0, node_util.styleText)("green", "✓")} ${label} in ${require_output.formatMsWithColor(require_output.getElapsedMs(active.hrStart))}` : `${(0, node_util.styleText)("red", "✗")} ${label} failed${reason}`;
443
+ const phase = state.phase;
444
+ if (phase) {
445
+ if (phase.showHookResults || !success) phase.lines.push(result);
446
+ phase.output.push(...active.lines);
447
+ return;
566
448
  }
449
+ stopSpinner();
450
+ writer.step(text(result));
451
+ writeOutput(active.lines);
452
+ });
453
+ context.hook("kubb:generation:end", () => {
454
+ stopSpinner();
455
+ stopProgress();
567
456
  });
568
457
  context.hook("kubb:lifecycle:end", () => {
458
+ closeGroup("failed");
569
459
  reset();
570
460
  });
571
- }
461
+ return { renderSummary(lines, { status }) {
462
+ if (silent || !state.groupOpen) return;
463
+ closeGroup(status);
464
+ writer.diagnostic([...lines]);
465
+ } };
466
+ };
467
+ }
468
+ /**
469
+ * TTY logger for local development, with animated steps and progress bars.
470
+ */
471
+ const clackLogger = {
472
+ name: "clack",
473
+ install: createLogger({
474
+ group: (title) => _clack_prompts.intro(title),
475
+ groupEnd: (text) => _clack_prompts.outro(text),
476
+ step: (text) => _clack_prompts.log.step(text, { spacing: 0 }),
477
+ info: (text) => _clack_prompts.log.info(text),
478
+ success: (text) => _clack_prompts.log.success(text),
479
+ warn: (text) => _clack_prompts.log.warn(text),
480
+ error: (text) => _clack_prompts.log.error(text),
481
+ block: (lines) => _clack_prompts.log.message(lines, { spacing: 0 }),
482
+ raw: (lines) => console.log((0, node_util.styleText)("dim", lines.join("\n"))),
483
+ diagnostic: (lines) => _clack_prompts.log.message(lines, {
484
+ symbol: "",
485
+ secondarySymbol: ""
486
+ }),
487
+ update: (lines, title) => _clack_prompts.box(lines.join("\n"), title, {
488
+ width: "auto",
489
+ formatBorder: (border) => (0, node_util.styleText)("yellow", border),
490
+ rounded: true,
491
+ withGuide: false,
492
+ contentAlign: "center",
493
+ titleAlign: "center"
494
+ }),
495
+ spinner() {
496
+ const spinner = _clack_prompts.spinner();
497
+ return {
498
+ start: (text) => spinner.start(text),
499
+ message: (text) => spinner.message(text),
500
+ clear: () => spinner.clear(),
501
+ stop: (text) => spinner.stop(text),
502
+ error: (text) => spinner.error(text)
503
+ };
504
+ },
505
+ progress(max) {
506
+ const bar = _clack_prompts.progress({
507
+ style: "block",
508
+ max,
509
+ size: 30
510
+ });
511
+ return {
512
+ start: (text) => bar.start(text),
513
+ advance: (text) => bar.advance(1, text),
514
+ stop: (text) => bar.stop(text)
515
+ };
516
+ }
517
+ })
572
518
  };
573
519
  //#endregion
574
520
  //#region src/loggers/plainLogger.ts
575
521
  /**
576
- * Plain console adapter for non-TTY environments, built on `console.log`.
522
+ * Prefixes that stand in for the symbols clack draws, so plain output reads the same way.
523
+ */
524
+ const SYMBOLS = {
525
+ info: "ℹ",
526
+ warn: "⚠",
527
+ error: "✗",
528
+ step: "◇"
529
+ };
530
+ function write(line) {
531
+ console.log(line);
532
+ }
533
+ /**
534
+ * Console adapter for non-TTY environments, and for an AI coding agent reading the output. Reports
535
+ * the same run in the same order as `clackLogger`, without the animation.
577
536
  */
578
537
  const plainLogger = {
579
538
  name: "plain",
580
- install(context, options) {
581
- const logLevel = options?.logLevel ?? _kubb_core.logLevel.info;
582
- const hookTimer = createHookTimer();
583
- function getMessage(message) {
584
- return formatMessage(message, logLevel);
585
- }
586
- function onStep(hook, message) {
587
- context.hook(hook, () => {
588
- if (logLevel <= _kubb_core.logLevel.silent) return;
589
- console.log(getMessage(message));
590
- });
591
- }
592
- context.hook("kubb:info", ({ message, info }) => {
593
- if (logLevel <= _kubb_core.logLevel.silent) return;
594
- const text = getMessage([
595
- "ℹ",
596
- message,
597
- info
598
- ].join(" "));
599
- console.log(text);
600
- });
601
- context.hook("kubb:success", ({ message, info = "" }) => {
602
- if (logLevel <= _kubb_core.logLevel.silent) return;
603
- const text = getMessage([
604
- "✓",
605
- message,
606
- logLevel >= _kubb_core.logLevel.info ? info : void 0
607
- ].filter(Boolean).join(" "));
608
- console.log(text);
609
- });
610
- context.hook("kubb:warn", ({ message, info }) => {
611
- if (logLevel < _kubb_core.logLevel.warn) return;
612
- const text = getMessage([
613
- "⚠",
614
- message,
615
- logLevel >= _kubb_core.logLevel.info ? info : void 0
616
- ].filter(Boolean).join(" "));
617
- console.log(text);
618
- });
619
- context.hook("kubb:error", ({ error }) => {
620
- const text = getMessage(["✗", error.message].join(" "));
621
- console.log(text);
622
- const frames = logLevel >= _kubb_core.logLevel.verbose ? formatErrorFrames(error) : null;
623
- if (frames) {
624
- for (const frame of frames.frames) console.log(getMessage(frame));
625
- if (frames.cause) {
626
- console.log(frames.cause.header);
627
- for (const frame of frames.cause.frames) console.log(getMessage(` ${frame}`));
628
- }
629
- }
630
- });
631
- context.hook("kubb:diagnostic", ({ diagnostic }) => {
632
- if (logLevel <= _kubb_core.logLevel.silent && diagnostic.severity !== "error") return;
633
- console.log(getMessage(_kubb_core.Diagnostics.formatLines(diagnostic).join("\n")));
634
- });
635
- context.hook("studio:connecting", ({ url }) => {
636
- if (logLevel <= _kubb_core.logLevel.silent) return;
637
- console.log(getMessage(`Connecting to ${url}`));
638
- });
639
- context.hook("studio:connected", ({ url, versions }) => {
640
- if (logLevel <= _kubb_core.logLevel.silent) return;
641
- console.log(getMessage(`✓ Connected to ${url} (${formatVersions(versions)})`));
642
- });
643
- context.hook("studio:ready", () => {
644
- if (logLevel <= _kubb_core.logLevel.silent) return;
645
- console.log(getMessage("✓ Ready to receive jobs"));
646
- });
647
- context.hook("studio:disconnected", ({ reason }) => {
648
- if (logLevel < _kubb_core.logLevel.warn) return;
649
- console.log(getMessage(`⚠ Kubb Studio ended the session (${reason})`));
650
- });
651
- context.hook("studio:command:start", ({ command }) => {
652
- if (logLevel <= _kubb_core.logLevel.silent) return;
653
- console.log(getMessage(`Kubb Studio asked to ${command}`));
654
- });
655
- context.hook("studio:command:end", ({ command, info }) => {
656
- if (logLevel <= _kubb_core.logLevel.silent) return;
657
- console.log(getMessage(`✓ Finished ${command}${info ? ` (${info})` : ""}`));
658
- });
659
- context.hook("studio:warn", ({ message }) => {
660
- if (logLevel < _kubb_core.logLevel.warn) return;
661
- console.log(getMessage(`⚠ ${message}`));
662
- });
663
- context.hook("studio:error", ({ error }) => {
664
- console.log(getMessage(`✗ ${error.message}`));
665
- });
666
- context.hook("kubb:lifecycle:start", ({ version }) => {
667
- console.log(`Kubb CLI v${version}`);
668
- });
669
- context.hook("kubb:generation:start", ({ config }) => {
670
- const text = getMessage(["Generation started", getInputPath(config)].filter(Boolean).join(" "));
671
- console.log(text);
672
- });
673
- context.hook("kubb:plugin:start", ({ plugin }) => {
674
- if (logLevel <= _kubb_core.logLevel.silent) return;
675
- const text = getMessage(`Generating ${plugin.name}`);
676
- console.log(text);
677
- });
678
- context.hook("kubb:plugin:end", ({ plugin, duration, success }) => {
679
- if (logLevel <= _kubb_core.logLevel.silent) return;
680
- const durationStr = formatMs(duration);
681
- const text = getMessage(success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`);
682
- console.log(text);
683
- });
684
- context.hook("kubb:files:processing:start", ({ files }) => {
685
- if (logLevel <= _kubb_core.logLevel.silent) return;
686
- const text = getMessage(`Writing ${files.length} files`);
687
- console.log(text);
688
- });
689
- context.hook("kubb:files:processing:update", ({ files }) => {
690
- if (logLevel <= _kubb_core.logLevel.silent) return;
691
- for (const { file, config } of files) console.log(getMessage(`Writing ${(0, node_path.relative)(config.root, file.path)}`));
692
- });
693
- context.hook("kubb:files:processing:end", () => {
694
- if (logLevel <= _kubb_core.logLevel.silent) return;
695
- const text = getMessage("Files written successfully");
696
- console.log(text);
697
- });
698
- context.hook("kubb:generation:end", ({ config }) => {
699
- const text = getMessage(config.name ? `Generation completed for ${config.name}` : "Generation completed");
700
- console.log(text);
701
- });
702
- onStep("kubb:format:start", "Format started");
703
- onStep("kubb:format:end", "Format completed");
704
- onStep("kubb:lint:start", "Lint started");
705
- onStep("kubb:lint:end", "Lint completed");
706
- onStep("kubb:hooks:start", "Hooks started");
707
- onStep("kubb:hooks:end", "Hooks completed");
708
- context.hook("kubb:hook:start", ({ id, command, name, args }) => {
709
- if (logLevel <= _kubb_core.logLevel.silent) return;
710
- if (id) hookTimer.start(id);
711
- const commandWithArgs = formatCommandWithArgs(command, args);
712
- console.log(getMessage(`Hook ${name ?? commandWithArgs} started`));
713
- });
714
- context.hook("kubb:hook:end", ({ id, command, name, args, success, error, stdout, stderr }) => {
715
- if (logLevel <= _kubb_core.logLevel.silent) return;
716
- const ms = id ? hookTimer.end(id) : void 0;
717
- const durationStr = ms !== void 0 ? ` in ${formatMs(ms)}` : "";
718
- const commandWithArgs = formatCommandWithArgs(command, args);
719
- if (success) console.log(getMessage(`✓ Hook ${name ?? commandWithArgs} completed${durationStr}`));
720
- else {
721
- if (stdout) console.log(stdout);
722
- if (stderr) console.error(stderr);
723
- const reason = error?.message ? ` (${error.message})` : "";
724
- console.log(getMessage(`✗ Hook ${name ?? commandWithArgs} failed${durationStr}${reason}`));
725
- }
726
- });
727
- }
539
+ install: createLogger({
540
+ group: write,
541
+ groupEnd: write,
542
+ step: (text) => write(`${SYMBOLS.step} ${text}`),
543
+ info: write,
544
+ success: write,
545
+ warn: write,
546
+ error: write,
547
+ block: (lines) => write(lines.join("\n")),
548
+ raw: (lines) => write(lines.join("\n")),
549
+ diagnostic: (lines) => write(lines.join("\n")),
550
+ update: (lines, title) => write([title, ...lines].join("\n")),
551
+ spinner: () => ({
552
+ start: write,
553
+ message: write,
554
+ clear: () => {},
555
+ stop: (text) => text && write(text),
556
+ error: (text) => write(`${SYMBOLS.error} ${text}`)
557
+ }),
558
+ progress: () => ({
559
+ start: write,
560
+ advance: write,
561
+ stop: (text) => text && write(text)
562
+ })
563
+ })
728
564
  };
729
565
  //#endregion
730
566
  //#region src/loggers/utils.ts
@@ -738,6 +574,15 @@ function getInputPath(config) {
738
574
  return typeof input?.path === "string" ? input.path : void 0;
739
575
  }
740
576
  /**
577
+ * Counts a noun, so a message never reads `1 files`.
578
+ *
579
+ * @example
580
+ * `pluralize(1, 'config')` returns `'1 config'`
581
+ */
582
+ function pluralize(count, noun) {
583
+ return `${count} ${count === 1 ? noun : `${noun}s`}`;
584
+ }
585
+ /**
741
586
  * Optionally prefix a message with a [HH:MM:SS] timestamp when logLevel >= verbose.
742
587
  * Shared across all logger adapters to avoid duplication.
743
588
  */
@@ -792,7 +637,7 @@ function formatErrorFrames(error) {
792
637
  */
793
638
  function buildProgressLine(state) {
794
639
  const parts = [];
795
- const duration = formatMs(getElapsedMs(state.hrStart));
640
+ const duration = require_output.formatMs(require_output.getElapsedMs(state.hrStart));
796
641
  if (state.totalPlugins > 0) {
797
642
  const pluginStr = state.failedPlugins > 0 ? `Plugins ${(0, node_util.styleText)("green", state.completedPlugins.toString())}/${state.totalPlugins} ${(0, node_util.styleText)("red", `(${state.failedPlugins} failed)`)}` : `Plugins ${(0, node_util.styleText)("green", state.completedPlugins.toString())}/${state.totalPlugins}`;
798
643
  parts.push(pluginStr);
@@ -834,23 +679,6 @@ function recordPluginResult(state, success) {
834
679
  else state.failedPlugins++;
835
680
  }
836
681
  /**
837
- * Creates a {@link HookTimer} backed by a private `id → hrtime` map.
838
- */
839
- function createHookTimer() {
840
- const starts = /* @__PURE__ */ new Map();
841
- return {
842
- start(id) {
843
- starts.set(id, node_process.default.hrtime());
844
- },
845
- end(id) {
846
- const hrStart = starts.get(id);
847
- if (!hrStart) return;
848
- starts.delete(id);
849
- return getElapsedMs(hrStart);
850
- }
851
- };
852
- }
853
- /**
854
682
  * Join a command and its optional args into a single display string.
855
683
  *
856
684
  * @example With args
@@ -897,19 +725,22 @@ function installReporter(context, reporter, ctx) {
897
725
  * `stdout`/`stderr` on `kubb:hook:end`, so nothing is returned here.
898
726
  *
899
727
  * Loggers and reporters are independent, except for `cli`: it both installs the live logger view
900
- * here and registers as a reporter that prints the per-config summary. The `json` reporter owns
901
- * stdout, so the whole `cli` reporter (live logger and summary) is skipped whenever `json` is
902
- * among the reporters, even if `cli` is also listed.
728
+ * here and registers a reporter that renders the per-config summary through that logger, so the
729
+ * summary lands inside the group the logger opened. The `json` reporter owns stdout, so the whole
730
+ * `cli` reporter (live logger and summary) is skipped whenever `json` is among the reporters, even
731
+ * if `cli` is also listed.
903
732
  */
904
733
  async function setupReporters(context, { logLevel, reporters }) {
905
734
  const hasJson = reporters.some((reporter) => reporter.name === "json");
906
735
  const ctx = { logLevel };
907
736
  for (const reporter of reporters) {
908
- if (reporter.name === "cli") {
909
- if (hasJson) continue;
910
- await (require_output.canUseTTY() && !require_agent.getAgentName() ? clackLogger : plainLogger).install(context, { logLevel });
737
+ if (reporter.name !== "cli") {
738
+ installReporter(context, reporter, ctx);
739
+ continue;
911
740
  }
912
- installReporter(context, reporter, ctx);
741
+ if (hasJson) continue;
742
+ const handle = await (require_output.canUseTTY() && !require_agent.getAgentName() ? clackLogger : plainLogger).install(context, { logLevel });
743
+ installReporter(context, (0, _kubb_core.createCliReporter)({ render: handle?.renderSummary }), ctx);
913
744
  }
914
745
  }
915
746
  /**
@@ -1309,6 +1140,12 @@ Object.defineProperty(exports, "linters", {
1309
1140
  return linters;
1310
1141
  }
1311
1142
  });
1143
+ Object.defineProperty(exports, "pluralize", {
1144
+ enumerable: true,
1145
+ get: function() {
1146
+ return pluralize;
1147
+ }
1148
+ });
1312
1149
  Object.defineProperty(exports, "runHook", {
1313
1150
  enumerable: true,
1314
1151
  get: function() {
@@ -1346,4 +1183,4 @@ Object.defineProperty(exports, "startWatcher", {
1346
1183
  }
1347
1184
  });
1348
1185
 
1349
- //# sourceMappingURL=utils-CMU1rLfY.cjs.map
1186
+ //# sourceMappingURL=utils-BP0xq5AK.cjs.map