@kubb/cli 5.0.0-beta.3 → 5.0.0-beta.31

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 (151) hide show
  1. package/README.md +177 -26
  2. package/dist/agent-BAAO2W7u.cjs +70 -0
  3. package/dist/agent-BAAO2W7u.cjs.map +1 -0
  4. package/dist/agent-WLRLgsEM.js +68 -0
  5. package/dist/agent-WLRLgsEM.js.map +1 -0
  6. package/dist/{chunk--u3MIqq1.js → chunk-BvFE5Tac.js} +1 -0
  7. package/dist/constants-B2JTeRBb.js +42 -0
  8. package/dist/constants-B2JTeRBb.js.map +1 -0
  9. package/dist/constants-BINTA5VZ.cjs +77 -0
  10. package/dist/constants-BINTA5VZ.cjs.map +1 -0
  11. package/dist/constants-BYGmiFs0.cjs +139 -0
  12. package/dist/constants-BYGmiFs0.cjs.map +1 -0
  13. package/dist/constants-DSJ-Xrbv.js +116 -0
  14. package/dist/constants-DSJ-Xrbv.js.map +1 -0
  15. package/dist/define-Bdn8j5VM.cjs.map +1 -1
  16. package/dist/{define-Ctii4bel.js → define-m_fp-Aqm.js} +2 -2
  17. package/dist/{define-Ctii4bel.js.map → define-m_fp-Aqm.js.map} +1 -1
  18. package/dist/{errors-CjPmyZHy.js → errors-CINO1EIv.js} +2 -2
  19. package/dist/{errors-CjPmyZHy.js.map → errors-CINO1EIv.js.map} +1 -1
  20. package/dist/errors-CLCjoSg0.cjs.map +1 -1
  21. package/dist/{generate-CTdVvIaP.js → generate-C4iw5Nou.js} +12 -6
  22. package/dist/generate-C4iw5Nou.js.map +1 -0
  23. package/dist/{generate-BzCMyyNN.cjs → generate-DKtBY8eR.cjs} +10 -4
  24. package/dist/generate-DKtBY8eR.cjs.map +1 -0
  25. package/dist/index.cjs +20 -11
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +22 -13
  29. package/dist/index.js.map +1 -1
  30. package/dist/init-DE_judaK.js +53 -0
  31. package/dist/init-DE_judaK.js.map +1 -0
  32. package/dist/init-berpsF2G.cjs +53 -0
  33. package/dist/init-berpsF2G.cjs.map +1 -0
  34. package/dist/mcp-Ce6errt_.js +39 -0
  35. package/dist/mcp-Ce6errt_.js.map +1 -0
  36. package/dist/mcp-DcohdQTl.cjs +39 -0
  37. package/dist/mcp-DcohdQTl.cjs.map +1 -0
  38. package/dist/package-C0vNpFXU.js +6 -0
  39. package/dist/package-C0vNpFXU.js.map +1 -0
  40. package/dist/{package-DcmDg_mw.cjs → package-DZDnoPgZ.cjs} +2 -2
  41. package/dist/package-DZDnoPgZ.cjs.map +1 -0
  42. package/dist/run-B11-UaUs.cjs +33 -0
  43. package/dist/run-B11-UaUs.cjs.map +1 -0
  44. package/dist/{init-eNRlotJK.js → run-BNqMQygv.js} +107 -149
  45. package/dist/run-BNqMQygv.js.map +1 -0
  46. package/dist/{generate-BL-Kp5GY.js → run-BgM41TQT.js} +561 -493
  47. package/dist/run-BgM41TQT.js.map +1 -0
  48. package/dist/{init-CZ5Xq2Hd.cjs → run-BnGfi7Cp.cjs} +105 -147
  49. package/dist/run-BnGfi7Cp.cjs.map +1 -0
  50. package/dist/{agent-sdYBBgrd.js → run-BzpYYOQs.js} +46 -43
  51. package/dist/run-BzpYYOQs.js.map +1 -0
  52. package/dist/run-CCZ24VKk.js +51 -0
  53. package/dist/run-CCZ24VKk.js.map +1 -0
  54. package/dist/run-CQbj3ley.cjs +52 -0
  55. package/dist/run-CQbj3ley.cjs.map +1 -0
  56. package/dist/{generate-DMqdAYqy.cjs → run-DeWgpA6S.cjs} +558 -490
  57. package/dist/run-DeWgpA6S.cjs.map +1 -0
  58. package/dist/{agent-B4cAAab2.cjs → run-DwdAwnLG.cjs} +44 -41
  59. package/dist/run-DwdAwnLG.cjs.map +1 -0
  60. package/dist/run-PSA9X7ci.js +32 -0
  61. package/dist/run-PSA9X7ci.js.map +1 -0
  62. package/dist/shell-475fQKaX.cjs.map +1 -1
  63. package/dist/{shell-DLzN4fRo.js → shell-CN6DNqeC.js} +2 -2
  64. package/dist/{shell-DLzN4fRo.js.map → shell-CN6DNqeC.js.map} +1 -1
  65. package/dist/{telemetry-DN95_2pF.cjs → telemetry-B2iWkY5e.cjs} +5 -7
  66. package/dist/telemetry-B2iWkY5e.cjs.map +1 -0
  67. package/dist/{telemetry-LgT_sdPe.js → telemetry-BkektVz6.js} +6 -8
  68. package/dist/telemetry-BkektVz6.js.map +1 -0
  69. package/dist/validate-DVeCYyIS.js +26 -0
  70. package/dist/validate-DVeCYyIS.js.map +1 -0
  71. package/dist/validate-ymG_XDSU.cjs +26 -0
  72. package/dist/validate-ymG_XDSU.cjs.map +1 -0
  73. package/package.json +14 -14
  74. package/src/commands/agent/start.ts +10 -7
  75. package/src/commands/agent.ts +3 -1
  76. package/src/commands/generate.ts +5 -3
  77. package/src/commands/init.ts +34 -3
  78. package/src/commands/mcp.ts +28 -4
  79. package/src/commands/validate.ts +6 -4
  80. package/src/constants.ts +2 -58
  81. package/src/index.ts +5 -3
  82. package/src/loggers/clackLogger.ts +85 -118
  83. package/src/loggers/fileSystemLogger.ts +28 -15
  84. package/src/loggers/githubActionsLogger.ts +87 -96
  85. package/src/loggers/plainLogger.ts +48 -81
  86. package/src/loggers/types.ts +6 -0
  87. package/src/loggers/utils.ts +235 -11
  88. package/src/runners/agent/run.ts +113 -0
  89. package/src/runners/agent/utils.ts +98 -0
  90. package/src/runners/generate/run.ts +321 -0
  91. package/src/runners/generate/utils.ts +225 -0
  92. package/src/runners/init/run.ts +212 -0
  93. package/src/{utils/packageManager.ts → runners/init/utils.ts} +12 -2
  94. package/src/runners/mcp/run.ts +37 -0
  95. package/src/runners/validate/run.ts +63 -0
  96. package/src/{utils/telemetry.ts → telemetry.ts} +27 -20
  97. package/dist/agent-B4cAAab2.cjs.map +0 -1
  98. package/dist/agent-BFACosbG.cjs +0 -58
  99. package/dist/agent-BFACosbG.cjs.map +0 -1
  100. package/dist/agent-s7TqqoTg.js +0 -56
  101. package/dist/agent-s7TqqoTg.js.map +0 -1
  102. package/dist/agent-sdYBBgrd.js.map +0 -1
  103. package/dist/constants-CnDXa1R6.cjs +0 -148
  104. package/dist/constants-CnDXa1R6.cjs.map +0 -1
  105. package/dist/constants-aL3CP_Wq.js +0 -95
  106. package/dist/constants-aL3CP_Wq.js.map +0 -1
  107. package/dist/generate-BL-Kp5GY.js.map +0 -1
  108. package/dist/generate-BzCMyyNN.cjs.map +0 -1
  109. package/dist/generate-CTdVvIaP.js.map +0 -1
  110. package/dist/generate-DMqdAYqy.cjs.map +0 -1
  111. package/dist/init-BHMGbly9.cjs +0 -25
  112. package/dist/init-BHMGbly9.cjs.map +0 -1
  113. package/dist/init-CZ5Xq2Hd.cjs.map +0 -1
  114. package/dist/init-eNRlotJK.js.map +0 -1
  115. package/dist/init-qgpg-iRW.js +0 -25
  116. package/dist/init-qgpg-iRW.js.map +0 -1
  117. package/dist/mcp-BRp-2Rdc.js +0 -16
  118. package/dist/mcp-BRp-2Rdc.js.map +0 -1
  119. package/dist/mcp-CYOgxB82.cjs +0 -47
  120. package/dist/mcp-CYOgxB82.cjs.map +0 -1
  121. package/dist/mcp-DmJm3TrU.js +0 -46
  122. package/dist/mcp-DmJm3TrU.js.map +0 -1
  123. package/dist/mcp-N3mRyVuO.cjs +0 -16
  124. package/dist/mcp-N3mRyVuO.cjs.map +0 -1
  125. package/dist/package-DcmDg_mw.cjs.map +0 -1
  126. package/dist/package-DtuyzAVW.js +0 -6
  127. package/dist/package-DtuyzAVW.js.map +0 -1
  128. package/dist/telemetry-DN95_2pF.cjs.map +0 -1
  129. package/dist/telemetry-LgT_sdPe.js.map +0 -1
  130. package/dist/validate-CJpTOzKA.js +0 -25
  131. package/dist/validate-CJpTOzKA.js.map +0 -1
  132. package/dist/validate-DyTbv7Bc.cjs +0 -25
  133. package/dist/validate-DyTbv7Bc.cjs.map +0 -1
  134. package/dist/validate-kLJoT_hi.js +0 -33
  135. package/dist/validate-kLJoT_hi.js.map +0 -1
  136. package/dist/validate-yKKzqEZ5.cjs +0 -34
  137. package/dist/validate-yKKzqEZ5.cjs.map +0 -1
  138. package/src/runners/agent.ts +0 -155
  139. package/src/runners/generate.ts +0 -333
  140. package/src/runners/init.ts +0 -296
  141. package/src/runners/mcp.ts +0 -51
  142. package/src/runners/validate.ts +0 -39
  143. package/src/types.ts +0 -11
  144. package/src/utils/Writables.ts +0 -17
  145. package/src/utils/executeHooks.ts +0 -45
  146. package/src/utils/flags.ts +0 -9
  147. package/src/utils/getConfig.ts +0 -10
  148. package/src/utils/getCosmiConfig.ts +0 -80
  149. package/src/utils/getSummary.ts +0 -68
  150. package/src/utils/runHook.ts +0 -91
  151. package/src/utils/watcher.ts +0 -19
@@ -1,9 +1,9 @@
1
1
  const require_chunk = require("./chunk-ByKO4r7w.cjs");
2
2
  const require_errors = require("./errors-CLCjoSg0.cjs");
3
- const require_telemetry = require("./telemetry-DN95_2pF.cjs");
3
+ const require_telemetry = require("./telemetry-B2iWkY5e.cjs");
4
4
  const require_shell = require("./shell-475fQKaX.cjs");
5
- const require_package = require("./package-DcmDg_mw.cjs");
6
- const require_constants = require("./constants-CnDXa1R6.cjs");
5
+ const require_package = require("./package-DZDnoPgZ.cjs");
6
+ const require_constants = require("./constants-BINTA5VZ.cjs");
7
7
  let node_util = require("node:util");
8
8
  let node_events = require("node:events");
9
9
  let node_crypto = require("node:crypto");
@@ -17,10 +17,9 @@ node_process = require_chunk.__toESM(node_process, 1);
17
17
  let _clack_prompts = require("@clack/prompts");
18
18
  _clack_prompts = require_chunk.__toESM(_clack_prompts, 1);
19
19
  let _kubb_core = require("@kubb/core");
20
- let tinyexec = require("tinyexec");
21
- let node_stream = require("node:stream");
22
20
  let cosmiconfig = require("cosmiconfig");
23
- let unrun = require("unrun");
21
+ let jiti = require("jiti");
22
+ let tinyexec = require("tinyexec");
24
23
  //#region ../../internals/utils/src/asyncEventEmitter.ts
25
24
  /**
26
25
  * Typed `EventEmitter` that awaits all async listeners before resolving.
@@ -51,9 +50,12 @@ var AsyncEventEmitter = class {
51
50
  * await emitter.emit('build', 'petstore')
52
51
  * ```
53
52
  */
54
- async emit(eventName, ...eventArgs) {
53
+ emit(eventName, ...eventArgs) {
55
54
  const listeners = this.#emitter.listeners(eventName);
56
55
  if (listeners.length === 0) return;
56
+ return this.#emitAll(eventName, listeners, eventArgs);
57
+ }
58
+ async #emitAll(eventName, listeners, eventArgs) {
57
59
  for (const listener of listeners) try {
58
60
  await listener(...eventArgs);
59
61
  } catch (err) {
@@ -489,112 +491,6 @@ async function detectLinter() {
489
491
  return null;
490
492
  }
491
493
  //#endregion
492
- //#region src/utils/getSummary.ts
493
- function getSummary({ failedPlugins, filesCreated, status, hrStart, config, pluginTimings }) {
494
- const duration = formatHrtime(hrStart);
495
- const pluginsCount = config.plugins?.length ?? 0;
496
- const successCount = pluginsCount - failedPlugins.size;
497
- const meta = {
498
- plugins: status === "success" ? `${(0, node_util.styleText)("green", `${successCount} successful`)}, ${pluginsCount} total` : `${(0, node_util.styleText)("green", `${successCount} successful`)}, ${(0, node_util.styleText)("red", `${failedPlugins.size} failed`)}, ${pluginsCount} total`,
499
- pluginsFailed: status === "failed" ? [...failedPlugins].map(({ plugin }) => randomCliColor(plugin.name)).join(", ") : void 0,
500
- filesCreated,
501
- time: (0, node_util.styleText)("green", duration),
502
- output: node_path.default.resolve(config.root, config.output.path)
503
- };
504
- const labels = {
505
- plugins: "Plugins:",
506
- failed: "Failed:",
507
- generated: "Generated:",
508
- pluginTimings: "Plugin Timings:",
509
- output: "Output:"
510
- };
511
- const maxLength = Math.max(0, ...[...Object.values(labels), ...pluginTimings ? Array.from(pluginTimings.keys()) : []].map((s) => s.length));
512
- const summaryLines = [];
513
- summaryLines.push(`${labels.plugins.padEnd(maxLength + 2)} ${meta.plugins}`);
514
- if (meta.pluginsFailed) summaryLines.push(`${labels.failed.padEnd(maxLength + 2)} ${meta.pluginsFailed}`);
515
- summaryLines.push(`${labels.generated.padEnd(maxLength + 2)} ${meta.filesCreated} files in ${meta.time}`);
516
- if (pluginTimings && pluginTimings.size > 0) {
517
- const sortedTimings = Array.from(pluginTimings.entries()).sort((a, b) => b[1] - a[1]);
518
- summaryLines.push(`${labels.pluginTimings}`);
519
- sortedTimings.forEach(([name, time]) => {
520
- const timeStr = time >= 1e3 ? `${(time / 1e3).toFixed(2)}s` : `${Math.round(time)}ms`;
521
- const barLength = Math.min(Math.ceil(time / 100), 10);
522
- const bar = (0, node_util.styleText)("dim", "█".repeat(barLength));
523
- summaryLines.push(`${(0, node_util.styleText)("dim", "•")} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`);
524
- });
525
- }
526
- summaryLines.push(`${labels.output.padEnd(maxLength + 2)} ${meta.output}`);
527
- return summaryLines;
528
- }
529
- //#endregion
530
- //#region src/utils/runHook.ts
531
- /**
532
- * Executes a hook command, emits debug and completion events, and forwards output to an optional sink.
533
- */
534
- async function runHook({ id, command, args, commandWithArgs, context, stream = false, sink }) {
535
- try {
536
- const proc = (0, tinyexec.x)(command, [...args ?? []], {
537
- nodeOptions: { detached: process.platform !== "win32" },
538
- throwOnError: true
539
- });
540
- if (stream && sink?.onLine) for await (const line of proc) sink.onLine(line);
541
- const result = await proc;
542
- await context.emit("kubb:debug", {
543
- date: /* @__PURE__ */ new Date(),
544
- logs: [result.stdout.trimEnd()]
545
- });
546
- await context.emit("kubb:hook:end", {
547
- command,
548
- args,
549
- id,
550
- success: true,
551
- error: null
552
- });
553
- } catch (err) {
554
- if (!(err instanceof tinyexec.NonZeroExitError)) {
555
- await context.emit("kubb:hook:end", {
556
- command,
557
- args,
558
- id,
559
- success: false,
560
- error: require_errors.toError(err)
561
- });
562
- await context.emit("kubb:error", { error: require_errors.toError(err) });
563
- return;
564
- }
565
- const stderr = err.output?.stderr ?? "";
566
- const stdout = err.output?.stdout ?? "";
567
- await context.emit("kubb:debug", {
568
- date: /* @__PURE__ */ new Date(),
569
- logs: [stdout, stderr].filter(Boolean)
570
- });
571
- if (stderr) sink?.onStderr?.(stderr);
572
- if (stdout) sink?.onStdout?.(stdout);
573
- const errorMessage = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
574
- await context.emit("kubb:hook:end", {
575
- command,
576
- args,
577
- id,
578
- success: false,
579
- error: errorMessage
580
- });
581
- await context.emit("kubb:error", { error: errorMessage });
582
- }
583
- }
584
- //#endregion
585
- //#region src/utils/Writables.ts
586
- var ClackWritable = class extends node_stream.Writable {
587
- taskLog;
588
- constructor(taskLog, opts) {
589
- super(opts);
590
- this.taskLog = taskLog;
591
- }
592
- _write(chunk, _encoding, callback) {
593
- this.taskLog.message(`${(0, node_util.styleText)("dim", chunk.toString())}`);
594
- callback();
595
- }
596
- };
597
- //#endregion
598
494
  //#region src/loggers/clackLogger.ts
599
495
  /**
600
496
  * TTY logger with beautiful UI and progress indicators for local development.
@@ -604,30 +500,22 @@ const clackLogger = (0, _kubb_core.defineLogger)({
604
500
  install(context, options) {
605
501
  const logLevel = options?.logLevel ?? _kubb_core.logLevel.info;
606
502
  const state = {
607
- totalPlugins: 0,
608
- completedPlugins: 0,
609
- failedPlugins: 0,
610
- totalFiles: 0,
611
- processedFiles: 0,
612
- hrStart: node_process.default.hrtime(),
503
+ ...createProgressCounters(),
613
504
  spinner: _clack_prompts.spinner(),
614
505
  isSpinning: false,
615
- activeProgress: /* @__PURE__ */ new Map()
506
+ activeProgress: /* @__PURE__ */ new Map(),
507
+ activeHookLogs: /* @__PURE__ */ new Map()
616
508
  };
617
509
  function reset() {
618
510
  for (const [_key, active] of state.activeProgress) {
619
511
  if (active.interval) clearInterval(active.interval);
620
512
  active.progressBar?.stop();
621
513
  }
622
- state.totalPlugins = 0;
623
- state.completedPlugins = 0;
624
- state.failedPlugins = 0;
625
- state.totalFiles = 0;
626
- state.processedFiles = 0;
627
- state.hrStart = node_process.default.hrtime();
514
+ resetProgressCounters(state);
628
515
  state.spinner = _clack_prompts.spinner();
629
516
  state.isSpinning = false;
630
517
  state.activeProgress.clear();
518
+ state.activeHookLogs.clear();
631
519
  }
632
520
  function showProgressStep() {
633
521
  if (logLevel <= _kubb_core.logLevel.silent) return;
@@ -637,11 +525,18 @@ const clackLogger = (0, _kubb_core.defineLogger)({
637
525
  function getMessage(message) {
638
526
  return formatMessage(message, logLevel);
639
527
  }
528
+ function onStep(event, message) {
529
+ context.on(event, () => {
530
+ if (logLevel <= _kubb_core.logLevel.silent) return;
531
+ _clack_prompts.log.step(getMessage(message));
532
+ });
533
+ }
640
534
  function startSpinner(text) {
641
535
  state.spinner.start(text);
642
536
  state.isSpinning = true;
643
537
  }
644
538
  function stopSpinner(text) {
539
+ if (!state.isSpinning) return;
645
540
  state.spinner.stop(text);
646
541
  state.isSpinning = false;
647
542
  }
@@ -652,8 +547,11 @@ const clackLogger = (0, _kubb_core.defineLogger)({
652
547
  message,
653
548
  (0, node_util.styleText)("dim", info)
654
549
  ].join(" "));
655
- if (state.isSpinning) state.spinner.message(text);
656
- else _clack_prompts.log.info(text);
550
+ if (state.isSpinning) {
551
+ state.spinner.message(text);
552
+ return;
553
+ }
554
+ _clack_prompts.log.info(text);
657
555
  });
658
556
  context.on("kubb:success", ({ message, info = "" }) => {
659
557
  if (logLevel <= _kubb_core.logLevel.silent) return;
@@ -662,8 +560,11 @@ const clackLogger = (0, _kubb_core.defineLogger)({
662
560
  message,
663
561
  logLevel >= _kubb_core.logLevel.info ? (0, node_util.styleText)("dim", info) : void 0
664
562
  ].filter(Boolean).join(" "));
665
- if (state.isSpinning) stopSpinner(text);
666
- else _clack_prompts.log.success(text);
563
+ if (state.isSpinning) {
564
+ stopSpinner(text);
565
+ return;
566
+ }
567
+ _clack_prompts.log.success(text);
667
568
  });
668
569
  context.on("kubb:warn", ({ message, info }) => {
669
570
  if (logLevel < _kubb_core.logLevel.warn) return;
@@ -677,8 +578,11 @@ const clackLogger = (0, _kubb_core.defineLogger)({
677
578
  context.on("kubb:error", ({ error }) => {
678
579
  const caused = require_errors.toCause(error);
679
580
  const text = [(0, node_util.styleText)("red", "✗"), error.message].join(" ");
680
- if (state.isSpinning) stopSpinner(getMessage(text));
681
- else _clack_prompts.log.error(getMessage(text));
581
+ if (state.isSpinning) {
582
+ stopSpinner(getMessage(text));
583
+ return;
584
+ }
585
+ _clack_prompts.log.error(getMessage(text));
682
586
  if (logLevel >= _kubb_core.logLevel.debug && error.stack) {
683
587
  const frames = error.stack.split("\n").slice(1, 4);
684
588
  for (const frame of frames) _clack_prompts.log.message(getMessage((0, node_util.styleText)("dim", frame.trim())));
@@ -729,6 +633,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
729
633
  context.on("kubb:generation:start", ({ config }) => {
730
634
  reset();
731
635
  state.totalPlugins = config.plugins?.length ?? 0;
636
+ if (logLevel <= _kubb_core.logLevel.silent) return;
732
637
  const text = getMessage(["Generation started", config.name ? `for ${(0, node_util.styleText)("dim", config.name)}` : void 0].filter(Boolean).join(" "));
733
638
  _clack_prompts.intro(text);
734
639
  });
@@ -755,8 +660,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
755
660
  const active = state.activeProgress.get(plugin.name);
756
661
  if (!active || logLevel === _kubb_core.logLevel.silent) return;
757
662
  clearInterval(active.interval);
758
- if (success) state.completedPlugins++;
759
- else state.failedPlugins++;
663
+ recordPluginResult(state, success);
760
664
  const durationStr = formatMsWithColor(duration);
761
665
  const text = getMessage(success ? `${(0, node_util.styleText)("bold", plugin.name)} completed in ${durationStr}` : `${(0, node_util.styleText)("bold", plugin.name)} failed in ${(0, node_util.styleText)("red", formatMs(duration))}`);
762
666
  active.progressBar.stop(text);
@@ -778,14 +682,14 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
778
682
  progressBar.start(getMessage(text));
779
683
  state.activeProgress.set("files", { progressBar });
780
684
  });
781
- context.on("kubb:file:processing:update", ({ file, config }) => {
685
+ context.on("kubb:files:processing:update", ({ files }) => {
782
686
  if (logLevel <= _kubb_core.logLevel.silent) return;
783
687
  stopSpinner();
784
- state.processedFiles++;
785
- const text = `Writing ${(0, node_path.relative)(config.root, file.path)}`;
786
688
  const active = state.activeProgress.get("files");
787
- if (!active) return;
788
- active.progressBar.advance(void 0, text);
689
+ for (const { file, config } of files) {
690
+ state.processedFiles++;
691
+ if (active) active.progressBar.advance(void 0, `Writing ${(0, node_path.relative)(config.root, file.path)}`);
692
+ }
789
693
  });
790
694
  context.on("kubb:files:processing:end", () => {
791
695
  if (logLevel <= _kubb_core.logLevel.silent) return;
@@ -798,68 +702,35 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
798
702
  showProgressStep();
799
703
  });
800
704
  context.on("kubb:generation:end", ({ config }) => {
705
+ stopSpinner();
801
706
  const text = getMessage(config.name ? `Generation completed for ${(0, node_util.styleText)("dim", config.name)}` : "Generation completed");
802
707
  _clack_prompts.outro(text);
803
708
  });
804
- context.on("kubb:format:start", () => {
805
- if (logLevel <= _kubb_core.logLevel.silent) return;
806
- const text = getMessage("Format started");
807
- _clack_prompts.intro(text);
808
- });
809
- context.on("kubb:format:end", () => {
810
- if (logLevel <= _kubb_core.logLevel.silent) return;
811
- const text = getMessage("Format completed");
812
- _clack_prompts.outro(text);
813
- });
814
- context.on("kubb:lint:start", () => {
815
- if (logLevel <= _kubb_core.logLevel.silent) return;
816
- const text = getMessage("Lint started");
817
- _clack_prompts.intro(text);
818
- });
819
- context.on("kubb:lint:end", () => {
820
- if (logLevel <= _kubb_core.logLevel.silent) return;
821
- const text = getMessage("Lint completed");
822
- _clack_prompts.outro(text);
709
+ onStep("kubb:format:start", "Formatting");
710
+ onStep("kubb:lint:start", "Linting");
711
+ onStep("kubb:hooks:start", "Running hooks");
712
+ context.on("kubb:hook:start", ({ id, command, args }) => {
713
+ if (logLevel <= _kubb_core.logLevel.silent || !id) return;
714
+ stopSpinner();
715
+ const title = getMessage(`Running ${(0, node_util.styleText)("dim", formatCommandWithArgs(command, args))}`);
716
+ const taskLog = _clack_prompts.taskLog({ title });
717
+ state.activeHookLogs.set(id, {
718
+ taskLog,
719
+ hrStart: node_process.default.hrtime()
720
+ });
823
721
  });
824
- context.on("kubb:hook:start", async ({ id, command, args }) => {
722
+ context.on("kubb:hook:end", ({ id, command, args, success, error }) => {
723
+ if (logLevel <= _kubb_core.logLevel.silent || !id) return;
724
+ const active = state.activeHookLogs.get(id);
725
+ if (!active) return;
726
+ state.activeHookLogs.delete(id);
825
727
  const commandWithArgs = formatCommandWithArgs(command, args);
826
- const text = getMessage(`Hook ${(0, node_util.styleText)("dim", commandWithArgs)} started`);
827
- if (!id) return;
828
- if (logLevel <= _kubb_core.logLevel.silent) {
829
- await runHook({
830
- id,
831
- command,
832
- args,
833
- commandWithArgs,
834
- context,
835
- sink: {
836
- onStderr: (s) => console.error(s),
837
- onStdout: (s) => console.log(s)
838
- }
839
- });
840
- return;
728
+ const duration = formatMsWithColor(getElapsedMs(active.hrStart));
729
+ if (success) active.taskLog.success(getMessage(`${(0, node_util.styleText)("dim", commandWithArgs)} completed in ${duration}`));
730
+ else {
731
+ const reason = error?.message ? ` (${error.message})` : "";
732
+ active.taskLog.error(getMessage(`${(0, node_util.styleText)("dim", commandWithArgs)} failed${reason}`), { showLog: true });
841
733
  }
842
- _clack_prompts.intro(text);
843
- const logger = _clack_prompts.taskLog({ title: getMessage(["Executing hook", logLevel >= _kubb_core.logLevel.info ? (0, node_util.styleText)("dim", commandWithArgs) : void 0].filter(Boolean).join(" ")) });
844
- const writable = new ClackWritable(logger);
845
- await runHook({
846
- id,
847
- command,
848
- args,
849
- commandWithArgs,
850
- context,
851
- stream: true,
852
- sink: {
853
- onLine: (line) => writable.write(line),
854
- onStderr: (s) => logger.error(s),
855
- onStdout: (s) => logger.message(s)
856
- }
857
- });
858
- });
859
- context.on("kubb:hook:end", ({ command, args }) => {
860
- if (logLevel <= _kubb_core.logLevel.silent) return;
861
- const text = getMessage(`Hook ${(0, node_util.styleText)("dim", formatCommandWithArgs(command, args))} successfully executed`);
862
- _clack_prompts.outro(text);
863
734
  });
864
735
  context.on("kubb:generation:summary", ({ config, pluginTimings, failedPlugins, filesCreated, status, hrStart }) => {
865
736
  const summary = getSummary({
@@ -890,13 +761,29 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
890
761
  context.on("kubb:lifecycle:end", () => {
891
762
  reset();
892
763
  });
764
+ return (_commandWithArgs, hookId) => {
765
+ if (logLevel <= _kubb_core.logLevel.silent) return {
766
+ onStdout: (s) => console.log(s),
767
+ onStderr: (s) => console.error(s)
768
+ };
769
+ const active = state.activeHookLogs.get(hookId);
770
+ if (!active) return null;
771
+ const { taskLog } = active;
772
+ return {
773
+ stream: true,
774
+ onLine: (line) => taskLog.message((0, node_util.styleText)("dim", line)),
775
+ onStdout: (s) => taskLog.message(s),
776
+ onStderr: (s) => taskLog.message((0, node_util.styleText)("red", s))
777
+ };
778
+ };
893
779
  }
894
780
  });
895
781
  //#endregion
896
782
  //#region src/loggers/fileSystemLogger.ts
897
783
  /**
898
784
  * FileSystem logger that captures debug events and writes them to `.kubb` directory files.
899
- * Note: Logs write on `lifecycle:end` or process exit. Cached logs may be lost if the process crashes before these events.
785
+ *
786
+ * @note Logs are written on `kubb:lifecycle:end` or process exit. Cached logs may be lost if the process crashes before either event.
900
787
  */
901
788
  const fileSystemLogger = (0, _kubb_core.defineLogger)({
902
789
  name: "filesystem",
@@ -921,29 +808,31 @@ const fileSystemLogger = (0, _kubb_core.defineLogger)({
921
808
  const pathName = (0, node_path.resolve)(node_process.default.cwd(), ".kubb", baseName);
922
809
  if (!files[pathName]) files[pathName] = [];
923
810
  if (log.logs.length > 0) {
924
- const timestamp = log.date.toLocaleString();
925
- files[pathName].push(`[${timestamp}]\n${log.logs.join("\n")}`);
811
+ const prefix = `[${log.date.toLocaleString()}] `;
812
+ const indent = " ".repeat(prefix.length);
813
+ const [first, ...rest] = log.logs;
814
+ files[pathName].push([prefix + first, ...rest.map((line) => indent + line)].join("\n"));
926
815
  }
927
816
  }
928
- for (const [fileName, logs] of Object.entries(files)) await write(fileName, logs.join("\n\n"));
817
+ for (const [fileName, logs] of Object.entries(files)) await write(fileName, logs.join("\n"));
929
818
  return Object.keys(files);
930
819
  }
931
820
  context.on("kubb:info", ({ message, info }) => {
932
821
  state.cachedLogs.add({
933
822
  date: /* @__PURE__ */ new Date(),
934
- logs: [`ℹ ${message} ${info}`]
823
+ logs: [`ℹ ${[message, info].filter(Boolean).join(" ")}`]
935
824
  });
936
825
  });
937
826
  context.on("kubb:success", ({ message, info }) => {
938
827
  state.cachedLogs.add({
939
828
  date: /* @__PURE__ */ new Date(),
940
- logs: [`✓ ${message} ${info}`]
829
+ logs: [`✓ ${[message, info].filter(Boolean).join(" ")}`]
941
830
  });
942
831
  });
943
832
  context.on("kubb:warn", ({ message, info }) => {
944
833
  state.cachedLogs.add({
945
834
  date: /* @__PURE__ */ new Date(),
946
- logs: [`⚠ ${message} ${info}`]
835
+ logs: [`⚠ ${[message, info].filter(Boolean).join(" ")}`]
947
836
  });
948
837
  });
949
838
  context.on("kubb:error", ({ error }) => {
@@ -952,29 +841,30 @@ const fileSystemLogger = (0, _kubb_core.defineLogger)({
952
841
  logs: [`✗ ${error.message}`, error.stack || "unknown stack"]
953
842
  });
954
843
  });
955
- context.on("kubb:debug", (message) => {
844
+ context.on("kubb:debug", ({ date, fileName, logs }) => {
956
845
  state.cachedLogs.add({
957
- date: /* @__PURE__ */ new Date(),
958
- logs: message.logs
846
+ date,
847
+ fileName,
848
+ logs
959
849
  });
960
850
  });
961
851
  context.on("kubb:plugin:start", ({ plugin }) => {
962
852
  state.cachedLogs.add({
963
853
  date: /* @__PURE__ */ new Date(),
964
- logs: [`Generating ${plugin.name}`]
854
+ logs: [`► Generating ${plugin.name}`]
965
855
  });
966
856
  });
967
857
  context.on("kubb:plugin:end", ({ plugin, duration, success }) => {
968
858
  const durationStr = formatMs(duration);
969
859
  state.cachedLogs.add({
970
860
  date: /* @__PURE__ */ new Date(),
971
- logs: [success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`]
861
+ logs: [success ? `✓ ${plugin.name} completed in ${durationStr}` : `✗ ${plugin.name} failed in ${durationStr}`]
972
862
  });
973
863
  });
974
864
  context.on("kubb:files:processing:start", ({ files }) => {
975
865
  state.cachedLogs.add({
976
866
  date: /* @__PURE__ */ new Date(),
977
- logs: [`Start ${files.length} writing:`, ...files.map((file) => file.path)]
867
+ logs: [`► Writing ${files.length} files`, ...files.map((file) => ` ${file.path}`)]
978
868
  });
979
869
  });
980
870
  context.on("kubb:generation:end", async ({ config }) => {
@@ -1006,22 +896,16 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1006
896
  install(context, options) {
1007
897
  const logLevel = options?.logLevel ?? _kubb_core.logLevel.info;
1008
898
  const state = {
1009
- totalPlugins: 0,
1010
- completedPlugins: 0,
1011
- failedPlugins: 0,
1012
- totalFiles: 0,
1013
- processedFiles: 0,
1014
- hrStart: process.hrtime(),
1015
- currentConfigs: []
899
+ ...createProgressCounters(),
900
+ currentConfigs: [],
901
+ openGroupDepth: 0
1016
902
  };
903
+ const hookTimer = createHookTimer();
1017
904
  function reset() {
1018
- state.totalPlugins = 0;
1019
- state.completedPlugins = 0;
1020
- state.failedPlugins = 0;
1021
- state.totalFiles = 0;
1022
- state.processedFiles = 0;
1023
- state.hrStart = process.hrtime();
905
+ closeAllGroups();
906
+ resetProgressCounters(state);
1024
907
  state.currentConfigs = [];
908
+ hookTimer.clear();
1025
909
  }
1026
910
  function showProgressStep() {
1027
911
  if (logLevel <= _kubb_core.logLevel.silent) return;
@@ -1033,9 +917,31 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1033
917
  }
1034
918
  function openGroup(name) {
1035
919
  console.log(`::group::${name}`);
920
+ state.openGroupDepth++;
1036
921
  }
1037
922
  function closeGroup(_name) {
1038
923
  console.log("::endgroup::");
924
+ if (state.openGroupDepth > 0) state.openGroupDepth--;
925
+ }
926
+ function closeAllGroups() {
927
+ while (state.openGroupDepth > 0) {
928
+ console.log("::endgroup::");
929
+ state.openGroupDepth--;
930
+ }
931
+ }
932
+ function onGroupStart(event, message, group) {
933
+ context.on(event, () => {
934
+ if (logLevel <= _kubb_core.logLevel.silent) return;
935
+ if (state.currentConfigs.length === 1) openGroup(group);
936
+ console.log(getMessage(message));
937
+ });
938
+ }
939
+ function onGroupEnd(event, message, group) {
940
+ context.on(event, () => {
941
+ if (logLevel <= _kubb_core.logLevel.silent) return;
942
+ console.log(getMessage(message));
943
+ if (state.currentConfigs.length === 1) closeGroup(group);
944
+ });
1039
945
  }
1040
946
  context.on("kubb:info", ({ message, info = "" }) => {
1041
947
  if (logLevel <= _kubb_core.logLevel.silent) return;
@@ -1066,6 +972,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1066
972
  });
1067
973
  context.on("kubb:error", ({ error }) => {
1068
974
  const caused = require_errors.toCause(error);
975
+ closeAllGroups();
1069
976
  if (logLevel <= _kubb_core.logLevel.silent) return;
1070
977
  const message = error.message || String(error);
1071
978
  console.error(`::error::${message}`);
@@ -1083,6 +990,9 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1083
990
  console.log((0, node_util.styleText)("yellow", `Kubb ${version} 🧩`));
1084
991
  reset();
1085
992
  });
993
+ context.on("kubb:version:new", ({ currentVersion, latestVersion }) => {
994
+ console.log(`::notice::Update available for Kubb: v${currentVersion} → v${latestVersion}. Run \`npm install -g @kubb/cli\` to update.`);
995
+ });
1086
996
  context.on("kubb:config:start", () => {
1087
997
  if (logLevel <= _kubb_core.logLevel.silent) return;
1088
998
  const text = getMessage("Configuration started");
@@ -1111,8 +1021,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1111
1021
  });
1112
1022
  context.on("kubb:plugin:end", ({ plugin, duration, success }) => {
1113
1023
  if (logLevel <= _kubb_core.logLevel.silent) return;
1114
- if (success) state.completedPlugins++;
1115
- else state.failedPlugins++;
1024
+ recordPluginResult(state, success);
1116
1025
  const durationStr = formatMsWithColor(duration);
1117
1026
  const text = getMessage(success ? `${(0, node_util.styleText)("bold", plugin.name)} completed in ${durationStr}` : `${(0, node_util.styleText)("bold", plugin.name)} failed in ${(0, node_util.styleText)("red", formatMs(duration))}`);
1118
1027
  console.log(text);
@@ -1135,63 +1044,38 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1135
1044
  if (state.currentConfigs.length === 1) closeGroup("File Generation");
1136
1045
  showProgressStep();
1137
1046
  });
1138
- context.on("kubb:file:processing:update", () => {
1047
+ context.on("kubb:files:processing:update", ({ files }) => {
1139
1048
  if (logLevel <= _kubb_core.logLevel.silent) return;
1140
- state.processedFiles++;
1049
+ state.processedFiles += files.length;
1141
1050
  });
1142
1051
  context.on("kubb:generation:end", ({ config }) => {
1143
1052
  const text = getMessage(config.name ? `${(0, node_util.styleText)("blue", "✓")} Generation completed for ${(0, node_util.styleText)("dim", config.name)}` : `${(0, node_util.styleText)("blue", "✓")} Generation completed`);
1144
1053
  console.log(text);
1145
1054
  });
1146
- context.on("kubb:format:start", () => {
1147
- if (logLevel <= _kubb_core.logLevel.silent) return;
1148
- const text = getMessage("Format started");
1149
- if (state.currentConfigs.length === 1) openGroup("Formatting");
1150
- console.log(text);
1151
- });
1152
- context.on("kubb:format:end", () => {
1153
- if (logLevel <= _kubb_core.logLevel.silent) return;
1154
- const text = getMessage("Format completed");
1155
- console.log(text);
1156
- if (state.currentConfigs.length === 1) closeGroup("Formatting");
1157
- });
1158
- context.on("kubb:lint:start", () => {
1159
- if (logLevel <= _kubb_core.logLevel.silent) return;
1160
- const text = getMessage("Lint started");
1161
- if (state.currentConfigs.length === 1) openGroup("Linting");
1162
- console.log(text);
1163
- });
1164
- context.on("kubb:lint:end", () => {
1055
+ onGroupStart("kubb:format:start", "Format started", "Formatting");
1056
+ onGroupEnd("kubb:format:end", "Format completed", "Formatting");
1057
+ onGroupStart("kubb:lint:start", "Lint started", "Linting");
1058
+ onGroupEnd("kubb:lint:end", "Lint completed", "Linting");
1059
+ onGroupStart("kubb:hooks:start", "Hooks started", "Hooks");
1060
+ onGroupEnd("kubb:hooks:end", "Hooks completed", "Hooks");
1061
+ context.on("kubb:hook:start", ({ id, command, args }) => {
1165
1062
  if (logLevel <= _kubb_core.logLevel.silent) return;
1166
- const text = getMessage("Lint completed");
1167
- console.log(text);
1168
- if (state.currentConfigs.length === 1) closeGroup("Linting");
1169
- });
1170
- context.on("kubb:hook:start", async ({ id, command, args }) => {
1063
+ if (id) hookTimer.start(id);
1171
1064
  const commandWithArgs = formatCommandWithArgs(command, args);
1172
1065
  const text = getMessage(`Hook ${(0, node_util.styleText)("dim", commandWithArgs)} started`);
1173
- if (logLevel > _kubb_core.logLevel.silent) {
1174
- if (state.currentConfigs.length === 1) openGroup(`Hook ${commandWithArgs}`);
1175
- console.log(text);
1176
- }
1177
- if (!id) return;
1178
- await runHook({
1179
- id,
1180
- command,
1181
- args,
1182
- commandWithArgs,
1183
- context,
1184
- sink: {
1185
- onStdout: logLevel > _kubb_core.logLevel.silent ? (s) => console.log(s) : void 0,
1186
- onStderr: logLevel > _kubb_core.logLevel.silent ? (s) => console.error(`::error::${s}`) : void 0
1187
- }
1188
- });
1066
+ if (state.currentConfigs.length === 1) openGroup(`Hook ${commandWithArgs}`);
1067
+ console.log(text);
1189
1068
  });
1190
- context.on("kubb:hook:end", ({ command, args }) => {
1069
+ context.on("kubb:hook:end", ({ id, command, args, success, error }) => {
1191
1070
  if (logLevel <= _kubb_core.logLevel.silent) return;
1071
+ const ms = id ? hookTimer.end(id) : void 0;
1072
+ const durationStr = ms !== void 0 ? ` in ${formatMsWithColor(ms)}` : "";
1192
1073
  const commandWithArgs = formatCommandWithArgs(command, args);
1193
- const text = getMessage(`Hook ${(0, node_util.styleText)("dim", commandWithArgs)} completed`);
1194
- console.log(text);
1074
+ if (success) console.log(getMessage(`${(0, node_util.styleText)("green", "✓")} Hook ${(0, node_util.styleText)("dim", commandWithArgs)} completed${durationStr}`));
1075
+ else {
1076
+ const reason = error?.message ? ` (${error.message})` : "";
1077
+ console.log(`::error::Hook ${commandWithArgs} failed${durationStr}${reason}`);
1078
+ }
1195
1079
  if (state.currentConfigs.length === 1) closeGroup(`Hook ${commandWithArgs}`);
1196
1080
  });
1197
1081
  context.on("kubb:generation:summary", ({ config, status, hrStart, failedPlugins }) => {
@@ -1205,6 +1089,10 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
1205
1089
  context.on("kubb:lifecycle:end", () => {
1206
1090
  reset();
1207
1091
  });
1092
+ return (_commandWithArgs, _hookId) => ({
1093
+ onStdout: logLevel > _kubb_core.logLevel.silent ? (s) => console.log(s) : void 0,
1094
+ onStderr: logLevel > _kubb_core.logLevel.silent ? (s) => console.error(`::error::${s}`) : void 0
1095
+ });
1208
1096
  }
1209
1097
  });
1210
1098
  //#endregion
@@ -1216,9 +1104,16 @@ const plainLogger = (0, _kubb_core.defineLogger)({
1216
1104
  name: "plain",
1217
1105
  install(context, options) {
1218
1106
  const logLevel = options?.logLevel ?? _kubb_core.logLevel.info;
1107
+ const hookTimer = createHookTimer();
1219
1108
  function getMessage(message) {
1220
1109
  return formatMessage(message, logLevel);
1221
1110
  }
1111
+ function onStep(event, message) {
1112
+ context.on(event, () => {
1113
+ if (logLevel <= _kubb_core.logLevel.silent) return;
1114
+ console.log(getMessage(message));
1115
+ });
1116
+ }
1222
1117
  context.on("kubb:info", ({ message, info }) => {
1223
1118
  if (logLevel <= _kubb_core.logLevel.silent) return;
1224
1119
  const text = getMessage([
@@ -1260,19 +1155,15 @@ const plainLogger = (0, _kubb_core.defineLogger)({
1260
1155
  }
1261
1156
  }
1262
1157
  });
1263
- context.on("kubb:lifecycle:start", () => {
1264
- console.log("Kubb CLI 🧩");
1265
- });
1266
- context.on("kubb:config:start", () => {
1267
- if (logLevel <= _kubb_core.logLevel.silent) return;
1268
- const text = getMessage("Configuration started");
1269
- console.log(text);
1158
+ context.on("kubb:lifecycle:start", ({ version }) => {
1159
+ console.log(`Kubb CLI v${version}`);
1270
1160
  });
1271
- context.on("kubb:config:end", () => {
1161
+ context.on("kubb:version:new", ({ currentVersion, latestVersion }) => {
1272
1162
  if (logLevel <= _kubb_core.logLevel.silent) return;
1273
- const text = getMessage("Configuration completed");
1274
- console.log(text);
1163
+ console.log(getMessage(`Update available: v${currentVersion} v${latestVersion}. Run \`npm install -g @kubb/cli\` to update.`));
1275
1164
  });
1165
+ onStep("kubb:config:start", "Configuration started");
1166
+ onStep("kubb:config:end", "Configuration completed");
1276
1167
  context.on("kubb:generation:start", () => {
1277
1168
  const text = getMessage("Generation started");
1278
1169
  console.log(text);
@@ -1293,10 +1184,9 @@ const plainLogger = (0, _kubb_core.defineLogger)({
1293
1184
  const text = getMessage(`Writing ${files.length} files`);
1294
1185
  console.log(text);
1295
1186
  });
1296
- context.on("kubb:file:processing:update", ({ file, config }) => {
1187
+ context.on("kubb:files:processing:update", ({ files }) => {
1297
1188
  if (logLevel <= _kubb_core.logLevel.silent) return;
1298
- const text = getMessage(`Writing ${(0, node_path.relative)(config.root, file.path)}`);
1299
- console.log(text);
1189
+ for (const { file, config } of files) console.log(getMessage(`Writing ${(0, node_path.relative)(config.root, file.path)}`));
1300
1190
  });
1301
1191
  context.on("kubb:files:processing:end", () => {
1302
1192
  if (logLevel <= _kubb_core.logLevel.silent) return;
@@ -1307,47 +1197,28 @@ const plainLogger = (0, _kubb_core.defineLogger)({
1307
1197
  const text = getMessage(config.name ? `Generation completed for ${config.name}` : "Generation completed");
1308
1198
  console.log(text);
1309
1199
  });
1310
- context.on("kubb:format:start", () => {
1311
- if (logLevel <= _kubb_core.logLevel.silent) return;
1312
- const text = getMessage("Format started");
1313
- console.log(text);
1314
- });
1315
- context.on("kubb:format:end", () => {
1316
- if (logLevel <= _kubb_core.logLevel.silent) return;
1317
- const text = getMessage("Format completed");
1318
- console.log(text);
1319
- });
1320
- context.on("kubb:lint:start", () => {
1321
- if (logLevel <= _kubb_core.logLevel.silent) return;
1322
- const text = getMessage("Lint started");
1323
- console.log(text);
1324
- });
1325
- context.on("kubb:lint:end", () => {
1200
+ onStep("kubb:format:start", "Format started");
1201
+ onStep("kubb:format:end", "Format completed");
1202
+ onStep("kubb:lint:start", "Lint started");
1203
+ onStep("kubb:lint:end", "Lint completed");
1204
+ onStep("kubb:hooks:start", "Hooks started");
1205
+ onStep("kubb:hooks:end", "Hooks completed");
1206
+ context.on("kubb:hook:start", ({ id, command, args }) => {
1326
1207
  if (logLevel <= _kubb_core.logLevel.silent) return;
1327
- const text = getMessage("Lint completed");
1328
- console.log(text);
1329
- });
1330
- context.on("kubb:hook:start", async ({ id, command, args }) => {
1208
+ if (id) hookTimer.start(id);
1331
1209
  const commandWithArgs = formatCommandWithArgs(command, args);
1332
- const text = getMessage(`Hook ${commandWithArgs} started`);
1333
- if (logLevel > _kubb_core.logLevel.silent) console.log(text);
1334
- if (!id) return;
1335
- await runHook({
1336
- id,
1337
- command,
1338
- args,
1339
- commandWithArgs,
1340
- context,
1341
- sink: {
1342
- onStdout: logLevel > _kubb_core.logLevel.silent ? (s) => console.log(s) : void 0,
1343
- onStderr: logLevel > _kubb_core.logLevel.silent ? (s) => console.error(s) : void 0
1344
- }
1345
- });
1210
+ console.log(getMessage(`Hook ${commandWithArgs} started`));
1346
1211
  });
1347
- context.on("kubb:hook:end", ({ command, args }) => {
1212
+ context.on("kubb:hook:end", ({ id, command, args, success, error }) => {
1348
1213
  if (logLevel <= _kubb_core.logLevel.silent) return;
1349
- const text = getMessage(`Hook ${formatCommandWithArgs(command, args)} completed`);
1350
- console.log(text);
1214
+ const ms = id ? hookTimer.end(id) : void 0;
1215
+ const durationStr = ms !== void 0 ? ` in ${formatMs(ms)}` : "";
1216
+ const commandWithArgs = formatCommandWithArgs(command, args);
1217
+ if (success) console.log(getMessage(`✓ Hook ${commandWithArgs} completed${durationStr}`));
1218
+ else {
1219
+ const reason = error?.message ? ` (${error.message})` : "";
1220
+ console.log(getMessage(`✗ Hook ${commandWithArgs} failed${durationStr}${reason}`));
1221
+ }
1351
1222
  });
1352
1223
  context.on("kubb:generation:summary", ({ config, pluginTimings, status, hrStart, failedPlugins, filesCreated }) => {
1353
1224
  const summary = getSummary({
@@ -1362,6 +1233,10 @@ const plainLogger = (0, _kubb_core.defineLogger)({
1362
1233
  console.log(summary.join("\n"));
1363
1234
  console.log(require_constants.SUMMARY_SEPARATOR);
1364
1235
  });
1236
+ return (_commandWithArgs, _hookId) => ({
1237
+ onStdout: logLevel > _kubb_core.logLevel.silent ? (s) => console.log(s) : void 0,
1238
+ onStderr: logLevel > _kubb_core.logLevel.silent ? (s) => console.error(s) : void 0
1239
+ });
1365
1240
  }
1366
1241
  });
1367
1242
  //#endregion
@@ -1396,6 +1271,57 @@ function buildProgressLine(state) {
1396
1271
  return parts.join((0, node_util.styleText)("dim", " | "));
1397
1272
  }
1398
1273
  /**
1274
+ * Creates the per-run progress counters shared by the clack and GitHub Actions loggers.
1275
+ */
1276
+ function createProgressCounters() {
1277
+ return {
1278
+ totalPlugins: 0,
1279
+ completedPlugins: 0,
1280
+ failedPlugins: 0,
1281
+ totalFiles: 0,
1282
+ processedFiles: 0,
1283
+ hrStart: node_process.default.hrtime()
1284
+ };
1285
+ }
1286
+ /**
1287
+ * Resets the progress counters in place at the start/end of a generation run.
1288
+ */
1289
+ function resetProgressCounters(state) {
1290
+ state.totalPlugins = 0;
1291
+ state.completedPlugins = 0;
1292
+ state.failedPlugins = 0;
1293
+ state.totalFiles = 0;
1294
+ state.processedFiles = 0;
1295
+ state.hrStart = node_process.default.hrtime();
1296
+ }
1297
+ /**
1298
+ * Records a finished plugin against the progress counters.
1299
+ */
1300
+ function recordPluginResult(state, success) {
1301
+ if (success) state.completedPlugins++;
1302
+ else state.failedPlugins++;
1303
+ }
1304
+ /**
1305
+ * Creates a {@link HookTimer} backed by a private `id → hrtime` map.
1306
+ */
1307
+ function createHookTimer() {
1308
+ const starts = /* @__PURE__ */ new Map();
1309
+ return {
1310
+ start(id) {
1311
+ starts.set(id, node_process.default.hrtime());
1312
+ },
1313
+ end(id) {
1314
+ const hrStart = starts.get(id);
1315
+ if (!hrStart) return;
1316
+ starts.delete(id);
1317
+ return getElapsedMs(hrStart);
1318
+ },
1319
+ clear() {
1320
+ starts.clear();
1321
+ }
1322
+ };
1323
+ }
1324
+ /**
1399
1325
  * Join a command and its optional args into a single display string.
1400
1326
  * e.g. ("prettier", ["--write", "."]) → "prettier --write ."
1401
1327
  */
@@ -1416,125 +1342,247 @@ async function setupLogger(context, { logLevel }) {
1416
1342
  const type = detectLogger();
1417
1343
  const logger = logMapper[type];
1418
1344
  if (!logger) throw new Error(`Unknown adapter type: ${type}`);
1419
- const cleanup = await logger.install(context, { logLevel });
1345
+ const makeSink = await logger.install(context, { logLevel });
1420
1346
  if (logLevel >= _kubb_core.logLevel.debug) await fileSystemLogger.install(context, { logLevel });
1421
- return cleanup;
1347
+ return typeof makeSink === "function" ? makeSink : null;
1422
1348
  }
1423
- //#endregion
1424
- //#region src/utils/executeHooks.ts
1425
- async function executeHooks({ configHooks, hooks }) {
1426
- const commands = Array.isArray(configHooks.done) ? configHooks.done : [configHooks.done].filter(Boolean);
1427
- for (const command of commands) {
1428
- const [cmd, ...args] = require_shell.tokenize(command);
1429
- if (!cmd) continue;
1430
- const hookId = (0, node_crypto.createHash)("sha256").update(command).digest("hex");
1431
- const hookEndPromise = new Promise((resolve, reject) => {
1432
- const handler = (ctx) => {
1433
- if (ctx.id !== hookId) return;
1434
- hooks.off("kubb:hook:end", handler);
1435
- if (!ctx.success) {
1436
- reject(ctx.error ?? /* @__PURE__ */ new Error(`Hook failed: ${command}`));
1437
- return;
1438
- }
1439
- hooks.emit("kubb:success", { message: `${(0, node_util.styleText)("dim", command)} successfully executed` }).then(resolve).catch(reject);
1440
- };
1441
- hooks.on("kubb:hook:end", handler);
1442
- });
1443
- await hooks.emit("kubb:hook:start", {
1444
- id: hookId,
1445
- command: cmd,
1446
- args
1349
+ /**
1350
+ * Builds the generation summary lines rendered in the end-of-run box.
1351
+ * Returns an array of styled strings, one per summary row.
1352
+ */
1353
+ function getSummary({ failedPlugins, filesCreated, status, hrStart, config, pluginTimings }) {
1354
+ const duration = formatHrtime(hrStart);
1355
+ const pluginsCount = config.plugins?.length ?? 0;
1356
+ const successCount = pluginsCount - failedPlugins.size;
1357
+ const meta = {
1358
+ plugins: status === "success" ? `${(0, node_util.styleText)("green", `${successCount} successful`)}, ${pluginsCount} total` : `${(0, node_util.styleText)("green", `${successCount} successful`)}, ${(0, node_util.styleText)("red", `${failedPlugins.size} failed`)}, ${pluginsCount} total`,
1359
+ pluginsFailed: status === "failed" ? [...failedPlugins].map(({ plugin }) => randomCliColor(plugin.name)).join(", ") : void 0,
1360
+ filesCreated,
1361
+ time: (0, node_util.styleText)("green", duration),
1362
+ output: node_path.default.resolve(config.root, config.output.path)
1363
+ };
1364
+ const labels = {
1365
+ plugins: "Plugins:",
1366
+ failed: "Failed:",
1367
+ generated: "Generated:",
1368
+ pluginTimings: "Plugin Timings:",
1369
+ output: "Output:"
1370
+ };
1371
+ const maxLength = Math.max(0, ...[...Object.values(labels), ...pluginTimings ? Array.from(pluginTimings.keys()) : []].map((s) => s.length));
1372
+ const summaryLines = [];
1373
+ summaryLines.push(`${labels.plugins.padEnd(maxLength + 2)} ${meta.plugins}`);
1374
+ if (meta.pluginsFailed) summaryLines.push(`${labels.failed.padEnd(maxLength + 2)} ${meta.pluginsFailed}`);
1375
+ summaryLines.push(`${labels.generated.padEnd(maxLength + 2)} ${meta.filesCreated} files in ${meta.time}`);
1376
+ if (pluginTimings && pluginTimings.size > 0) {
1377
+ const sortedTimings = Array.from(pluginTimings.entries()).sort((a, b) => b[1] - a[1]);
1378
+ summaryLines.push(`${labels.pluginTimings}`);
1379
+ sortedTimings.forEach(([name, time]) => {
1380
+ const timeStr = time >= 1e3 ? `${(time / 1e3).toFixed(2)}s` : `${Math.round(time)}ms`;
1381
+ const barLength = Math.min(Math.ceil(time / 100), 10);
1382
+ const bar = (0, node_util.styleText)("dim", "█".repeat(barLength));
1383
+ summaryLines.push(`${(0, node_util.styleText)("dim", "•")} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`);
1447
1384
  });
1448
- await hookEndPromise;
1449
1385
  }
1386
+ summaryLines.push(`${labels.output.padEnd(maxLength + 2)} ${meta.output}`);
1387
+ return summaryLines;
1450
1388
  }
1451
1389
  //#endregion
1452
- //#region src/utils/getConfig.ts
1453
- async function getConfigs(config, args) {
1454
- const resolved = await (typeof config === "function" ? config(args) : config);
1455
- return (Array.isArray(resolved) ? resolved : [resolved]).map((item) => ({
1456
- ...item,
1457
- plugins: item.plugins ?? []
1458
- }));
1459
- }
1460
- //#endregion
1461
- //#region src/utils/getCosmiConfig.ts
1462
- const unrunInputOptions = { transform: { jsx: {
1463
- runtime: "automatic",
1464
- importSource: "@kubb/renderer-jsx"
1465
- } } };
1466
- const tsLoader = async (configFile) => {
1467
- const { module } = await (0, unrun.unrun)({
1468
- path: configFile,
1469
- inputOptions: unrunInputOptions
1470
- });
1471
- return module;
1472
- };
1473
- async function getCosmiConfig(moduleName, config) {
1474
- let result;
1475
- const searchPlaces = [
1476
- "package.json",
1477
- `.${moduleName}rc`,
1478
- `.${moduleName}rc.json`,
1479
- `.${moduleName}rc.yaml`,
1480
- `.${moduleName}rc.yml`,
1481
- `.${moduleName}rc.ts`,
1482
- `.${moduleName}rc.mts`,
1483
- `.${moduleName}rc.cts`,
1484
- `.${moduleName}rc.js`,
1485
- `.${moduleName}rc.mjs`,
1486
- `.${moduleName}rc.cjs`,
1487
- `${moduleName}.config.ts`,
1488
- `${moduleName}.config.mts`,
1489
- `${moduleName}.config.cts`,
1490
- `${moduleName}.config.js`,
1491
- `${moduleName}.config.mjs`,
1492
- `${moduleName}.config.cjs`
1493
- ];
1494
- const explorer = (0, cosmiconfig.cosmiconfig)(moduleName, {
1390
+ //#region src/runners/generate/utils.ts
1391
+ const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, {
1392
+ jsx: {
1393
+ runtime: "automatic",
1394
+ importSource: "@kubb/renderer-jsx"
1395
+ },
1396
+ moduleCache: false
1397
+ });
1398
+ const tsLoader = (configFile) => jiti$1.import(configFile, { default: true });
1399
+ const MODULE_NAME = "kubb";
1400
+ const BASE_SEARCH_PLACES = [
1401
+ "package.json",
1402
+ `.${MODULE_NAME}rc`,
1403
+ `.${MODULE_NAME}rc.json`,
1404
+ `.${MODULE_NAME}rc.yaml`,
1405
+ `.${MODULE_NAME}rc.yml`,
1406
+ `.${MODULE_NAME}rc.ts`,
1407
+ `.${MODULE_NAME}rc.mts`,
1408
+ `.${MODULE_NAME}rc.cts`,
1409
+ `.${MODULE_NAME}rc.js`,
1410
+ `.${MODULE_NAME}rc.mjs`,
1411
+ `.${MODULE_NAME}rc.cjs`,
1412
+ `${MODULE_NAME}.config.ts`,
1413
+ `${MODULE_NAME}.config.mts`,
1414
+ `${MODULE_NAME}.config.cts`,
1415
+ `${MODULE_NAME}.config.js`,
1416
+ `${MODULE_NAME}.config.mjs`,
1417
+ `${MODULE_NAME}.config.cjs`
1418
+ ];
1419
+ const SEARCH_PLACES = [
1420
+ "",
1421
+ ".config/",
1422
+ "configs/"
1423
+ ].flatMap((prefix) => BASE_SEARCH_PLACES.map((p) => `${prefix}${p}`));
1424
+ async function getCosmiConfig(configFile) {
1425
+ const explorer = (0, cosmiconfig.cosmiconfig)(MODULE_NAME, {
1495
1426
  cache: false,
1496
- searchPlaces: [
1497
- ...searchPlaces.map((searchPlace) => {
1498
- return `.config/${searchPlace}`;
1499
- }),
1500
- ...searchPlaces.map((searchPlace) => {
1501
- return `configs/${searchPlace}`;
1502
- }),
1503
- ...searchPlaces
1504
- ],
1427
+ searchPlaces: SEARCH_PLACES,
1505
1428
  loaders: {
1506
1429
  ".ts": tsLoader,
1507
1430
  ".mts": tsLoader,
1508
1431
  ".cts": tsLoader
1509
1432
  }
1510
1433
  });
1434
+ let result;
1511
1435
  try {
1512
- result = config ? await explorer.load(config) : await explorer.search();
1436
+ result = configFile ? await explorer.load(configFile) : await explorer.search();
1513
1437
  } catch (error) {
1514
1438
  throw new Error("Config failed loading", { cause: error });
1515
1439
  }
1516
- if (result?.isEmpty || !result || !result.config) throw new Error("Config not defined, create a kubb.config.js or pass through your config with the option --config");
1440
+ if (!result?.config || result.isEmpty) throw new Error("Config not defined, create a kubb.config.js or pass through your config with the option --config");
1517
1441
  return result;
1518
1442
  }
1519
- //#endregion
1520
- //#region src/utils/watcher.ts
1521
- async function startWatcher(path, cb) {
1443
+ /**
1444
+ * Discovers the Kubb config via cosmiconfig and resolves it into a normalized array of configs.
1445
+ * Every config in the result is guaranteed to have a `plugins` array.
1446
+ */
1447
+ async function getConfigs({ configPath, input, watch, logLevel }) {
1448
+ const result = await getCosmiConfig(configPath);
1449
+ const cli = {
1450
+ config: configPath,
1451
+ input,
1452
+ watch,
1453
+ logLevel
1454
+ };
1455
+ const resolved = await (typeof result.config === "function" ? result.config(cli) : result.config);
1456
+ const userConfigs = Array.isArray(resolved) ? resolved : [resolved];
1457
+ return {
1458
+ configPath: result.filepath,
1459
+ configs: userConfigs.map((item) => ({
1460
+ ...item,
1461
+ plugins: item.plugins ?? []
1462
+ }))
1463
+ };
1464
+ }
1465
+ /**
1466
+ * Runs the `done` hooks defined in a Kubb config in sequence.
1467
+ */
1468
+ async function executeHooks({ configHooks, hooks, makeSink }) {
1469
+ const commands = Array.isArray(configHooks.done) ? configHooks.done : [configHooks.done].filter(Boolean);
1470
+ for (const command of commands) {
1471
+ const [cmd, ...args] = require_shell.tokenize(command);
1472
+ if (!cmd) continue;
1473
+ const hookId = (0, node_crypto.createHash)("sha256").update(command).digest("hex");
1474
+ const commandWithArgs = [cmd, ...args].join(" ");
1475
+ await hooks.emit("kubb:hook:start", {
1476
+ id: hookId,
1477
+ command: cmd,
1478
+ args
1479
+ });
1480
+ const { stream = false, onLine, onStdout, onStderr } = makeSink?.(commandWithArgs, hookId) ?? {};
1481
+ await runHook({
1482
+ id: hookId,
1483
+ command: cmd,
1484
+ args,
1485
+ commandWithArgs,
1486
+ context: hooks,
1487
+ stream,
1488
+ sink: {
1489
+ onLine,
1490
+ onStdout,
1491
+ onStderr
1492
+ }
1493
+ });
1494
+ }
1495
+ }
1496
+ async function runHook({ id, command, args, commandWithArgs, context, stream = false, sink }) {
1497
+ const emitEnd = (success, error) => context.emit("kubb:hook:end", {
1498
+ command,
1499
+ args,
1500
+ id,
1501
+ success,
1502
+ error
1503
+ });
1504
+ try {
1505
+ const proc = (0, tinyexec.x)(command, [...args ?? []], {
1506
+ nodeOptions: { detached: process.platform !== "win32" },
1507
+ throwOnError: true
1508
+ });
1509
+ if (stream && sink?.onLine) for await (const line of proc) sink.onLine(line);
1510
+ const result = await proc;
1511
+ await context.emit("kubb:debug", {
1512
+ date: /* @__PURE__ */ new Date(),
1513
+ logs: [result.stdout.trimEnd()]
1514
+ });
1515
+ await context.emit("kubb:success", { message: `${(0, node_util.styleText)("dim", commandWithArgs)} successfully executed` });
1516
+ await emitEnd(true, null);
1517
+ } catch (err) {
1518
+ if (!(err instanceof tinyexec.NonZeroExitError)) {
1519
+ const error = require_errors.toError(err);
1520
+ await emitEnd(false, error);
1521
+ await context.emit("kubb:error", { error });
1522
+ return;
1523
+ }
1524
+ const stderr = err.output?.stderr ?? "";
1525
+ const stdout = err.output?.stdout ?? "";
1526
+ await context.emit("kubb:debug", {
1527
+ date: /* @__PURE__ */ new Date(),
1528
+ logs: [stdout, stderr].filter(Boolean)
1529
+ });
1530
+ if (stderr) sink?.onStderr?.(stderr);
1531
+ if (stdout) sink?.onStdout?.(stdout);
1532
+ const error = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
1533
+ await emitEnd(false, error);
1534
+ await context.emit("kubb:error", { error });
1535
+ }
1536
+ }
1537
+ /**
1538
+ * Starts a file watcher on the given paths and calls `cb` on any change.
1539
+ * Ignores `.git` and `node_modules` directories.
1540
+ */
1541
+ async function startWatcher(path, cb, log = {
1542
+ info: console.log,
1543
+ error: console.log
1544
+ }) {
1522
1545
  const { watch } = await import("chokidar");
1523
- watch(path, {
1546
+ const watcher = watch(path, {
1524
1547
  ignorePermissionErrors: true,
1525
1548
  ignored: require_constants.WATCHER_IGNORED_PATHS
1526
- }).on("all", async (type, file) => {
1527
- console.log((0, node_util.styleText)("yellow", (0, node_util.styleText)("bold", `Change detected: ${type} ${file}`)));
1549
+ });
1550
+ process.once("SIGINT", () => {
1551
+ watcher.close();
1552
+ });
1553
+ process.once("SIGTERM", () => {
1554
+ watcher.close();
1555
+ });
1556
+ watcher.on("all", async (type, file) => {
1557
+ log.info((0, node_util.styleText)("yellow", (0, node_util.styleText)("bold", `Change detected: ${type} ${file}`)));
1528
1558
  try {
1529
1559
  await cb(path);
1530
1560
  } catch (_e) {
1531
- console.log((0, node_util.styleText)("red", "Watcher failed"));
1561
+ log.error((0, node_util.styleText)("red", "Watcher failed"));
1532
1562
  }
1533
1563
  });
1534
1564
  }
1535
1565
  //#endregion
1536
- //#region src/runners/generate.ts
1537
- async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefix, noToolMessage, configName, outputPath, logLevel, hooks, onStart, onEnd }) {
1566
+ //#region src/runners/generate/run.ts
1567
+ /**
1568
+ * Registers a one-shot `kubb:hook:end` listener for `hookId` BEFORE the caller emits `kubb:hook:start`,
1569
+ * avoiding the race where a synchronous emitter fires end before the listener is attached.
1570
+ */
1571
+ function waitForHookEnd(hooks, hookId, onSuccess, fallbackErrorMessage) {
1572
+ return new Promise((resolve, reject) => {
1573
+ const handler = (ctx) => {
1574
+ if (ctx.id !== hookId) return;
1575
+ hooks.off("kubb:hook:end", handler);
1576
+ if (!ctx.success) {
1577
+ reject(ctx.error ?? new Error(fallbackErrorMessage));
1578
+ return;
1579
+ }
1580
+ Promise.resolve(onSuccess()).then(resolve).catch(reject);
1581
+ };
1582
+ hooks.on("kubb:hook:end", handler);
1583
+ });
1584
+ }
1585
+ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefix, noToolMessage, configName, outputPath, logLevel, hooks, makeSink, onStart, onEnd }) {
1538
1586
  await onStart();
1539
1587
  let resolvedTool = toolValue;
1540
1588
  if (resolvedTool === "auto") {
@@ -1548,29 +1596,35 @@ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefi
1548
1596
  let toolError;
1549
1597
  if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap) {
1550
1598
  const toolConfig = toolMap[resolvedTool];
1599
+ const hookId = (0, node_crypto.createHash)("sha256").update([configName, resolvedTool].filter(Boolean).join("-")).digest("hex");
1600
+ const successMessage = [
1601
+ `${successPrefix} with ${(0, node_util.styleText)("dim", resolvedTool)}`,
1602
+ logLevel >= _kubb_core.logLevel.info ? `on ${(0, node_util.styleText)("dim", outputPath)}` : void 0,
1603
+ "successfully"
1604
+ ].filter(Boolean).join(" ");
1551
1605
  try {
1552
- const hookId = (0, node_crypto.createHash)("sha256").update([configName, resolvedTool].filter(Boolean).join("-")).digest("hex");
1553
- const hookEndPromise = new Promise((resolve, reject) => {
1554
- const handler = (ctx) => {
1555
- if (ctx.id !== hookId) return;
1556
- hooks.off("kubb:hook:end", handler);
1557
- if (!ctx.success) {
1558
- reject(ctx.error ?? /* @__PURE__ */ new Error(`${toolConfig.errorMessage}`));
1559
- return;
1560
- }
1561
- hooks.emit("kubb:success", { message: [
1562
- `${successPrefix} with ${(0, node_util.styleText)("dim", resolvedTool)}`,
1563
- logLevel >= _kubb_core.logLevel.info ? `on ${(0, node_util.styleText)("dim", outputPath)}` : void 0,
1564
- "successfully"
1565
- ].filter(Boolean).join(" ") }).then(resolve).catch(reject);
1566
- };
1567
- hooks.on("kubb:hook:end", handler);
1568
- });
1606
+ const hookArgs = toolConfig.args(outputPath);
1607
+ const commandWithArgs = [toolConfig.command, ...hookArgs].join(" ");
1608
+ const hookEndPromise = waitForHookEnd(hooks, hookId, () => hooks.emit("kubb:success", { message: successMessage }), toolConfig.errorMessage);
1569
1609
  await hooks.emit("kubb:hook:start", {
1570
1610
  id: hookId,
1571
1611
  command: toolConfig.command,
1572
- args: toolConfig.args(outputPath)
1612
+ args: hookArgs
1573
1613
  });
1614
+ const { stream = false, onLine, onStdout, onStderr } = makeSink?.(commandWithArgs, hookId) ?? {};
1615
+ runHook({
1616
+ id: hookId,
1617
+ command: toolConfig.command,
1618
+ args: hookArgs,
1619
+ commandWithArgs,
1620
+ context: hooks,
1621
+ stream,
1622
+ sink: {
1623
+ onLine,
1624
+ onStdout,
1625
+ onStderr
1626
+ }
1627
+ }).catch(() => {});
1574
1628
  await hookEndPromise;
1575
1629
  } catch (caughtError) {
1576
1630
  const err = require_errors.toError(caughtError);
@@ -1582,9 +1636,9 @@ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefi
1582
1636
  if (toolError) throw toolError;
1583
1637
  }
1584
1638
  async function generate(options) {
1585
- const { input, hooks, logLevel } = options;
1639
+ const { input, hooks, logLevel, makeSink } = options;
1586
1640
  const hrStart = node_process.default.hrtime();
1587
- const inputPath = input ?? ("path" in options.config.input ? options.config.input.path : void 0);
1641
+ const inputPath = input ?? (options.config.input && "path" in options.config.input ? options.config.input.path : void 0);
1588
1642
  const config = {
1589
1643
  ...options.config,
1590
1644
  input: inputPath ? {
@@ -1594,25 +1648,36 @@ async function generate(options) {
1594
1648
  ...options.config.output
1595
1649
  };
1596
1650
  const kubb = (0, _kubb_core.createKubb)(config, { hooks });
1597
- await kubb.setup();
1598
1651
  await hooks.emit("kubb:generation:start", { config });
1599
1652
  await hooks.emit("kubb:info", {
1600
1653
  message: config.name ? `Setup generation ${(0, node_util.styleText)("bold", config.name)}` : "Setup generation",
1601
1654
  info: inputPath
1602
1655
  });
1656
+ await kubb.setup();
1603
1657
  await hooks.emit("kubb:info", {
1604
1658
  message: config.name ? `Build generation ${(0, node_util.styleText)("bold", config.name)}` : "Build generation",
1605
1659
  info: inputPath
1606
1660
  });
1607
1661
  const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild();
1608
1662
  await hooks.emit("kubb:info", { message: "Load summary" });
1663
+ const telemetryPlugins = Array.from(driver.plugins.values(), (p) => ({
1664
+ name: p.name,
1665
+ options: p.options
1666
+ }));
1667
+ const reportTelemetry = (status) => require_telemetry.sendTelemetry(require_telemetry.buildTelemetryEvent({
1668
+ command: "generate",
1669
+ kubbVersion: require_package.version,
1670
+ plugins: telemetryPlugins,
1671
+ hrStart,
1672
+ filesCreated: files.length,
1673
+ status
1674
+ }));
1609
1675
  if (failedPlugins.size > 0 || error) {
1610
- const allErrors = [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean);
1676
+ const allErrors = [error, ...Array.from(failedPlugins, (it) => it.error)].filter(Boolean);
1611
1677
  for (const err of allErrors) await hooks.emit("kubb:error", { error: err });
1612
1678
  await hooks.emit("kubb:generation:end", {
1613
1679
  config,
1614
- files,
1615
- sources: kubb.sources
1680
+ storage: kubb.storage
1616
1681
  });
1617
1682
  await hooks.emit("kubb:generation:summary", {
1618
1683
  config,
@@ -1622,62 +1687,51 @@ async function generate(options) {
1622
1687
  hrStart,
1623
1688
  pluginTimings: logLevel >= _kubb_core.logLevel.verbose ? pluginTimings : void 0
1624
1689
  });
1625
- await require_telemetry.sendTelemetry(require_telemetry.buildTelemetryEvent({
1626
- command: "generate",
1627
- kubbVersion: require_package.version,
1628
- plugins: Array.from(driver.plugins.values(), (p) => ({
1629
- name: p.name,
1630
- options: p.options
1631
- })),
1632
- hrStart,
1633
- filesCreated: files.length,
1634
- status: "failed"
1635
- }));
1636
- node_process.default.exit(1);
1690
+ await reportTelemetry("failed");
1691
+ return false;
1637
1692
  }
1638
1693
  await hooks.emit("kubb:success", {
1639
- message: "Generation successfully",
1694
+ message: "Generation succeeded",
1640
1695
  info: inputPath
1641
1696
  });
1642
1697
  await hooks.emit("kubb:generation:end", {
1643
1698
  config,
1644
- files,
1645
- sources: kubb.sources
1699
+ storage: kubb.storage
1646
1700
  });
1647
1701
  const outputPath = node_path.default.resolve(config.root, config.output.path);
1648
- if (config.output.format) await runToolPass({
1702
+ const toolPasses = [config.output.format && {
1649
1703
  toolValue: config.output.format,
1650
1704
  detect: detectFormatter,
1651
1705
  toolMap: formatters,
1652
1706
  toolLabel: "formatter",
1653
1707
  successPrefix: "Formatting",
1654
1708
  noToolMessage: "No formatter found (oxfmt, biome, or prettier). Skipping formatting.",
1655
- configName: config.name,
1656
- outputPath,
1657
- logLevel,
1658
- hooks,
1659
1709
  onStart: () => hooks.emit("kubb:format:start"),
1660
1710
  onEnd: () => hooks.emit("kubb:format:end")
1661
- });
1662
- if (config.output.lint) await runToolPass({
1711
+ }, config.output.lint && {
1663
1712
  toolValue: config.output.lint,
1664
1713
  detect: detectLinter,
1665
1714
  toolMap: linters,
1666
1715
  toolLabel: "linter",
1667
1716
  successPrefix: "Linting",
1668
1717
  noToolMessage: "No linter found (oxlint, biome, or eslint). Skipping linting.",
1718
+ onStart: () => hooks.emit("kubb:lint:start"),
1719
+ onEnd: () => hooks.emit("kubb:lint:end")
1720
+ }].filter(Boolean);
1721
+ for (const pass of toolPasses) await runToolPass({
1722
+ ...pass,
1669
1723
  configName: config.name,
1670
1724
  outputPath,
1671
1725
  logLevel,
1672
1726
  hooks,
1673
- onStart: () => hooks.emit("kubb:lint:start"),
1674
- onEnd: () => hooks.emit("kubb:lint:end")
1727
+ makeSink
1675
1728
  });
1676
1729
  if (config.hooks) {
1677
1730
  await hooks.emit("kubb:hooks:start");
1678
1731
  await executeHooks({
1679
1732
  configHooks: config.hooks,
1680
- hooks
1733
+ hooks,
1734
+ makeSink
1681
1735
  });
1682
1736
  await hooks.emit("kubb:hooks:end");
1683
1737
  }
@@ -1689,68 +1743,82 @@ async function generate(options) {
1689
1743
  hrStart,
1690
1744
  pluginTimings
1691
1745
  });
1692
- await require_telemetry.sendTelemetry(require_telemetry.buildTelemetryEvent({
1693
- command: "generate",
1694
- kubbVersion: require_package.version,
1695
- plugins: Array.from(driver.plugins.values(), (p) => ({
1696
- name: p.name,
1697
- options: p.options
1698
- })),
1699
- hrStart,
1700
- filesCreated: files.length,
1701
- status: "success"
1702
- }));
1746
+ await reportTelemetry("success");
1747
+ return true;
1703
1748
  }
1704
- async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }) {
1705
- const logLevel = _kubb_core.logLevel[logLevelKey] ?? _kubb_core.logLevel.info;
1706
- const hooks = new AsyncEventEmitter();
1707
- await setupLogger(hooks, { logLevel });
1749
+ async function checkForUpdate(hooks) {
1708
1750
  await require_telemetry.executeIfOnline(async () => {
1709
1751
  try {
1710
- const latestVersion = (await (await fetch(require_constants.KUBB_NPM_PACKAGE_URL)).json()).version;
1711
- if (latestVersion && require_package.version < latestVersion) await hooks.emit("kubb:version:new", {
1752
+ const data = await (await fetch(require_constants.KUBB_NPM_PACKAGE_URL)).json();
1753
+ if (data.version && require_package.version < data.version) await hooks.emit("kubb:version:new", {
1712
1754
  currentVersion: require_package.version,
1713
- latestVersion
1755
+ latestVersion: data.version
1714
1756
  });
1715
1757
  } catch {}
1716
1758
  });
1759
+ }
1760
+ /**
1761
+ * Runs the full Kubb generation lifecycle for the given CLI options.
1762
+ * Sets up the logger, checks for a newer version, loads configs, and calls `generate` for each config entry.
1763
+ */
1764
+ async function run({ input, configPath, logLevel: logLevelKey, watch }) {
1765
+ const logLevel = _kubb_core.logLevel[logLevelKey] ?? _kubb_core.logLevel.info;
1766
+ const hooks = new AsyncEventEmitter();
1767
+ const makeSink = await setupLogger(hooks, { logLevel });
1768
+ await hooks.emit("kubb:lifecycle:start", { version: require_package.version });
1769
+ await checkForUpdate(hooks);
1717
1770
  try {
1718
- const result = await getCosmiConfig("kubb", configPath);
1719
- const configs = await getConfigs(result.config, { input });
1720
1771
  await hooks.emit("kubb:config:start");
1772
+ const { configs, configPath: resolvedConfigPath } = await getConfigs({
1773
+ configPath,
1774
+ input,
1775
+ watch,
1776
+ logLevel: logLevelKey
1777
+ });
1778
+ const relativeConfigPath = node_path.default.relative(node_process.default.cwd(), resolvedConfigPath);
1721
1779
  await hooks.emit("kubb:info", {
1722
1780
  message: "Config loaded",
1723
- info: node_path.default.relative(node_process.default.cwd(), result.filepath)
1781
+ info: relativeConfigPath
1724
1782
  });
1725
1783
  await hooks.emit("kubb:success", {
1726
1784
  message: "Config loaded successfully",
1727
- info: node_path.default.relative(node_process.default.cwd(), result.filepath)
1785
+ info: relativeConfigPath
1728
1786
  });
1729
1787
  await hooks.emit("kubb:config:end", { configs });
1730
- await hooks.emit("kubb:lifecycle:start", { version: require_package.version });
1788
+ let anyFailed = false;
1731
1789
  for (const config of configs) if ((0, _kubb_core.isInputPath)(config) && watch) await startWatcher([input || config.input.path], async (paths) => {
1732
- hooks.removeAll();
1733
1790
  await generate({
1734
1791
  input,
1735
1792
  config,
1736
1793
  logLevel,
1737
- hooks
1794
+ hooks,
1795
+ makeSink
1738
1796
  });
1739
1797
  _clack_prompts.log.step((0, node_util.styleText)("yellow", `Watching for changes in ${paths.join(" and ")}`));
1798
+ }, {
1799
+ info: (msg) => _clack_prompts.log.info(msg),
1800
+ error: (msg) => _clack_prompts.log.error(msg)
1740
1801
  });
1741
- else await generate({
1742
- input,
1743
- config,
1744
- logLevel,
1745
- hooks
1746
- });
1802
+ else try {
1803
+ if (!await generate({
1804
+ input,
1805
+ config,
1806
+ logLevel,
1807
+ hooks,
1808
+ makeSink
1809
+ })) anyFailed = true;
1810
+ } catch (configError) {
1811
+ await hooks.emit("kubb:error", { error: require_errors.toError(configError) });
1812
+ anyFailed = true;
1813
+ }
1747
1814
  await hooks.emit("kubb:lifecycle:end");
1815
+ if (anyFailed) node_process.default.exit(1);
1748
1816
  } catch (error) {
1749
1817
  await hooks.emit("kubb:error", { error: require_errors.toError(error) });
1750
1818
  node_process.default.exit(1);
1751
1819
  }
1752
1820
  }
1753
1821
  //#endregion
1754
- exports.runGenerateCommand = runGenerateCommand;
1822
+ exports.run = run;
1755
1823
 
1756
- //# sourceMappingURL=generate-DMqdAYqy.cjs.map
1824
+ //# sourceMappingURL=run-DeWgpA6S.cjs.map