@kubb/cli 5.0.0-alpha.9 → 5.0.0-beta.10

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 (173) hide show
  1. package/README.md +180 -27
  2. package/bin/kubb.js +6 -0
  3. package/dist/agent-Bx2yllmS.js +68 -0
  4. package/dist/agent-Bx2yllmS.js.map +1 -0
  5. package/dist/agent-CeLwj5im.cjs +70 -0
  6. package/dist/agent-CeLwj5im.cjs.map +1 -0
  7. package/dist/{chunk--u3MIqq1.js → chunk-BvFE5Tac.js} +1 -0
  8. package/dist/constants-B2JTeRBb.js +42 -0
  9. package/dist/constants-B2JTeRBb.js.map +1 -0
  10. package/dist/constants-BINTA5VZ.cjs +77 -0
  11. package/dist/constants-BINTA5VZ.cjs.map +1 -0
  12. package/dist/constants-BYGmiFs0.cjs +139 -0
  13. package/dist/constants-BYGmiFs0.cjs.map +1 -0
  14. package/dist/constants-DSJ-Xrbv.js +116 -0
  15. package/dist/constants-DSJ-Xrbv.js.map +1 -0
  16. package/dist/define-Bdn8j5VM.cjs +54 -0
  17. package/dist/define-Bdn8j5VM.cjs.map +1 -0
  18. package/dist/define-m_fp-Aqm.js +43 -0
  19. package/dist/define-m_fp-Aqm.js.map +1 -0
  20. package/dist/errors-CINO1EIv.js +43 -0
  21. package/dist/errors-CINO1EIv.js.map +1 -0
  22. package/dist/{errors-DBW0N9w4.cjs → errors-CLCjoSg0.cjs} +22 -6
  23. package/dist/errors-CLCjoSg0.cjs.map +1 -0
  24. package/dist/{generate-Rly1EXBN.js → generate-BLvcvoIj.js} +12 -6
  25. package/dist/generate-BLvcvoIj.js.map +1 -0
  26. package/dist/{generate-DU5zzc54.cjs → generate-drLxTAnz.cjs} +11 -5
  27. package/dist/generate-drLxTAnz.cjs.map +1 -0
  28. package/dist/index.cjs +52 -21
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +53 -22
  32. package/dist/index.js.map +1 -1
  33. package/dist/init-CGu7JZEF.js +53 -0
  34. package/dist/init-CGu7JZEF.js.map +1 -0
  35. package/dist/init-CyCjvIEF.cjs +53 -0
  36. package/dist/init-CyCjvIEF.cjs.map +1 -0
  37. package/dist/mcp-Cq2sylQC.js +39 -0
  38. package/dist/mcp-Cq2sylQC.js.map +1 -0
  39. package/dist/mcp-DSF5gpI-.cjs +39 -0
  40. package/dist/mcp-DSF5gpI-.cjs.map +1 -0
  41. package/dist/{package-BJ6ionm6.cjs → package-DZ-6zAIO.cjs} +2 -2
  42. package/dist/package-DZ-6zAIO.cjs.map +1 -0
  43. package/dist/package-OLYIpjqw.js +6 -0
  44. package/dist/package-OLYIpjqw.js.map +1 -0
  45. package/dist/{generate-BHNyeQXl.js → run-Bfbr3RaM.js} +781 -502
  46. package/dist/run-Bfbr3RaM.js.map +1 -0
  47. package/dist/run-BzpYYOQs.js +121 -0
  48. package/dist/run-BzpYYOQs.js.map +1 -0
  49. package/dist/run-CCZ24VKk.js +51 -0
  50. package/dist/run-CCZ24VKk.js.map +1 -0
  51. package/dist/run-CF97BWVa.js +244 -0
  52. package/dist/run-CF97BWVa.js.map +1 -0
  53. package/dist/run-CQbj3ley.cjs +52 -0
  54. package/dist/run-CQbj3ley.cjs.map +1 -0
  55. package/dist/{generate-Cq5RDTBL.cjs → run-CVlrIZoW.cjs} +786 -507
  56. package/dist/run-CVlrIZoW.cjs.map +1 -0
  57. package/dist/run-D0hmRpHy.js +49 -0
  58. package/dist/run-D0hmRpHy.js.map +1 -0
  59. package/dist/run-DwdAwnLG.cjs +125 -0
  60. package/dist/run-DwdAwnLG.cjs.map +1 -0
  61. package/dist/run-Lr0Ctnu0.cjs +50 -0
  62. package/dist/run-Lr0Ctnu0.cjs.map +1 -0
  63. package/dist/run-YsoCk5we.cjs +248 -0
  64. package/dist/run-YsoCk5we.cjs.map +1 -0
  65. package/dist/{shell-7HPrTCJ5.cjs → shell-475fQKaX.cjs} +8 -3
  66. package/dist/shell-475fQKaX.cjs.map +1 -0
  67. package/dist/{shell-DqqWsHCD.js → shell-CN6DNqeC.js} +9 -4
  68. package/dist/shell-CN6DNqeC.js.map +1 -0
  69. package/dist/{telemetry-DZ7IrLAU.cjs → telemetry-B2iWkY5e.cjs} +53 -13
  70. package/dist/telemetry-B2iWkY5e.cjs.map +1 -0
  71. package/dist/{telemetry-BF3SMlH6.js → telemetry-BkektVz6.js} +52 -12
  72. package/dist/telemetry-BkektVz6.js.map +1 -0
  73. package/dist/validate-CAUqLaGt.js +26 -0
  74. package/dist/validate-CAUqLaGt.js.map +1 -0
  75. package/dist/validate-J6AEd5zK.cjs +26 -0
  76. package/dist/validate-J6AEd5zK.cjs.map +1 -0
  77. package/package.json +57 -48
  78. package/src/commands/agent/start.ts +27 -8
  79. package/src/commands/agent.ts +3 -1
  80. package/src/commands/generate.ts +39 -8
  81. package/src/commands/init.ts +40 -4
  82. package/src/commands/mcp.ts +28 -4
  83. package/src/commands/validate.ts +11 -4
  84. package/src/constants.ts +5 -80
  85. package/src/index.ts +12 -13
  86. package/src/loggers/clackLogger.ts +98 -88
  87. package/src/loggers/fileSystemLogger.ts +37 -25
  88. package/src/loggers/githubActionsLogger.ts +35 -48
  89. package/src/loggers/plainLogger.ts +33 -45
  90. package/src/loggers/types.ts +6 -0
  91. package/src/loggers/utils.ts +155 -9
  92. package/src/runners/agent/run.ts +113 -0
  93. package/src/runners/agent/utils.ts +98 -0
  94. package/src/runners/generate/run.ts +276 -0
  95. package/src/runners/generate/utils.ts +209 -0
  96. package/src/runners/init/run.ts +211 -0
  97. package/src/{utils/packageManager.ts → runners/init/utils.ts} +10 -0
  98. package/src/runners/mcp/run.ts +55 -0
  99. package/src/runners/validate/run.ts +63 -0
  100. package/src/{utils/telemetry.ts → telemetry.ts} +28 -8
  101. package/bin/kubb.cjs +0 -18
  102. package/dist/agent-5mmp7QzF.js +0 -56
  103. package/dist/agent-5mmp7QzF.js.map +0 -1
  104. package/dist/agent-BKphjOIF.cjs +0 -58
  105. package/dist/agent-BKphjOIF.cjs.map +0 -1
  106. package/dist/agent-BapvKB4r.cjs +0 -92
  107. package/dist/agent-BapvKB4r.cjs.map +0 -1
  108. package/dist/agent-CBrpIMMU.js +0 -88
  109. package/dist/agent-CBrpIMMU.js.map +0 -1
  110. package/dist/constants-D0XHAHeZ.cjs +0 -178
  111. package/dist/constants-D0XHAHeZ.cjs.map +0 -1
  112. package/dist/constants-DJM9zCXm.js +0 -131
  113. package/dist/constants-DJM9zCXm.js.map +0 -1
  114. package/dist/define--M_JMcDC.js +0 -25
  115. package/dist/define--M_JMcDC.js.map +0 -1
  116. package/dist/define-D6Kfm7-Z.cjs +0 -36
  117. package/dist/define-D6Kfm7-Z.cjs.map +0 -1
  118. package/dist/errors-6mF_WKxg.js +0 -27
  119. package/dist/errors-6mF_WKxg.js.map +0 -1
  120. package/dist/errors-DBW0N9w4.cjs.map +0 -1
  121. package/dist/generate-BHNyeQXl.js.map +0 -1
  122. package/dist/generate-Cq5RDTBL.cjs.map +0 -1
  123. package/dist/generate-DU5zzc54.cjs.map +0 -1
  124. package/dist/generate-Rly1EXBN.js.map +0 -1
  125. package/dist/init-BK6inBTR.cjs +0 -306
  126. package/dist/init-BK6inBTR.cjs.map +0 -1
  127. package/dist/init-BQ6zfsnw.js +0 -302
  128. package/dist/init-BQ6zfsnw.js.map +0 -1
  129. package/dist/init-CN1JFyGX.cjs +0 -25
  130. package/dist/init-CN1JFyGX.cjs.map +0 -1
  131. package/dist/init-iN7e1XwI.js +0 -25
  132. package/dist/init-iN7e1XwI.js.map +0 -1
  133. package/dist/jiti-Cd3S0xwr.cjs +0 -16
  134. package/dist/jiti-Cd3S0xwr.cjs.map +0 -1
  135. package/dist/jiti-e08mD2Ph.js +0 -11
  136. package/dist/jiti-e08mD2Ph.js.map +0 -1
  137. package/dist/mcp-BiGUvbWP.js +0 -41
  138. package/dist/mcp-BiGUvbWP.js.map +0 -1
  139. package/dist/mcp-CONmm_xT.cjs +0 -42
  140. package/dist/mcp-CONmm_xT.cjs.map +0 -1
  141. package/dist/mcp-T7Q4nWbT.cjs +0 -16
  142. package/dist/mcp-T7Q4nWbT.cjs.map +0 -1
  143. package/dist/mcp-eP1S40LZ.js +0 -16
  144. package/dist/mcp-eP1S40LZ.js.map +0 -1
  145. package/dist/package-BJ6ionm6.cjs.map +0 -1
  146. package/dist/package-BKZ0H3Zf.js +0 -6
  147. package/dist/package-BKZ0H3Zf.js.map +0 -1
  148. package/dist/shell-7HPrTCJ5.cjs.map +0 -1
  149. package/dist/shell-DqqWsHCD.js.map +0 -1
  150. package/dist/telemetry-BF3SMlH6.js.map +0 -1
  151. package/dist/telemetry-DZ7IrLAU.cjs.map +0 -1
  152. package/dist/validate-BImbbx1t.js +0 -41
  153. package/dist/validate-BImbbx1t.js.map +0 -1
  154. package/dist/validate-DAZdX_0i.js +0 -25
  155. package/dist/validate-DAZdX_0i.js.map +0 -1
  156. package/dist/validate-DucFMytl.cjs +0 -25
  157. package/dist/validate-DucFMytl.cjs.map +0 -1
  158. package/dist/validate-ujLCYSWU.cjs +0 -42
  159. package/dist/validate-ujLCYSWU.cjs.map +0 -1
  160. package/src/runners/agent.ts +0 -102
  161. package/src/runners/generate.ts +0 -343
  162. package/src/runners/init.ts +0 -323
  163. package/src/runners/mcp.ts +0 -32
  164. package/src/runners/validate.ts +0 -35
  165. package/src/types.ts +0 -11
  166. package/src/utils/Writables.ts +0 -17
  167. package/src/utils/executeHooks.ts +0 -45
  168. package/src/utils/flags.ts +0 -10
  169. package/src/utils/getCosmiConfig.ts +0 -71
  170. package/src/utils/getSummary.ts +0 -68
  171. package/src/utils/jiti.ts +0 -9
  172. package/src/utils/runHook.ts +0 -75
  173. package/src/utils/watcher.ts +0 -19
@@ -1,33 +1,41 @@
1
1
  const require_chunk = require("./chunk-ByKO4r7w.cjs");
2
- const require_errors = require("./errors-DBW0N9w4.cjs");
3
- const require_telemetry = require("./telemetry-DZ7IrLAU.cjs");
4
- const require_shell = require("./shell-7HPrTCJ5.cjs");
5
- const require_package = require("./package-BJ6ionm6.cjs");
6
- const require_constants = require("./constants-D0XHAHeZ.cjs");
2
+ const require_errors = require("./errors-CLCjoSg0.cjs");
3
+ const require_telemetry = require("./telemetry-B2iWkY5e.cjs");
4
+ const require_shell = require("./shell-475fQKaX.cjs");
5
+ const require_package = require("./package-DZ-6zAIO.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");
10
- require("node:fs");
10
+ let node_child_process = require("node:child_process");
11
+ let node_fs = require("node:fs");
11
12
  let node_fs_promises = require("node:fs/promises");
12
13
  let node_path = require("node:path");
13
- node_path = require_chunk.__toESM(node_path);
14
+ node_path = require_chunk.__toESM(node_path, 1);
14
15
  let node_process = require("node:process");
15
- node_process = require_chunk.__toESM(node_process);
16
+ node_process = require_chunk.__toESM(node_process, 1);
16
17
  let _clack_prompts = require("@clack/prompts");
17
- _clack_prompts = require_chunk.__toESM(_clack_prompts);
18
+ _clack_prompts = require_chunk.__toESM(_clack_prompts, 1);
18
19
  let _kubb_core = require("@kubb/core");
19
- let tinyexec = require("tinyexec");
20
20
  let node_stream = require("node:stream");
21
21
  let cosmiconfig = require("cosmiconfig");
22
22
  let jiti = require("jiti");
23
+ let tinyexec = require("tinyexec");
23
24
  //#region ../../internals/utils/src/asyncEventEmitter.ts
24
25
  /**
25
- * A typed EventEmitter that awaits all async listeners before resolving.
26
+ * Typed `EventEmitter` that awaits all async listeners before resolving.
26
27
  * Wraps Node's `EventEmitter` with full TypeScript event-map inference.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
32
+ * emitter.on('build', async (name) => { console.log(name) })
33
+ * await emitter.emit('build', 'petstore') // all listeners awaited
34
+ * ```
27
35
  */
28
36
  var AsyncEventEmitter = class {
29
37
  /**
30
- * `maxListener` controls the maximum number of listeners per event before Node emits a memory-leak warning.
38
+ * Maximum number of listeners per event before Node emits a memory-leak warning.
31
39
  * @default 10
32
40
  */
33
41
  constructor(maxListener = 10) {
@@ -35,31 +43,48 @@ var AsyncEventEmitter = class {
35
43
  }
36
44
  #emitter = new node_events.EventEmitter();
37
45
  /**
38
- * Emits an event and awaits all registered listeners in parallel.
46
+ * Emits `eventName` and awaits all registered listeners sequentially.
39
47
  * Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * await emitter.emit('build', 'petstore')
52
+ * ```
40
53
  */
41
54
  async emit(eventName, ...eventArgs) {
42
55
  const listeners = this.#emitter.listeners(eventName);
43
56
  if (listeners.length === 0) return;
44
- await Promise.all(listeners.map(async (listener) => {
57
+ for (const listener of listeners) try {
58
+ await listener(...eventArgs);
59
+ } catch (err) {
60
+ let serializedArgs;
45
61
  try {
46
- return await listener(...eventArgs);
47
- } catch (err) {
48
- let serializedArgs;
49
- try {
50
- serializedArgs = JSON.stringify(eventArgs);
51
- } catch {
52
- serializedArgs = String(eventArgs);
53
- }
54
- throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: require_errors.toError(err) });
62
+ serializedArgs = JSON.stringify(eventArgs);
63
+ } catch {
64
+ serializedArgs = String(eventArgs);
55
65
  }
56
- }));
66
+ throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: require_errors.toError(err) });
67
+ }
57
68
  }
58
- /** Registers a persistent listener for the given event. */
69
+ /**
70
+ * Registers a persistent listener for `eventName`.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * emitter.on('build', async (name) => { console.log(name) })
75
+ * ```
76
+ */
59
77
  on(eventName, handler) {
60
78
  this.#emitter.on(eventName, handler);
61
79
  }
62
- /** Registers a one-shot listener that removes itself after the first invocation. */
80
+ /**
81
+ * Registers a one-shot listener that removes itself after the first invocation.
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * emitter.onOnce('build', async (name) => { console.log(name) })
86
+ * ```
87
+ */
63
88
  onOnce(eventName, handler) {
64
89
  const wrapper = (...args) => {
65
90
  this.off(eventName, wrapper);
@@ -67,11 +92,37 @@ var AsyncEventEmitter = class {
67
92
  };
68
93
  this.on(eventName, wrapper);
69
94
  }
70
- /** Removes a previously registered listener. */
95
+ /**
96
+ * Removes a previously registered listener.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * emitter.off('build', handler)
101
+ * ```
102
+ */
71
103
  off(eventName, handler) {
72
104
  this.#emitter.off(eventName, handler);
73
105
  }
74
- /** Removes all listeners from every event channel. */
106
+ /**
107
+ * Returns the number of listeners registered for `eventName`.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * emitter.on('build', handler)
112
+ * emitter.listenerCount('build') // 1
113
+ * ```
114
+ */
115
+ listenerCount(eventName) {
116
+ return this.#emitter.listenerCount(eventName);
117
+ }
118
+ /**
119
+ * Removes all listeners from every event channel.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * emitter.removeAll()
124
+ * ```
125
+ */
75
126
  removeAll() {
76
127
  this.#emitter.removeAllListeners();
77
128
  }
@@ -79,8 +130,15 @@ var AsyncEventEmitter = class {
79
130
  //#endregion
80
131
  //#region ../../internals/utils/src/time.ts
81
132
  /**
82
- * Calculates elapsed time in milliseconds from a high-resolution start time.
83
- * Rounds to 2 decimal places to provide sub-millisecond precision without noise.
133
+ * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
134
+ * Rounds to 2 decimal places for sub-millisecond precision without noise.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * const start = process.hrtime()
139
+ * doWork()
140
+ * getElapsedMs(start) // 42.35
141
+ * ```
84
142
  */
85
143
  function getElapsedMs(hrStart) {
86
144
  const [seconds, nanoseconds] = process.hrtime(hrStart);
@@ -88,8 +146,14 @@ function getElapsedMs(hrStart) {
88
146
  return Math.round(ms * 100) / 100;
89
147
  }
90
148
  /**
91
- * Converts a millisecond duration into a human-readable string.
92
- * Adjusts units (ms, s, m s) based on the magnitude of the duration.
149
+ * Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * formatMs(250) // '250ms'
154
+ * formatMs(1500) // '1.50s'
155
+ * formatMs(90000) // '1m 30.0s'
156
+ * ```
93
157
  */
94
158
  function formatMs(ms) {
95
159
  if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
@@ -97,7 +161,14 @@ function formatMs(ms) {
97
161
  return `${Math.round(ms)}ms`;
98
162
  }
99
163
  /**
100
- * Convenience helper: formats the elapsed time since `hrStart` in one step.
164
+ * Formats the elapsed time since `hrStart` as a human-readable string.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const start = process.hrtime()
169
+ * doWork()
170
+ * formatHrtime(start) // '1.50s'
171
+ * ```
101
172
  */
102
173
  function formatHrtime(hrStart) {
103
174
  return formatMs(getElapsedMs(hrStart));
@@ -139,18 +210,46 @@ function gradient(colorStops, text) {
139
210
  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`;
140
211
  }).join("");
141
212
  }
142
- /** ANSI color functions for each part of the Kubb mascot illustration. */
213
+ /**
214
+ * ANSI color functions for each part of the Kubb mascot illustration.
215
+ */
143
216
  const palette = {
217
+ /**
218
+ * Top cap of the skittle.
219
+ */
144
220
  lid: hex("#F55A17"),
221
+ /**
222
+ * Upper wood body.
223
+ */
145
224
  woodTop: hex("#F5A217"),
225
+ /**
226
+ * Middle wood body.
227
+ */
146
228
  woodMid: hex("#F58517"),
229
+ /**
230
+ * Base wood body.
231
+ */
147
232
  woodBase: hex("#B45309"),
233
+ /**
234
+ * Eye whites.
235
+ */
148
236
  eye: hex("#FFFFFF"),
237
+ /**
238
+ * Highlight accent.
239
+ */
149
240
  highlight: hex("#adadc6"),
241
+ /**
242
+ * Cheek blush.
243
+ */
150
244
  blush: hex("#FDA4AF")
151
245
  };
152
246
  /**
153
- * Generates the Kubb mascot welcome banner.
247
+ * Generates the Kubb mascot welcome banner as an ANSI-colored string.
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * console.log(getIntro({ title: 'kubb.config.ts', description: 'generating…', version: '5.0.0', areEyesOpen: true }))
252
+ * ```
154
253
  */
155
254
  function getIntro({ title, description, version, areEyesOpen }) {
156
255
  const kubbVersion = gradient([
@@ -168,7 +267,9 @@ function getIntro({ title, description, version, areEyesOpen }) {
168
267
  ${palette.woodBase("▀▀▀▀▀▀▀▀▀▀▀▀▀")}
169
268
  `;
170
269
  }
171
- /** ANSI color names available for terminal output. */
270
+ /**
271
+ * ANSI color names used by {@link randomCliColor} for deterministic terminal coloring.
272
+ */
172
273
  const randomColors = [
173
274
  "black",
174
275
  "red",
@@ -181,15 +282,27 @@ const randomColors = [
181
282
  "gray"
182
283
  ];
183
284
  /**
184
- * Returns the text wrapped in a deterministic ANSI color derived from the text's SHA-256 hash.
285
+ * Wraps `text` in a deterministic ANSI color derived from the text's SHA-256 hash.
286
+ *
287
+ * @example
288
+ * ```ts
289
+ * randomCliColor('petstore') // '\x1b[33m' + 'petstore' + '\x1b[39m' (always the same color for 'petstore')
290
+ * ```
185
291
  */
186
292
  function randomCliColor(text) {
187
293
  if (!text) return "";
188
294
  return (0, node_util.styleText)(randomColors[(0, node_crypto.createHash)("sha256").update(text).digest().readUInt32BE(0) % randomColors.length] ?? "white", text);
189
295
  }
190
296
  /**
191
- * Formats a millisecond duration with an ANSI color based on thresholds:
192
- * green 500 ms · yellow 1 000 ms · red > 1 000 ms
297
+ * Formats a millisecond duration with a threshold-based ANSI color.
298
+ * `≤ 500 ms` → green · `≤ 1 000 ms` → yellow · `> 1 000 ms` → red.
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * formatMsWithColor(200) // '\x1b[32m200ms\x1b[39m'
303
+ * formatMsWithColor(800) // '\x1b[33m800ms\x1b[39m'
304
+ * formatMsWithColor(1500) // '\x1b[31m1.50s\x1b[39m'
305
+ * ```
193
306
  */
194
307
  function formatMsWithColor(ms) {
195
308
  const formatted = formatMs(ms);
@@ -198,27 +311,96 @@ function formatMsWithColor(ms) {
198
311
  return (0, node_util.styleText)("red", formatted);
199
312
  }
200
313
  //#endregion
314
+ //#region ../../internals/utils/src/formatters.ts
315
+ /**
316
+ * CLI command descriptors for each supported code formatter.
317
+ *
318
+ * Each entry contains the executable `command`, an `args` factory that maps an
319
+ * output path to the correct argument list, and an `errorMessage` shown when
320
+ * the formatter is not found.
321
+ */
322
+ const formatters = {
323
+ prettier: {
324
+ command: "prettier",
325
+ args: (outputPath) => [
326
+ "--ignore-unknown",
327
+ "--write",
328
+ outputPath
329
+ ],
330
+ errorMessage: "Prettier not found"
331
+ },
332
+ biome: {
333
+ command: "biome",
334
+ args: (outputPath) => [
335
+ "format",
336
+ "--write",
337
+ outputPath
338
+ ],
339
+ errorMessage: "Biome not found"
340
+ },
341
+ oxfmt: {
342
+ command: "oxfmt",
343
+ args: (outputPath) => [outputPath],
344
+ errorMessage: "Oxfmt not found"
345
+ }
346
+ };
347
+ async function isFormatterAvailable(formatter) {
348
+ return new Promise((resolve) => {
349
+ const child = (0, node_child_process.spawn)(formatter, ["--version"], { stdio: "ignore" });
350
+ child.on("close", (code) => resolve(code === 0));
351
+ child.on("error", () => resolve(false));
352
+ });
353
+ }
354
+ /**
355
+ * Detects the first available code formatter on the current system.
356
+ *
357
+ * - Checks in preference order: `oxfmt`, `biome`, `prettier`.
358
+ * - Returns `null` when none are found.
359
+ *
360
+ * @example
361
+ * ```ts
362
+ * const formatter = await detectFormatter()
363
+ * if (formatter) {
364
+ * console.log(`Using ${formatter} for formatting`)
365
+ * }
366
+ * ```
367
+ */
368
+ async function detectFormatter() {
369
+ const formatterNames = new Set([
370
+ "oxfmt",
371
+ "biome",
372
+ "prettier"
373
+ ]);
374
+ for (const formatter of formatterNames) if (await isFormatterAvailable(formatter)) return formatter;
375
+ return null;
376
+ }
377
+ //#endregion
201
378
  //#region ../../internals/utils/src/fs.ts
202
379
  /**
203
380
  * Writes `data` to `path`, trimming leading/trailing whitespace before saving.
204
- * Skips the write and returns `undefined` when the trimmed content is empty or
205
- * identical to what is already on disk.
381
+ * Skips the write when the trimmed content is empty or identical to what is already on disk.
206
382
  * Creates any missing parent directories automatically.
207
- * When `sanity` is `true`, re-reads the file after writing and throws if the
208
- * content does not match.
383
+ * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.
384
+ *
385
+ * @example
386
+ * ```ts
387
+ * await write('./src/Pet.ts', source) // writes and returns trimmed content
388
+ * await write('./src/Pet.ts', source) // null — file unchanged
389
+ * await write('./src/Pet.ts', ' ') // null — empty content skipped
390
+ * ```
209
391
  */
210
392
  async function write(path, data, options = {}) {
211
393
  const trimmed = data.trim();
212
- if (trimmed === "") return void 0;
394
+ if (trimmed === "") return null;
213
395
  const resolved = (0, node_path.resolve)(path);
214
396
  if (typeof Bun !== "undefined") {
215
397
  const file = Bun.file(resolved);
216
- if ((await file.exists() ? await file.text() : null) === trimmed) return void 0;
398
+ if ((await file.exists() ? await file.text() : null) === trimmed) return null;
217
399
  await Bun.write(resolved, trimmed);
218
400
  return trimmed;
219
401
  }
220
402
  try {
221
- if (await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" }) === trimmed) return void 0;
403
+ if (await (0, node_fs_promises.readFile)(resolved, { encoding: "utf-8" }) === trimmed) return null;
222
404
  } catch {}
223
405
  await (0, node_fs_promises.mkdir)((0, node_path.dirname)(resolved), { recursive: true });
224
406
  await (0, node_fs_promises.writeFile)(resolved, trimmed, { encoding: "utf-8" });
@@ -230,102 +412,88 @@ async function write(path, data, options = {}) {
230
412
  return trimmed;
231
413
  }
232
414
  //#endregion
233
- //#region src/utils/getSummary.ts
234
- function getSummary({ failedPlugins, filesCreated, status, hrStart, config, pluginTimings }) {
235
- const duration = formatHrtime(hrStart);
236
- const pluginsCount = config.plugins?.length ?? 0;
237
- const successCount = pluginsCount - failedPlugins.size;
238
- const meta = {
239
- 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`,
240
- pluginsFailed: status === "failed" ? [...failedPlugins].map(({ plugin }) => randomCliColor(plugin.name)).join(", ") : void 0,
241
- filesCreated,
242
- time: (0, node_util.styleText)("green", duration),
243
- output: node_path.default.isAbsolute(config.root) ? node_path.default.resolve(config.root, config.output.path) : config.root
244
- };
245
- const labels = {
246
- plugins: "Plugins:",
247
- failed: "Failed:",
248
- generated: "Generated:",
249
- pluginTimings: "Plugin Timings:",
250
- output: "Output:"
251
- };
252
- const maxLength = Math.max(0, ...[...Object.values(labels), ...pluginTimings ? Array.from(pluginTimings.keys()) : []].map((s) => s.length));
253
- const summaryLines = [];
254
- summaryLines.push(`${labels.plugins.padEnd(maxLength + 2)} ${meta.plugins}`);
255
- if (meta.pluginsFailed) summaryLines.push(`${labels.failed.padEnd(maxLength + 2)} ${meta.pluginsFailed}`);
256
- summaryLines.push(`${labels.generated.padEnd(maxLength + 2)} ${meta.filesCreated} files in ${meta.time}`);
257
- if (pluginTimings && pluginTimings.size > 0) {
258
- const sortedTimings = Array.from(pluginTimings.entries()).sort((a, b) => b[1] - a[1]);
259
- summaryLines.push(`${labels.pluginTimings}`);
260
- sortedTimings.forEach(([name, time]) => {
261
- const timeStr = time >= 1e3 ? `${(time / 1e3).toFixed(2)}s` : `${Math.round(time)}ms`;
262
- const barLength = Math.min(Math.ceil(time / 100), 10);
263
- const bar = (0, node_util.styleText)("dim", "█".repeat(barLength));
264
- summaryLines.push(`${(0, node_util.styleText)("dim", "•")} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`);
265
- });
415
+ //#region ../../internals/utils/src/linters.ts
416
+ /**
417
+ * Collects all files under `dir` recursively using Node's built-in fs APIs.
418
+ *
419
+ * Passing explicit file paths to oxlint (instead of a directory) bypasses
420
+ * oxlint's `.gitignore`-aware directory traversal, which would otherwise skip
421
+ * files that are listed in `.gitignore` (e.g. generated output directories).
422
+ */
423
+ function findLintableFiles(dir) {
424
+ try {
425
+ return (0, node_fs.readdirSync)(dir, {
426
+ withFileTypes: true,
427
+ recursive: true
428
+ }).filter((d) => d.isFile()).map((d) => `${d.parentPath}/${d.name}`);
429
+ } catch {
430
+ return [];
266
431
  }
267
- summaryLines.push(`${labels.output.padEnd(maxLength + 2)} ${meta.output}`);
268
- return summaryLines;
269
432
  }
270
- //#endregion
271
- //#region src/utils/runHook.ts
272
433
  /**
273
- * Execute a hook command, emit debug/hook:end events, and forward output to
274
- * an optional HookOutputSink. All three logger adapters share this function
275
- * so the process-spawning logic lives in exactly one place.
434
+ * CLI command descriptors for each supported linter.
435
+ *
436
+ * Each entry contains the executable `command`, an `args` factory that maps an
437
+ * output path to the correct argument list, and an `errorMessage` shown when
438
+ * the linter is not found.
276
439
  */
277
- async function runHook({ id, command, args, commandWithArgs, context, stream = false, sink }) {
278
- try {
279
- const proc = (0, tinyexec.x)(command, [...args ?? []], {
280
- nodeOptions: { detached: true },
281
- throwOnError: true
282
- });
283
- if (stream && sink?.onLine) for await (const line of proc) sink.onLine(line);
284
- const result = await proc;
285
- await context.emit("debug", {
286
- date: /* @__PURE__ */ new Date(),
287
- logs: [result.stdout.trimEnd()]
288
- });
289
- await context.emit("hook:end", {
290
- command,
291
- args,
292
- id,
293
- success: true,
294
- error: null
295
- });
296
- } catch (err) {
297
- if (!(err instanceof tinyexec.NonZeroExitError)) {
298
- await context.emit("hook:end", {
299
- command,
300
- args,
301
- id,
302
- success: false,
303
- error: require_errors.toError(err)
304
- });
305
- await context.emit("error", require_errors.toError(err));
306
- return;
307
- }
308
- const stderr = err.output?.stderr ?? "";
309
- const stdout = err.output?.stdout ?? "";
310
- await context.emit("debug", {
311
- date: /* @__PURE__ */ new Date(),
312
- logs: [stdout, stderr].filter(Boolean)
313
- });
314
- if (stderr) sink?.onStderr?.(stderr);
315
- if (stdout) sink?.onStdout?.(stdout);
316
- const errorMessage = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
317
- await context.emit("hook:end", {
318
- command,
319
- args,
320
- id,
321
- success: false,
322
- error: errorMessage
323
- });
324
- await context.emit("error", errorMessage);
440
+ const linters = {
441
+ eslint: {
442
+ command: "eslint",
443
+ args: (outputPath) => [outputPath, "--fix"],
444
+ errorMessage: "Eslint not found"
445
+ },
446
+ biome: {
447
+ command: "biome",
448
+ args: (outputPath) => [
449
+ "lint",
450
+ "--fix",
451
+ outputPath
452
+ ],
453
+ errorMessage: "Biome not found"
454
+ },
455
+ oxlint: {
456
+ command: "oxlint",
457
+ args: (outputPath) => ["--fix", ...findLintableFiles(outputPath)],
458
+ errorMessage: "Oxlint not found"
325
459
  }
460
+ };
461
+ async function isLinterAvailable(linter) {
462
+ return new Promise((resolve) => {
463
+ const child = (0, node_child_process.spawn)(linter, ["--version"], { stdio: "ignore" });
464
+ child.on("close", (code) => resolve(code === 0));
465
+ child.on("error", () => resolve(false));
466
+ });
467
+ }
468
+ /**
469
+ * Detects the first available linter on the current system.
470
+ *
471
+ * - Checks in preference order: `oxlint`, `biome`, `eslint`.
472
+ * - Returns `null` when none are found.
473
+ *
474
+ * @example
475
+ * ```ts
476
+ * const linter = await detectLinter()
477
+ * if (linter) {
478
+ * console.log(`Using ${linter} for linting`)
479
+ * }
480
+ * ```
481
+ */
482
+ async function detectLinter() {
483
+ const linterNames = new Set([
484
+ "oxlint",
485
+ "biome",
486
+ "eslint"
487
+ ]);
488
+ for (const linter of linterNames) if (await isLinterAvailable(linter)) return linter;
489
+ return null;
326
490
  }
327
491
  //#endregion
328
- //#region src/utils/Writables.ts
492
+ //#region src/loggers/clackLogger.ts
493
+ /**
494
+ * Node.js `Writable` stream that forwards each chunk to a clack `taskLog` message.
495
+ * Used to pipe hook subprocess output into the clack task log UI.
496
+ */
329
497
  var ClackWritable = class extends node_stream.Writable {
330
498
  taskLog;
331
499
  constructor(taskLog, opts) {
@@ -337,11 +505,8 @@ var ClackWritable = class extends node_stream.Writable {
337
505
  callback();
338
506
  }
339
507
  };
340
- //#endregion
341
- //#region src/loggers/clackLogger.ts
342
508
  /**
343
- * Clack adapter for local TTY environments
344
- * Provides a beautiful CLI UI with flat structure inspired by Claude's CLI patterns
509
+ * TTY logger with beautiful UI and progress indicators for local development.
345
510
  */
346
511
  const clackLogger = (0, _kubb_core.defineLogger)({
347
512
  name: "clack",
@@ -389,7 +554,7 @@ const clackLogger = (0, _kubb_core.defineLogger)({
389
554
  state.spinner.stop(text);
390
555
  state.isSpinning = false;
391
556
  }
392
- context.on("info", (message, info = "") => {
557
+ context.on("kubb:info", ({ message, info = "" }) => {
393
558
  if (logLevel <= _kubb_core.logLevel.silent) return;
394
559
  const text = getMessage([
395
560
  (0, node_util.styleText)("blue", "ℹ"),
@@ -399,7 +564,7 @@ const clackLogger = (0, _kubb_core.defineLogger)({
399
564
  if (state.isSpinning) state.spinner.message(text);
400
565
  else _clack_prompts.log.info(text);
401
566
  });
402
- context.on("success", (message, info = "") => {
567
+ context.on("kubb:success", ({ message, info = "" }) => {
403
568
  if (logLevel <= _kubb_core.logLevel.silent) return;
404
569
  const text = getMessage([
405
570
  (0, node_util.styleText)("blue", "✓"),
@@ -409,7 +574,7 @@ const clackLogger = (0, _kubb_core.defineLogger)({
409
574
  if (state.isSpinning) stopSpinner(text);
410
575
  else _clack_prompts.log.success(text);
411
576
  });
412
- context.on("warn", (message, info) => {
577
+ context.on("kubb:warn", ({ message, info }) => {
413
578
  if (logLevel < _kubb_core.logLevel.warn) return;
414
579
  const text = getMessage([
415
580
  (0, node_util.styleText)("yellow", "⚠"),
@@ -418,7 +583,7 @@ const clackLogger = (0, _kubb_core.defineLogger)({
418
583
  ].filter(Boolean).join(" "));
419
584
  _clack_prompts.log.warn(text);
420
585
  });
421
- context.on("error", (error) => {
586
+ context.on("kubb:error", ({ error }) => {
422
587
  const caused = require_errors.toCause(error);
423
588
  const text = [(0, node_util.styleText)("red", "✗"), error.message].join(" ");
424
589
  if (state.isSpinning) stopSpinner(getMessage(text));
@@ -433,19 +598,24 @@ const clackLogger = (0, _kubb_core.defineLogger)({
433
598
  }
434
599
  }
435
600
  });
436
- context.on("version:new", (version, latestVersion) => {
601
+ context.on("kubb:version:new", ({ currentVersion, latestVersion }) => {
437
602
  if (logLevel <= _kubb_core.logLevel.silent) return;
438
- _clack_prompts.box(`\`v${version}\` → \`v${latestVersion}\`
603
+ try {
604
+ _clack_prompts.box(`\`v${currentVersion}\` → \`v${latestVersion}\`
439
605
  Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
440
- width: "auto",
441
- formatBorder: (s) => (0, node_util.styleText)("yellow", s),
442
- rounded: true,
443
- withGuide: false,
444
- contentAlign: "center",
445
- titleAlign: "center"
446
- });
606
+ width: "auto",
607
+ formatBorder: (s) => (0, node_util.styleText)("yellow", s),
608
+ rounded: true,
609
+ withGuide: false,
610
+ contentAlign: "center",
611
+ titleAlign: "center"
612
+ });
613
+ } catch {
614
+ console.log(`Update available for Kubb: v${currentVersion} → v${latestVersion}`);
615
+ console.log("Run `npm install -g @kubb/cli` to update");
616
+ }
447
617
  });
448
- context.on("lifecycle:start", async (version) => {
618
+ context.on("kubb:lifecycle:start", async ({ version }) => {
449
619
  console.log(`\n${getIntro({
450
620
  title: "The ultimate toolkit for working with APIs",
451
621
  description: "Ready to start",
@@ -454,24 +624,24 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
454
624
  })}\n`);
455
625
  reset();
456
626
  });
457
- context.on("config:start", () => {
627
+ context.on("kubb:config:start", () => {
458
628
  if (logLevel <= _kubb_core.logLevel.silent) return;
459
629
  const text = getMessage("Configuration started");
460
630
  _clack_prompts.intro(text);
461
631
  startSpinner(getMessage("Configuration loading"));
462
632
  });
463
- context.on("config:end", (_configs) => {
633
+ context.on("kubb:config:end", () => {
464
634
  if (logLevel <= _kubb_core.logLevel.silent) return;
465
635
  const text = getMessage("Configuration completed");
466
636
  _clack_prompts.outro(text);
467
637
  });
468
- context.on("generation:start", (config) => {
638
+ context.on("kubb:generation:start", ({ config }) => {
469
639
  reset();
470
640
  state.totalPlugins = config.plugins?.length ?? 0;
471
641
  const text = getMessage(["Generation started", config.name ? `for ${(0, node_util.styleText)("dim", config.name)}` : void 0].filter(Boolean).join(" "));
472
642
  _clack_prompts.intro(text);
473
643
  });
474
- context.on("plugin:start", (plugin) => {
644
+ context.on("kubb:plugin:start", ({ plugin }) => {
475
645
  if (logLevel <= _kubb_core.logLevel.silent) return;
476
646
  stopSpinner();
477
647
  const progressBar = _clack_prompts.progress({
@@ -489,7 +659,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
489
659
  interval
490
660
  });
491
661
  });
492
- context.on("plugin:end", (plugin, { duration, success }) => {
662
+ context.on("kubb:plugin:end", ({ plugin, duration, success }) => {
493
663
  stopSpinner();
494
664
  const active = state.activeProgress.get(plugin.name);
495
665
  if (!active || logLevel === _kubb_core.logLevel.silent) return;
@@ -502,7 +672,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
502
672
  state.activeProgress.delete(plugin.name);
503
673
  showProgressStep();
504
674
  });
505
- context.on("files:processing:start", (files) => {
675
+ context.on("kubb:files:processing:start", ({ files }) => {
506
676
  if (logLevel <= _kubb_core.logLevel.silent) return;
507
677
  stopSpinner();
508
678
  state.totalFiles = files.length;
@@ -513,11 +683,11 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
513
683
  max: files.length,
514
684
  size: 30
515
685
  });
516
- context.emit("info", text);
686
+ context.emit("kubb:info", { message: text });
517
687
  progressBar.start(getMessage(text));
518
688
  state.activeProgress.set("files", { progressBar });
519
689
  });
520
- context.on("file:processing:update", ({ file, config }) => {
690
+ context.on("kubb:file:processing:update", ({ file, config }) => {
521
691
  if (logLevel <= _kubb_core.logLevel.silent) return;
522
692
  stopSpinner();
523
693
  state.processedFiles++;
@@ -526,7 +696,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
526
696
  if (!active) return;
527
697
  active.progressBar.advance(void 0, text);
528
698
  });
529
- context.on("files:processing:end", () => {
699
+ context.on("kubb:files:processing:end", () => {
530
700
  if (logLevel <= _kubb_core.logLevel.silent) return;
531
701
  stopSpinner();
532
702
  const text = getMessage("Files written successfully");
@@ -536,71 +706,41 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
536
706
  state.activeProgress.delete("files");
537
707
  showProgressStep();
538
708
  });
539
- context.on("generation:end", (config) => {
709
+ context.on("kubb:generation:end", ({ config }) => {
540
710
  const text = getMessage(config.name ? `Generation completed for ${(0, node_util.styleText)("dim", config.name)}` : "Generation completed");
541
711
  _clack_prompts.outro(text);
542
712
  });
543
- context.on("format:start", () => {
713
+ context.on("kubb:format:start", () => {
544
714
  if (logLevel <= _kubb_core.logLevel.silent) return;
545
715
  const text = getMessage("Format started");
546
716
  _clack_prompts.intro(text);
547
717
  });
548
- context.on("format:end", () => {
718
+ context.on("kubb:format:end", () => {
549
719
  if (logLevel <= _kubb_core.logLevel.silent) return;
550
720
  const text = getMessage("Format completed");
551
721
  _clack_prompts.outro(text);
552
722
  });
553
- context.on("lint:start", () => {
723
+ context.on("kubb:lint:start", () => {
554
724
  if (logLevel <= _kubb_core.logLevel.silent) return;
555
725
  const text = getMessage("Lint started");
556
726
  _clack_prompts.intro(text);
557
727
  });
558
- context.on("lint:end", () => {
728
+ context.on("kubb:lint:end", () => {
559
729
  if (logLevel <= _kubb_core.logLevel.silent) return;
560
730
  const text = getMessage("Lint completed");
561
731
  _clack_prompts.outro(text);
562
732
  });
563
- context.on("hook:start", async ({ id, command, args }) => {
564
- const commandWithArgs = formatCommandWithArgs(command, args);
565
- const text = getMessage(`Hook ${(0, node_util.styleText)("dim", commandWithArgs)} started`);
566
- if (!id) return;
567
- if (logLevel <= _kubb_core.logLevel.silent) {
568
- await runHook({
569
- id,
570
- command,
571
- args,
572
- commandWithArgs,
573
- context,
574
- sink: {
575
- onStderr: (s) => console.error(s),
576
- onStdout: (s) => console.log(s)
577
- }
578
- });
579
- return;
580
- }
733
+ context.on("kubb:hook:start", ({ command, args }) => {
734
+ if (logLevel <= _kubb_core.logLevel.silent) return;
735
+ const text = getMessage(`Hook ${(0, node_util.styleText)("dim", formatCommandWithArgs(command, args))} started`);
581
736
  _clack_prompts.intro(text);
582
- 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(" ")) });
583
- const writable = new ClackWritable(logger);
584
- await runHook({
585
- id,
586
- command,
587
- args,
588
- commandWithArgs,
589
- context,
590
- stream: true,
591
- sink: {
592
- onLine: (line) => writable.write(line),
593
- onStderr: (s) => logger.error(s),
594
- onStdout: (s) => logger.message(s)
595
- }
596
- });
597
737
  });
598
- context.on("hook:end", ({ command, args }) => {
738
+ context.on("kubb:hook:end", ({ command, args }) => {
599
739
  if (logLevel <= _kubb_core.logLevel.silent) return;
600
740
  const text = getMessage(`Hook ${(0, node_util.styleText)("dim", formatCommandWithArgs(command, args))} successfully executed`);
601
741
  _clack_prompts.outro(text);
602
742
  });
603
- context.on("generation:summary", (config, { pluginTimings, failedPlugins, filesCreated, status, hrStart }) => {
743
+ context.on("kubb:generation:summary", ({ config, pluginTimings, failedPlugins, filesCreated, status, hrStart }) => {
604
744
  const summary = getSummary({
605
745
  failedPlugins,
606
746
  filesCreated,
@@ -613,28 +753,44 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
613
753
  summary.unshift("\n");
614
754
  summary.push("\n");
615
755
  const borderColor = status === "success" ? "green" : "red";
616
- _clack_prompts.box(summary.join("\n"), getMessage(title), {
617
- width: "auto",
618
- formatBorder: (s) => (0, node_util.styleText)(borderColor, s),
619
- rounded: true,
620
- withGuide: false,
621
- contentAlign: "left",
622
- titleAlign: "center"
623
- });
756
+ try {
757
+ _clack_prompts.box(summary.join("\n"), getMessage(title), {
758
+ width: "auto",
759
+ formatBorder: (s) => (0, node_util.styleText)(borderColor, s),
760
+ rounded: true,
761
+ withGuide: false,
762
+ contentAlign: "left",
763
+ titleAlign: "center"
764
+ });
765
+ } catch {
766
+ console.log(summary.join("\n"));
767
+ }
624
768
  });
625
- context.on("lifecycle:end", () => {
769
+ context.on("kubb:lifecycle:end", () => {
626
770
  reset();
627
771
  });
772
+ return (commandWithArgs) => {
773
+ if (logLevel <= _kubb_core.logLevel.silent) return {
774
+ onStdout: (s) => console.log(s),
775
+ onStderr: (s) => console.error(s)
776
+ };
777
+ 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(" ")) });
778
+ const writable = new ClackWritable(logger);
779
+ return {
780
+ stream: true,
781
+ onLine: (line) => writable.write(line),
782
+ onStdout: (s) => logger.message(s),
783
+ onStderr: (s) => logger.error(s)
784
+ };
785
+ };
628
786
  }
629
787
  });
630
788
  //#endregion
631
789
  //#region src/loggers/fileSystemLogger.ts
632
790
  /**
633
- * FileSystem logger for debug log persistence
634
- * Captures debug and verbose events and writes them to files in .kubb directory
791
+ * FileSystem logger that captures debug events and writes them to `.kubb` directory files.
635
792
  *
636
- * Note: Logs are written on lifecycle:end or process exit. If the process crashes
637
- * before these events, some cached logs may be lost.
793
+ * @note Logs are written on `kubb:lifecycle:end` or process exit. Cached logs may be lost if the process crashes before either event.
638
794
  */
639
795
  const fileSystemLogger = (0, _kubb_core.defineLogger)({
640
796
  name: "filesystem",
@@ -659,67 +815,73 @@ const fileSystemLogger = (0, _kubb_core.defineLogger)({
659
815
  const pathName = (0, node_path.resolve)(node_process.default.cwd(), ".kubb", baseName);
660
816
  if (!files[pathName]) files[pathName] = [];
661
817
  if (log.logs.length > 0) {
662
- const timestamp = log.date.toLocaleString();
663
- files[pathName].push(`[${timestamp}]\n${log.logs.join("\n")}`);
818
+ const prefix = `[${log.date.toLocaleString()}] `;
819
+ const indent = " ".repeat(prefix.length);
820
+ const [first, ...rest] = log.logs;
821
+ files[pathName].push([prefix + first, ...rest.map((line) => indent + line)].join("\n"));
664
822
  }
665
823
  }
666
- await Promise.all(Object.entries(files).map(([fileName, logs]) => write(fileName, logs.join("\n\n"))));
824
+ for (const [fileName, logs] of Object.entries(files)) await write(fileName, logs.join("\n"));
667
825
  return Object.keys(files);
668
826
  }
669
- context.on("info", (message, info) => {
827
+ context.on("kubb:info", ({ message, info }) => {
670
828
  state.cachedLogs.add({
671
829
  date: /* @__PURE__ */ new Date(),
672
- logs: [`ℹ ${message} ${info}`]
830
+ logs: [`ℹ ${[message, info].filter(Boolean).join(" ")}`]
673
831
  });
674
832
  });
675
- context.on("success", (message, info) => {
833
+ context.on("kubb:success", ({ message, info }) => {
676
834
  state.cachedLogs.add({
677
835
  date: /* @__PURE__ */ new Date(),
678
- logs: [`✓ ${message} ${info}`]
836
+ logs: [`✓ ${[message, info].filter(Boolean).join(" ")}`]
679
837
  });
680
838
  });
681
- context.on("warn", (message, info) => {
839
+ context.on("kubb:warn", ({ message, info }) => {
682
840
  state.cachedLogs.add({
683
841
  date: /* @__PURE__ */ new Date(),
684
- logs: [`⚠ ${message} ${info}`]
842
+ logs: [`⚠ ${[message, info].filter(Boolean).join(" ")}`]
685
843
  });
686
844
  });
687
- context.on("error", (error) => {
845
+ context.on("kubb:error", ({ error }) => {
688
846
  state.cachedLogs.add({
689
847
  date: /* @__PURE__ */ new Date(),
690
848
  logs: [`✗ ${error.message}`, error.stack || "unknown stack"]
691
849
  });
692
850
  });
693
- context.on("debug", (message) => {
851
+ context.on("kubb:debug", ({ date, fileName, logs }) => {
694
852
  state.cachedLogs.add({
695
- date: /* @__PURE__ */ new Date(),
696
- logs: message.logs
853
+ date,
854
+ fileName,
855
+ logs
697
856
  });
698
857
  });
699
- context.on("plugin:start", (plugin) => {
858
+ context.on("kubb:plugin:start", ({ plugin }) => {
700
859
  state.cachedLogs.add({
701
860
  date: /* @__PURE__ */ new Date(),
702
- logs: [`Generating ${plugin.name}`]
861
+ logs: [`► Generating ${plugin.name}`]
703
862
  });
704
863
  });
705
- context.on("plugin:end", (plugin, { duration, success }) => {
864
+ context.on("kubb:plugin:end", ({ plugin, duration, success }) => {
706
865
  const durationStr = formatMs(duration);
707
866
  state.cachedLogs.add({
708
867
  date: /* @__PURE__ */ new Date(),
709
- logs: [success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`]
868
+ logs: [success ? `✓ ${plugin.name} completed in ${durationStr}` : `✗ ${plugin.name} failed in ${durationStr}`]
710
869
  });
711
870
  });
712
- context.on("files:processing:start", (files) => {
871
+ context.on("kubb:files:processing:start", ({ files }) => {
713
872
  state.cachedLogs.add({
714
873
  date: /* @__PURE__ */ new Date(),
715
- logs: [`Start ${files.length} writing:`, ...files.map((file) => file.path)]
874
+ logs: [`► Writing ${files.length} files`, ...files.map((file) => ` ${file.path}`)]
716
875
  });
717
876
  });
718
- context.on("generation:end", async (config) => {
877
+ context.on("kubb:generation:end", async ({ config }) => {
719
878
  const writtenFilePaths = await writeLogs(config.name);
720
879
  if (writtenFilePaths.length > 0) {
721
880
  const files = writtenFilePaths.map((f) => (0, node_path.relative)(node_process.default.cwd(), f));
722
- await context.emit("info", "Debug files written to:", files.join(", "));
881
+ await context.emit("kubb:info", {
882
+ message: "Debug files written to:",
883
+ info: files.join(", ")
884
+ });
723
885
  }
724
886
  reset();
725
887
  });
@@ -734,8 +896,7 @@ const fileSystemLogger = (0, _kubb_core.defineLogger)({
734
896
  //#endregion
735
897
  //#region src/loggers/githubActionsLogger.ts
736
898
  /**
737
- * GitHub Actions adapter for CI environments
738
- * Uses Github group annotations for collapsible sections
899
+ * GitHub Actions logger using group annotations for collapsible sections in CI.
739
900
  */
740
901
  const githubActionsLogger = (0, _kubb_core.defineLogger)({
741
902
  name: "github-actions",
@@ -773,7 +934,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
773
934
  function closeGroup(_name) {
774
935
  console.log("::endgroup::");
775
936
  }
776
- context.on("info", (message, info = "") => {
937
+ context.on("kubb:info", ({ message, info = "" }) => {
777
938
  if (logLevel <= _kubb_core.logLevel.silent) return;
778
939
  const text = getMessage([
779
940
  (0, node_util.styleText)("blue", "ℹ"),
@@ -782,7 +943,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
782
943
  ].join(" "));
783
944
  console.log(text);
784
945
  });
785
- context.on("success", (message, info = "") => {
946
+ context.on("kubb:success", ({ message, info = "" }) => {
786
947
  if (logLevel <= _kubb_core.logLevel.silent) return;
787
948
  const text = getMessage([
788
949
  (0, node_util.styleText)("blue", "✓"),
@@ -791,7 +952,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
791
952
  ].filter(Boolean).join(" "));
792
953
  console.log(text);
793
954
  });
794
- context.on("warn", (message, info = "") => {
955
+ context.on("kubb:warn", ({ message, info = "" }) => {
795
956
  if (logLevel <= _kubb_core.logLevel.silent) return;
796
957
  const text = getMessage([
797
958
  (0, node_util.styleText)("yellow", "⚠"),
@@ -800,7 +961,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
800
961
  ].filter(Boolean).join(" "));
801
962
  console.warn(`::warning::${text}`);
802
963
  });
803
- context.on("error", (error) => {
964
+ context.on("kubb:error", ({ error }) => {
804
965
  const caused = require_errors.toCause(error);
805
966
  if (logLevel <= _kubb_core.logLevel.silent) return;
806
967
  const message = error.message || String(error);
@@ -815,37 +976,37 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
815
976
  }
816
977
  }
817
978
  });
818
- context.on("lifecycle:start", (version) => {
979
+ context.on("kubb:lifecycle:start", ({ version }) => {
819
980
  console.log((0, node_util.styleText)("yellow", `Kubb ${version} 🧩`));
820
981
  reset();
821
982
  });
822
- context.on("config:start", () => {
983
+ context.on("kubb:config:start", () => {
823
984
  if (logLevel <= _kubb_core.logLevel.silent) return;
824
985
  const text = getMessage("Configuration started");
825
986
  openGroup("Configuration");
826
987
  console.log(text);
827
988
  });
828
- context.on("config:end", (configs) => {
989
+ context.on("kubb:config:end", ({ configs }) => {
829
990
  state.currentConfigs = configs;
830
991
  if (logLevel <= _kubb_core.logLevel.silent) return;
831
992
  const text = getMessage("Configuration completed");
832
993
  console.log(text);
833
994
  closeGroup("Configuration");
834
995
  });
835
- context.on("generation:start", (config) => {
996
+ context.on("kubb:generation:start", ({ config }) => {
836
997
  reset();
837
998
  state.totalPlugins = config.plugins?.length ?? 0;
838
999
  const text = config.name ? `Generation for ${(0, node_util.styleText)("bold", config.name)}` : "Generation";
839
1000
  if (state.currentConfigs.length > 1) openGroup(text);
840
1001
  if (state.currentConfigs.length === 1) console.log(getMessage(text));
841
1002
  });
842
- context.on("plugin:start", (plugin) => {
1003
+ context.on("kubb:plugin:start", ({ plugin }) => {
843
1004
  if (logLevel <= _kubb_core.logLevel.silent) return;
844
1005
  const text = getMessage(`Generating ${(0, node_util.styleText)("bold", plugin.name)}`);
845
1006
  if (state.currentConfigs.length === 1) openGroup(`Plugin: ${plugin.name}`);
846
1007
  console.log(text);
847
1008
  });
848
- context.on("plugin:end", (plugin, { duration, success }) => {
1009
+ context.on("kubb:plugin:end", ({ plugin, duration, success }) => {
849
1010
  if (logLevel <= _kubb_core.logLevel.silent) return;
850
1011
  if (success) state.completedPlugins++;
851
1012
  else state.failedPlugins++;
@@ -856,7 +1017,7 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
856
1017
  if (state.currentConfigs.length === 1) closeGroup(`Plugin: ${plugin.name}`);
857
1018
  showProgressStep();
858
1019
  });
859
- context.on("files:processing:start", (files) => {
1020
+ context.on("kubb:files:processing:start", ({ files }) => {
860
1021
  if (logLevel <= _kubb_core.logLevel.silent) return;
861
1022
  state.totalFiles = files.length;
862
1023
  state.processedFiles = 0;
@@ -864,73 +1025,60 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
864
1025
  const text = getMessage(`Writing ${files.length} files`);
865
1026
  console.log(text);
866
1027
  });
867
- context.on("files:processing:end", () => {
1028
+ context.on("kubb:files:processing:end", () => {
868
1029
  if (logLevel <= _kubb_core.logLevel.silent) return;
869
1030
  const text = getMessage("Files written successfully");
870
1031
  console.log(text);
871
1032
  if (state.currentConfigs.length === 1) closeGroup("File Generation");
872
1033
  showProgressStep();
873
1034
  });
874
- context.on("file:processing:update", () => {
1035
+ context.on("kubb:file:processing:update", () => {
875
1036
  if (logLevel <= _kubb_core.logLevel.silent) return;
876
1037
  state.processedFiles++;
877
1038
  });
878
- context.on("generation:end", (config) => {
1039
+ context.on("kubb:generation:end", ({ config }) => {
879
1040
  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`);
880
1041
  console.log(text);
881
1042
  });
882
- context.on("format:start", () => {
1043
+ context.on("kubb:format:start", () => {
883
1044
  if (logLevel <= _kubb_core.logLevel.silent) return;
884
1045
  const text = getMessage("Format started");
885
1046
  if (state.currentConfigs.length === 1) openGroup("Formatting");
886
1047
  console.log(text);
887
1048
  });
888
- context.on("format:end", () => {
1049
+ context.on("kubb:format:end", () => {
889
1050
  if (logLevel <= _kubb_core.logLevel.silent) return;
890
1051
  const text = getMessage("Format completed");
891
1052
  console.log(text);
892
1053
  if (state.currentConfigs.length === 1) closeGroup("Formatting");
893
1054
  });
894
- context.on("lint:start", () => {
1055
+ context.on("kubb:lint:start", () => {
895
1056
  if (logLevel <= _kubb_core.logLevel.silent) return;
896
1057
  const text = getMessage("Lint started");
897
1058
  if (state.currentConfigs.length === 1) openGroup("Linting");
898
1059
  console.log(text);
899
1060
  });
900
- context.on("lint:end", () => {
1061
+ context.on("kubb:lint:end", () => {
901
1062
  if (logLevel <= _kubb_core.logLevel.silent) return;
902
1063
  const text = getMessage("Lint completed");
903
1064
  console.log(text);
904
1065
  if (state.currentConfigs.length === 1) closeGroup("Linting");
905
1066
  });
906
- context.on("hook:start", async ({ id, command, args }) => {
1067
+ context.on("kubb:hook:start", ({ command, args }) => {
1068
+ if (logLevel <= _kubb_core.logLevel.silent) return;
907
1069
  const commandWithArgs = formatCommandWithArgs(command, args);
908
1070
  const text = getMessage(`Hook ${(0, node_util.styleText)("dim", commandWithArgs)} started`);
909
- if (logLevel > _kubb_core.logLevel.silent) {
910
- if (state.currentConfigs.length === 1) openGroup(`Hook ${commandWithArgs}`);
911
- console.log(text);
912
- }
913
- if (!id) return;
914
- await runHook({
915
- id,
916
- command,
917
- args,
918
- commandWithArgs,
919
- context,
920
- sink: {
921
- onStdout: logLevel > _kubb_core.logLevel.silent ? (s) => console.log(s) : void 0,
922
- onStderr: logLevel > _kubb_core.logLevel.silent ? (s) => console.error(`::error::${s}`) : void 0
923
- }
924
- });
1071
+ if (state.currentConfigs.length === 1) openGroup(`Hook ${commandWithArgs}`);
1072
+ console.log(text);
925
1073
  });
926
- context.on("hook:end", ({ command, args }) => {
1074
+ context.on("kubb:hook:end", ({ command, args }) => {
927
1075
  if (logLevel <= _kubb_core.logLevel.silent) return;
928
1076
  const commandWithArgs = formatCommandWithArgs(command, args);
929
1077
  const text = getMessage(`Hook ${(0, node_util.styleText)("dim", commandWithArgs)} completed`);
930
1078
  console.log(text);
931
1079
  if (state.currentConfigs.length === 1) closeGroup(`Hook ${commandWithArgs}`);
932
1080
  });
933
- context.on("generation:summary", (config, { status, hrStart, failedPlugins }) => {
1081
+ context.on("kubb:generation:summary", ({ config, status, hrStart, failedPlugins }) => {
934
1082
  const pluginsCount = config.plugins?.length ?? 0;
935
1083
  const successCount = pluginsCount - failedPlugins.size;
936
1084
  const duration = formatHrtime(hrStart);
@@ -938,16 +1086,19 @@ const githubActionsLogger = (0, _kubb_core.defineLogger)({
938
1086
  console.log(status === "success" ? `Kubb Summary: ${(0, node_util.styleText)("blue", "✓")} ${`${successCount} successful`}, ${pluginsCount} total, ${(0, node_util.styleText)("green", duration)}` : `Kubb Summary: ${(0, node_util.styleText)("blue", "✓")} ${`${successCount} successful`}, ✗ ${`${failedPlugins.size} failed`}, ${pluginsCount} total, ${(0, node_util.styleText)("green", duration)}`);
939
1087
  if (state.currentConfigs.length > 1) closeGroup(config.name ? `Generation for ${(0, node_util.styleText)("bold", config.name)}` : "Generation");
940
1088
  });
941
- context.on("lifecycle:end", () => {
1089
+ context.on("kubb:lifecycle:end", () => {
942
1090
  reset();
943
1091
  });
1092
+ return (_commandWithArgs) => ({
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
+ });
944
1096
  }
945
1097
  });
946
1098
  //#endregion
947
1099
  //#region src/loggers/plainLogger.ts
948
1100
  /**
949
- * Plain console adapter for non-TTY environments
950
- * Simple console.log output with indentation
1101
+ * Plain console adapter for non-TTY environments with simple `console.log` output.
951
1102
  */
952
1103
  const plainLogger = (0, _kubb_core.defineLogger)({
953
1104
  name: "plain",
@@ -956,7 +1107,7 @@ const plainLogger = (0, _kubb_core.defineLogger)({
956
1107
  function getMessage(message) {
957
1108
  return formatMessage(message, logLevel);
958
1109
  }
959
- context.on("info", (message, info) => {
1110
+ context.on("kubb:info", ({ message, info }) => {
960
1111
  if (logLevel <= _kubb_core.logLevel.silent) return;
961
1112
  const text = getMessage([
962
1113
  "ℹ",
@@ -965,7 +1116,7 @@ const plainLogger = (0, _kubb_core.defineLogger)({
965
1116
  ].join(" "));
966
1117
  console.log(text);
967
1118
  });
968
- context.on("success", (message, info = "") => {
1119
+ context.on("kubb:success", ({ message, info = "" }) => {
969
1120
  if (logLevel <= _kubb_core.logLevel.silent) return;
970
1121
  const text = getMessage([
971
1122
  "✓",
@@ -974,7 +1125,7 @@ const plainLogger = (0, _kubb_core.defineLogger)({
974
1125
  ].filter(Boolean).join(" "));
975
1126
  console.log(text);
976
1127
  });
977
- context.on("warn", (message, info) => {
1128
+ context.on("kubb:warn", ({ message, info }) => {
978
1129
  if (logLevel < _kubb_core.logLevel.warn) return;
979
1130
  const text = getMessage([
980
1131
  "⚠",
@@ -983,7 +1134,7 @@ const plainLogger = (0, _kubb_core.defineLogger)({
983
1134
  ].filter(Boolean).join(" "));
984
1135
  console.log(text);
985
1136
  });
986
- context.on("error", (error) => {
1137
+ context.on("kubb:error", ({ error }) => {
987
1138
  const caused = require_errors.toCause(error);
988
1139
  const text = getMessage(["✗", error.message].join(" "));
989
1140
  console.log(text);
@@ -997,96 +1148,84 @@ const plainLogger = (0, _kubb_core.defineLogger)({
997
1148
  }
998
1149
  }
999
1150
  });
1000
- context.on("lifecycle:start", () => {
1151
+ context.on("kubb:lifecycle:start", () => {
1001
1152
  console.log("Kubb CLI 🧩");
1002
1153
  });
1003
- context.on("config:start", () => {
1154
+ context.on("kubb:config:start", () => {
1004
1155
  if (logLevel <= _kubb_core.logLevel.silent) return;
1005
1156
  const text = getMessage("Configuration started");
1006
1157
  console.log(text);
1007
1158
  });
1008
- context.on("config:end", () => {
1159
+ context.on("kubb:config:end", () => {
1009
1160
  if (logLevel <= _kubb_core.logLevel.silent) return;
1010
1161
  const text = getMessage("Configuration completed");
1011
1162
  console.log(text);
1012
1163
  });
1013
- context.on("generation:start", () => {
1164
+ context.on("kubb:generation:start", () => {
1014
1165
  const text = getMessage("Generation started");
1015
1166
  console.log(text);
1016
1167
  });
1017
- context.on("plugin:start", (plugin) => {
1168
+ context.on("kubb:plugin:start", ({ plugin }) => {
1018
1169
  if (logLevel <= _kubb_core.logLevel.silent) return;
1019
1170
  const text = getMessage(`Generating ${plugin.name}`);
1020
1171
  console.log(text);
1021
1172
  });
1022
- context.on("plugin:end", (plugin, { duration, success }) => {
1173
+ context.on("kubb:plugin:end", ({ plugin, duration, success }) => {
1023
1174
  if (logLevel <= _kubb_core.logLevel.silent) return;
1024
1175
  const durationStr = formatMs(duration);
1025
1176
  const text = getMessage(success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`);
1026
1177
  console.log(text);
1027
1178
  });
1028
- context.on("files:processing:start", (files) => {
1179
+ context.on("kubb:files:processing:start", ({ files }) => {
1029
1180
  if (logLevel <= _kubb_core.logLevel.silent) return;
1030
1181
  const text = getMessage(`Writing ${files.length} files`);
1031
1182
  console.log(text);
1032
1183
  });
1033
- context.on("file:processing:update", ({ file, config }) => {
1184
+ context.on("kubb:file:processing:update", ({ file, config }) => {
1034
1185
  if (logLevel <= _kubb_core.logLevel.silent) return;
1035
1186
  const text = getMessage(`Writing ${(0, node_path.relative)(config.root, file.path)}`);
1036
1187
  console.log(text);
1037
1188
  });
1038
- context.on("files:processing:end", () => {
1189
+ context.on("kubb:files:processing:end", () => {
1039
1190
  if (logLevel <= _kubb_core.logLevel.silent) return;
1040
1191
  const text = getMessage("Files written successfully");
1041
1192
  console.log(text);
1042
1193
  });
1043
- context.on("generation:end", (config) => {
1194
+ context.on("kubb:generation:end", ({ config }) => {
1044
1195
  const text = getMessage(config.name ? `Generation completed for ${config.name}` : "Generation completed");
1045
1196
  console.log(text);
1046
1197
  });
1047
- context.on("format:start", () => {
1198
+ context.on("kubb:format:start", () => {
1048
1199
  if (logLevel <= _kubb_core.logLevel.silent) return;
1049
1200
  const text = getMessage("Format started");
1050
1201
  console.log(text);
1051
1202
  });
1052
- context.on("format:end", () => {
1203
+ context.on("kubb:format:end", () => {
1053
1204
  if (logLevel <= _kubb_core.logLevel.silent) return;
1054
1205
  const text = getMessage("Format completed");
1055
1206
  console.log(text);
1056
1207
  });
1057
- context.on("lint:start", () => {
1208
+ context.on("kubb:lint:start", () => {
1058
1209
  if (logLevel <= _kubb_core.logLevel.silent) return;
1059
1210
  const text = getMessage("Lint started");
1060
1211
  console.log(text);
1061
1212
  });
1062
- context.on("lint:end", () => {
1213
+ context.on("kubb:lint:end", () => {
1063
1214
  if (logLevel <= _kubb_core.logLevel.silent) return;
1064
1215
  const text = getMessage("Lint completed");
1065
1216
  console.log(text);
1066
1217
  });
1067
- context.on("hook:start", async ({ id, command, args }) => {
1068
- const commandWithArgs = formatCommandWithArgs(command, args);
1069
- const text = getMessage(`Hook ${commandWithArgs} started`);
1070
- if (logLevel > _kubb_core.logLevel.silent) console.log(text);
1071
- if (!id) return;
1072
- await runHook({
1073
- id,
1074
- command,
1075
- args,
1076
- commandWithArgs,
1077
- context,
1078
- sink: {
1079
- onStdout: logLevel > _kubb_core.logLevel.silent ? (s) => console.log(s) : void 0,
1080
- onStderr: logLevel > _kubb_core.logLevel.silent ? (s) => console.error(s) : void 0
1081
- }
1082
- });
1218
+ context.on("kubb:hook:start", ({ command, args }) => {
1219
+ if (logLevel <= _kubb_core.logLevel.silent) return;
1220
+ const text = getMessage(`Hook ${formatCommandWithArgs(command, args)} started`);
1221
+ console.log(text);
1083
1222
  });
1084
- context.on("hook:end", ({ command, args }) => {
1223
+ context.on("kubb:hook:end", ({ command, args }) => {
1085
1224
  if (logLevel <= _kubb_core.logLevel.silent) return;
1086
1225
  const text = getMessage(`Hook ${formatCommandWithArgs(command, args)} completed`);
1087
1226
  console.log(text);
1088
1227
  });
1089
- context.on("generation:summary", (config, { pluginTimings, status, hrStart, failedPlugins, filesCreated }) => {
1228
+ context.on("kubb:generation:summary", ({ config, pluginTimings, status, hrStart, failedPlugins, filesCreated }) => {
1090
1229
  const summary = getSummary({
1091
1230
  failedPlugins,
1092
1231
  filesCreated,
@@ -1099,6 +1238,10 @@ const plainLogger = (0, _kubb_core.defineLogger)({
1099
1238
  console.log(summary.join("\n"));
1100
1239
  console.log(require_constants.SUMMARY_SEPARATOR);
1101
1240
  });
1241
+ return (_commandWithArgs) => ({
1242
+ onStdout: logLevel > _kubb_core.logLevel.silent ? (s) => console.log(s) : void 0,
1243
+ onStderr: logLevel > _kubb_core.logLevel.silent ? (s) => console.error(s) : void 0
1244
+ });
1102
1245
  }
1103
1246
  });
1104
1247
  //#endregion
@@ -1153,310 +1296,446 @@ async function setupLogger(context, { logLevel }) {
1153
1296
  const type = detectLogger();
1154
1297
  const logger = logMapper[type];
1155
1298
  if (!logger) throw new Error(`Unknown adapter type: ${type}`);
1156
- const cleanup = await logger.install(context, { logLevel });
1299
+ const makeSink = await logger.install(context, { logLevel });
1157
1300
  if (logLevel >= _kubb_core.logLevel.debug) await fileSystemLogger.install(context, { logLevel });
1158
- return cleanup;
1301
+ return typeof makeSink === "function" ? makeSink : void 0;
1159
1302
  }
1160
- //#endregion
1161
- //#region src/utils/executeHooks.ts
1162
- async function executeHooks({ hooks, events }) {
1163
- const commands = Array.isArray(hooks.done) ? hooks.done : [hooks.done].filter(Boolean);
1164
- for (const command of commands) {
1165
- const [cmd, ...args] = require_shell.tokenize(command);
1166
- if (!cmd) continue;
1167
- const hookId = (0, node_crypto.createHash)("sha256").update(command).digest("hex");
1168
- const hookEndPromise = new Promise((resolve, reject) => {
1169
- const handler = ({ id, success, error }) => {
1170
- if (id !== hookId) return;
1171
- events.off("hook:end", handler);
1172
- if (!success) {
1173
- reject(error ?? /* @__PURE__ */ new Error(`Hook failed: ${command}`));
1174
- return;
1175
- }
1176
- events.emit("success", `${(0, node_util.styleText)("dim", command)} successfully executed`).then(resolve).catch(reject);
1177
- };
1178
- events.on("hook:end", handler);
1179
- });
1180
- await events.emit("hook:start", {
1181
- id: hookId,
1182
- command: cmd,
1183
- args
1303
+ /**
1304
+ * Builds the generation summary lines rendered in the end-of-run box.
1305
+ * Returns an array of styled strings, one per summary row.
1306
+ */
1307
+ function getSummary({ failedPlugins, filesCreated, status, hrStart, config, pluginTimings }) {
1308
+ const duration = formatHrtime(hrStart);
1309
+ const pluginsCount = config.plugins?.length ?? 0;
1310
+ const successCount = pluginsCount - failedPlugins.size;
1311
+ const meta = {
1312
+ 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`,
1313
+ pluginsFailed: status === "failed" ? [...failedPlugins].map(({ plugin }) => randomCliColor(plugin.name)).join(", ") : void 0,
1314
+ filesCreated,
1315
+ time: (0, node_util.styleText)("green", duration),
1316
+ output: node_path.default.resolve(config.root, config.output.path)
1317
+ };
1318
+ const labels = {
1319
+ plugins: "Plugins:",
1320
+ failed: "Failed:",
1321
+ generated: "Generated:",
1322
+ pluginTimings: "Plugin Timings:",
1323
+ output: "Output:"
1324
+ };
1325
+ const maxLength = Math.max(0, ...[...Object.values(labels), ...pluginTimings ? Array.from(pluginTimings.keys()) : []].map((s) => s.length));
1326
+ const summaryLines = [];
1327
+ summaryLines.push(`${labels.plugins.padEnd(maxLength + 2)} ${meta.plugins}`);
1328
+ if (meta.pluginsFailed) summaryLines.push(`${labels.failed.padEnd(maxLength + 2)} ${meta.pluginsFailed}`);
1329
+ summaryLines.push(`${labels.generated.padEnd(maxLength + 2)} ${meta.filesCreated} files in ${meta.time}`);
1330
+ if (pluginTimings && pluginTimings.size > 0) {
1331
+ const sortedTimings = Array.from(pluginTimings.entries()).sort((a, b) => b[1] - a[1]);
1332
+ summaryLines.push(`${labels.pluginTimings}`);
1333
+ sortedTimings.forEach(([name, time]) => {
1334
+ const timeStr = time >= 1e3 ? `${(time / 1e3).toFixed(2)}s` : `${Math.round(time)}ms`;
1335
+ const barLength = Math.min(Math.ceil(time / 100), 10);
1336
+ const bar = (0, node_util.styleText)("dim", "█".repeat(barLength));
1337
+ summaryLines.push(`${(0, node_util.styleText)("dim", "•")} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`);
1184
1338
  });
1185
- await hookEndPromise;
1186
1339
  }
1340
+ summaryLines.push(`${labels.output.padEnd(maxLength + 2)} ${meta.output}`);
1341
+ return summaryLines;
1187
1342
  }
1188
1343
  //#endregion
1189
- //#region src/utils/getCosmiConfig.ts
1344
+ //#region src/runners/generate/utils.ts
1190
1345
  const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, {
1191
1346
  jsx: {
1192
1347
  runtime: "automatic",
1193
- importSource: "@kubb/react-fabric"
1348
+ importSource: "@kubb/renderer-jsx"
1194
1349
  },
1195
- sourceMaps: true,
1196
- interopDefault: true
1350
+ moduleCache: false
1197
1351
  });
1198
- const tsLoader = async (configFile) => {
1199
- return await jiti$1.import(configFile, { default: true });
1200
- };
1201
- async function getCosmiConfig(moduleName, config) {
1202
- let result;
1203
- const searchPlaces = [
1204
- "package.json",
1205
- `.${moduleName}rc`,
1206
- `.${moduleName}rc.json`,
1207
- `.${moduleName}rc.yaml`,
1208
- `.${moduleName}rc.yml`,
1209
- `.${moduleName}rc.ts`,
1210
- `.${moduleName}rc.js`,
1211
- `.${moduleName}rc.mjs`,
1212
- `.${moduleName}rc.cjs`,
1213
- `${moduleName}.config.ts`,
1214
- `${moduleName}.config.js`,
1215
- `${moduleName}.config.mjs`,
1216
- `${moduleName}.config.cjs`
1217
- ];
1218
- const explorer = (0, cosmiconfig.cosmiconfig)(moduleName, {
1352
+ const tsLoader = (configFile) => jiti$1.import(configFile, { default: true });
1353
+ const MODULE_NAME = "kubb";
1354
+ const BASE_SEARCH_PLACES = [
1355
+ "package.json",
1356
+ `.${MODULE_NAME}rc`,
1357
+ `.${MODULE_NAME}rc.json`,
1358
+ `.${MODULE_NAME}rc.yaml`,
1359
+ `.${MODULE_NAME}rc.yml`,
1360
+ `.${MODULE_NAME}rc.ts`,
1361
+ `.${MODULE_NAME}rc.mts`,
1362
+ `.${MODULE_NAME}rc.cts`,
1363
+ `.${MODULE_NAME}rc.js`,
1364
+ `.${MODULE_NAME}rc.mjs`,
1365
+ `.${MODULE_NAME}rc.cjs`,
1366
+ `${MODULE_NAME}.config.ts`,
1367
+ `${MODULE_NAME}.config.mts`,
1368
+ `${MODULE_NAME}.config.cts`,
1369
+ `${MODULE_NAME}.config.js`,
1370
+ `${MODULE_NAME}.config.mjs`,
1371
+ `${MODULE_NAME}.config.cjs`
1372
+ ];
1373
+ const SEARCH_PLACES = [
1374
+ "",
1375
+ ".config/",
1376
+ "configs/"
1377
+ ].flatMap((prefix) => BASE_SEARCH_PLACES.map((p) => `${prefix}${p}`));
1378
+ async function getCosmiConfig(configFile) {
1379
+ const explorer = (0, cosmiconfig.cosmiconfig)(MODULE_NAME, {
1219
1380
  cache: false,
1220
- searchPlaces: [
1221
- ...searchPlaces.map((searchPlace) => {
1222
- return `.config/${searchPlace}`;
1223
- }),
1224
- ...searchPlaces.map((searchPlace) => {
1225
- return `configs/${searchPlace}`;
1226
- }),
1227
- ...searchPlaces
1228
- ],
1229
- loaders: { ".ts": tsLoader }
1381
+ searchPlaces: SEARCH_PLACES,
1382
+ loaders: {
1383
+ ".ts": tsLoader,
1384
+ ".mts": tsLoader,
1385
+ ".cts": tsLoader
1386
+ }
1230
1387
  });
1388
+ let result;
1231
1389
  try {
1232
- result = config ? await explorer.load(config) : await explorer.search();
1390
+ result = configFile ? await explorer.load(configFile) : await explorer.search();
1233
1391
  } catch (error) {
1234
1392
  throw new Error("Config failed loading", { cause: error });
1235
1393
  }
1236
- 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");
1394
+ 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");
1237
1395
  return result;
1238
1396
  }
1239
- //#endregion
1240
- //#region src/utils/watcher.ts
1241
- async function startWatcher(path, cb) {
1397
+ /**
1398
+ * Discovers the Kubb config via cosmiconfig and resolves it into a normalized array of configs.
1399
+ * Every config in the result is guaranteed to have a `plugins` array.
1400
+ */
1401
+ async function getConfigs({ configPath, input }) {
1402
+ const result = await getCosmiConfig(configPath);
1403
+ const resolved = await (typeof result.config === "function" ? result.config({ input }) : result.config);
1404
+ const userConfigs = Array.isArray(resolved) ? resolved : [resolved];
1405
+ return {
1406
+ configPath: result.filepath,
1407
+ configs: userConfigs.map((item) => ({
1408
+ ...item,
1409
+ plugins: item.plugins ?? []
1410
+ }))
1411
+ };
1412
+ }
1413
+ /**
1414
+ * Runs the `done` hooks defined in a Kubb config in sequence.
1415
+ */
1416
+ async function executeHooks({ configHooks, hooks, makeSink }) {
1417
+ const commands = Array.isArray(configHooks.done) ? configHooks.done : [configHooks.done].filter(Boolean);
1418
+ for (const command of commands) {
1419
+ const [cmd, ...args] = require_shell.tokenize(command);
1420
+ if (!cmd) continue;
1421
+ const hookId = (0, node_crypto.createHash)("sha256").update(command).digest("hex");
1422
+ const commandWithArgs = [cmd, ...args].join(" ");
1423
+ await hooks.emit("kubb:hook:start", {
1424
+ id: hookId,
1425
+ command: cmd,
1426
+ args
1427
+ });
1428
+ const { stream = false, onLine, onStdout, onStderr } = makeSink?.(commandWithArgs) ?? {};
1429
+ await runHook({
1430
+ id: hookId,
1431
+ command: cmd,
1432
+ args,
1433
+ commandWithArgs,
1434
+ context: hooks,
1435
+ stream,
1436
+ sink: {
1437
+ onLine,
1438
+ onStdout,
1439
+ onStderr
1440
+ }
1441
+ });
1442
+ }
1443
+ }
1444
+ async function runHook({ id, command, args, commandWithArgs, context, stream = false, sink }) {
1445
+ const emitEnd = (success, error) => context.emit("kubb:hook:end", {
1446
+ command,
1447
+ args,
1448
+ id,
1449
+ success,
1450
+ error
1451
+ });
1452
+ try {
1453
+ const proc = (0, tinyexec.x)(command, [...args ?? []], {
1454
+ nodeOptions: { detached: process.platform !== "win32" },
1455
+ throwOnError: true
1456
+ });
1457
+ if (stream && sink?.onLine) for await (const line of proc) sink.onLine(line);
1458
+ const result = await proc;
1459
+ await context.emit("kubb:debug", {
1460
+ date: /* @__PURE__ */ new Date(),
1461
+ logs: [result.stdout.trimEnd()]
1462
+ });
1463
+ await context.emit("kubb:success", { message: `${(0, node_util.styleText)("dim", commandWithArgs)} successfully executed` });
1464
+ await emitEnd(true, null);
1465
+ } catch (err) {
1466
+ if (!(err instanceof tinyexec.NonZeroExitError)) {
1467
+ const error = require_errors.toError(err);
1468
+ await emitEnd(false, error);
1469
+ await context.emit("kubb:error", { error });
1470
+ return;
1471
+ }
1472
+ const stderr = err.output?.stderr ?? "";
1473
+ const stdout = err.output?.stdout ?? "";
1474
+ await context.emit("kubb:debug", {
1475
+ date: /* @__PURE__ */ new Date(),
1476
+ logs: [stdout, stderr].filter(Boolean)
1477
+ });
1478
+ if (stderr) sink?.onStderr?.(stderr);
1479
+ if (stdout) sink?.onStdout?.(stdout);
1480
+ const error = /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`);
1481
+ await emitEnd(false, error);
1482
+ await context.emit("kubb:error", { error });
1483
+ }
1484
+ }
1485
+ /**
1486
+ * Starts a file watcher on the given paths and calls `cb` on any change.
1487
+ * Ignores `.git` and `node_modules` directories.
1488
+ */
1489
+ async function startWatcher(path, cb, log = {
1490
+ info: console.log,
1491
+ error: console.log
1492
+ }) {
1242
1493
  const { watch } = await import("chokidar");
1243
1494
  watch(path, {
1244
1495
  ignorePermissionErrors: true,
1245
1496
  ignored: require_constants.WATCHER_IGNORED_PATHS
1246
1497
  }).on("all", async (type, file) => {
1247
- console.log((0, node_util.styleText)("yellow", (0, node_util.styleText)("bold", `Change detected: ${type} ${file}`)));
1498
+ log.info((0, node_util.styleText)("yellow", (0, node_util.styleText)("bold", `Change detected: ${type} ${file}`)));
1248
1499
  try {
1249
1500
  await cb(path);
1250
1501
  } catch (_e) {
1251
- console.log((0, node_util.styleText)("red", "Watcher failed"));
1502
+ log.error((0, node_util.styleText)("red", "Watcher failed"));
1252
1503
  }
1253
1504
  });
1254
1505
  }
1255
1506
  //#endregion
1256
- //#region src/runners/generate.ts
1257
- async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefix, noToolMessage, configName, outputPath, logLevel, events, onStart, onEnd }) {
1507
+ //#region src/runners/generate/run.ts
1508
+ /**
1509
+ * Registers a one-shot `kubb:hook:end` listener for `hookId` BEFORE the caller emits `kubb:hook:start`,
1510
+ * avoiding the race where a synchronous emitter fires end before the listener is attached.
1511
+ */
1512
+ function waitForHookEnd(hooks, hookId, onSuccess, fallbackErrorMessage) {
1513
+ return new Promise((resolve, reject) => {
1514
+ const handler = (ctx) => {
1515
+ if (ctx.id !== hookId) return;
1516
+ hooks.off("kubb:hook:end", handler);
1517
+ if (!ctx.success) {
1518
+ reject(ctx.error ?? new Error(fallbackErrorMessage));
1519
+ return;
1520
+ }
1521
+ onSuccess().then(resolve).catch(reject);
1522
+ };
1523
+ hooks.on("kubb:hook:end", handler);
1524
+ });
1525
+ }
1526
+ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefix, noToolMessage, configName, outputPath, logLevel, hooks, onStart, onEnd }) {
1258
1527
  await onStart();
1259
1528
  let resolvedTool = toolValue;
1260
1529
  if (resolvedTool === "auto") {
1261
1530
  const detected = await detect();
1262
- if (!detected) await events.emit("warn", noToolMessage);
1531
+ if (!detected) await hooks.emit("kubb:warn", { message: noToolMessage });
1263
1532
  else {
1264
1533
  resolvedTool = detected;
1265
- await events.emit("info", `Auto-detected ${toolLabel}: ${(0, node_util.styleText)("dim", resolvedTool)}`);
1534
+ await hooks.emit("kubb:info", { message: `Auto-detected ${toolLabel}: ${(0, node_util.styleText)("dim", resolvedTool)}` });
1266
1535
  }
1267
1536
  }
1537
+ let toolError;
1268
1538
  if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap) {
1269
1539
  const toolConfig = toolMap[resolvedTool];
1540
+ const hookId = (0, node_crypto.createHash)("sha256").update([configName, resolvedTool].filter(Boolean).join("-")).digest("hex");
1541
+ const successMessage = [
1542
+ `${successPrefix} with ${(0, node_util.styleText)("dim", resolvedTool)}`,
1543
+ logLevel >= _kubb_core.logLevel.info ? `on ${(0, node_util.styleText)("dim", outputPath)}` : void 0,
1544
+ "successfully"
1545
+ ].filter(Boolean).join(" ");
1270
1546
  try {
1271
- const hookId = (0, node_crypto.createHash)("sha256").update([configName, resolvedTool].filter(Boolean).join("-")).digest("hex");
1272
- const hookEndPromise = new Promise((resolve, reject) => {
1273
- const handler = ({ id, success, error }) => {
1274
- if (id !== hookId) return;
1275
- events.off("hook:end", handler);
1276
- if (!success) {
1277
- reject(error ?? /* @__PURE__ */ new Error(`${toolConfig.errorMessage}`));
1278
- return;
1279
- }
1280
- events.emit("success", [
1281
- `${successPrefix} with ${(0, node_util.styleText)("dim", resolvedTool)}`,
1282
- logLevel >= _kubb_core.logLevel.info ? `on ${(0, node_util.styleText)("dim", outputPath)}` : void 0,
1283
- "successfully"
1284
- ].filter(Boolean).join(" ")).then(resolve).catch(reject);
1285
- };
1286
- events.on("hook:end", handler);
1287
- });
1288
- await events.emit("hook:start", {
1547
+ const hookEndPromise = waitForHookEnd(hooks, hookId, () => hooks.emit("kubb:success", { message: successMessage }), toolConfig.errorMessage);
1548
+ await hooks.emit("kubb:hook:start", {
1289
1549
  id: hookId,
1290
1550
  command: toolConfig.command,
1291
1551
  args: toolConfig.args(outputPath)
1292
1552
  });
1293
1553
  await hookEndPromise;
1294
1554
  } catch (caughtError) {
1295
- const err = new Error(toolConfig.errorMessage);
1296
- err.cause = caughtError;
1297
- await events.emit("error", err);
1555
+ const err = require_errors.toError(caughtError);
1556
+ await hooks.emit("kubb:error", { error: err });
1557
+ toolError = err;
1298
1558
  }
1299
1559
  }
1300
1560
  await onEnd();
1561
+ if (toolError) throw toolError;
1301
1562
  }
1302
- async function generate({ input, config: userConfig, events, logLevel }) {
1303
- const inputPath = input ?? ("path" in userConfig.input ? userConfig.input.path : void 0);
1563
+ async function generate(options) {
1564
+ const { input, hooks, logLevel, makeSink } = options;
1304
1565
  const hrStart = node_process.default.hrtime();
1566
+ const inputPath = input ?? (options.config.input && "path" in options.config.input ? options.config.input.path : void 0);
1305
1567
  const config = {
1306
- ...userConfig,
1307
- root: userConfig.root || node_process.default.cwd(),
1568
+ ...options.config,
1308
1569
  input: inputPath ? {
1309
- ...userConfig.input,
1570
+ ...options.config.input,
1310
1571
  path: inputPath
1311
- } : userConfig.input,
1312
- output: {
1313
- write: true,
1314
- barrelType: "named",
1315
- extension: { ".ts": ".ts" },
1316
- format: "prettier",
1317
- ...userConfig.output
1318
- }
1572
+ } : options.config.input,
1573
+ ...options.config.output
1319
1574
  };
1320
- await events.emit("generation:start", config);
1321
- await events.emit("info", config.name ? `Setup generation ${(0, node_util.styleText)("bold", config.name)}` : "Setup generation", inputPath);
1322
- const { sources, fabric, driver } = await (0, _kubb_core.setup)({
1323
- config,
1324
- events
1575
+ const kubb = (0, _kubb_core.createKubb)(config, { hooks });
1576
+ await kubb.setup();
1577
+ await hooks.emit("kubb:generation:start", { config });
1578
+ await hooks.emit("kubb:info", {
1579
+ message: config.name ? `Setup generation ${(0, node_util.styleText)("bold", config.name)}` : "Setup generation",
1580
+ info: inputPath
1325
1581
  });
1326
- await events.emit("info", config.name ? `Build generation ${(0, node_util.styleText)("bold", config.name)}` : "Build generation", inputPath);
1327
- const { files, failedPlugins, pluginTimings, error } = await (0, _kubb_core.safeBuild)({
1328
- config,
1329
- events
1330
- }, {
1331
- driver,
1332
- fabric,
1333
- events,
1334
- sources
1582
+ await hooks.emit("kubb:info", {
1583
+ message: config.name ? `Build generation ${(0, node_util.styleText)("bold", config.name)}` : "Build generation",
1584
+ info: inputPath
1335
1585
  });
1336
- await events.emit("info", "Load summary");
1586
+ const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild();
1587
+ await hooks.emit("kubb:info", { message: "Load summary" });
1588
+ const telemetryPlugins = Array.from(driver.plugins.values(), (p) => ({
1589
+ name: p.name,
1590
+ options: p.options
1591
+ }));
1592
+ const reportTelemetry = (status) => require_telemetry.sendTelemetry(require_telemetry.buildTelemetryEvent({
1593
+ command: "generate",
1594
+ kubbVersion: require_package.version,
1595
+ plugins: telemetryPlugins,
1596
+ hrStart,
1597
+ filesCreated: files.length,
1598
+ status
1599
+ }));
1337
1600
  if (failedPlugins.size > 0 || error) {
1338
- const allErrors = [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean);
1339
- for (const err of allErrors) await events.emit("error", err);
1340
- await events.emit("generation:end", config, files, sources);
1341
- await events.emit("generation:summary", config, {
1601
+ const allErrors = [error, ...Array.from(failedPlugins, (it) => it.error)].filter(Boolean);
1602
+ for (const err of allErrors) await hooks.emit("kubb:error", { error: err });
1603
+ await hooks.emit("kubb:generation:end", {
1604
+ config,
1605
+ files,
1606
+ sources: kubb.sources
1607
+ });
1608
+ await hooks.emit("kubb:generation:summary", {
1609
+ config,
1342
1610
  failedPlugins,
1343
1611
  filesCreated: files.length,
1344
1612
  status: "failed",
1345
1613
  hrStart,
1346
1614
  pluginTimings: logLevel >= _kubb_core.logLevel.verbose ? pluginTimings : void 0
1347
1615
  });
1348
- await require_telemetry.sendTelemetry(require_telemetry.buildTelemetryEvent({
1349
- command: "generate",
1350
- kubbVersion: require_package.version,
1351
- plugins: driver.plugins.map((p) => ({
1352
- name: p.name,
1353
- options: p.options
1354
- })),
1355
- hrStart,
1356
- filesCreated: files.length,
1357
- status: "failed"
1358
- }));
1616
+ await reportTelemetry("failed");
1359
1617
  node_process.default.exit(1);
1360
1618
  }
1361
- await events.emit("success", "Generation successfully", inputPath);
1362
- await events.emit("generation:end", config, files, sources);
1619
+ await hooks.emit("kubb:success", {
1620
+ message: "Generation succeeded",
1621
+ info: inputPath
1622
+ });
1623
+ await hooks.emit("kubb:generation:end", {
1624
+ config,
1625
+ files,
1626
+ sources: kubb.sources
1627
+ });
1363
1628
  const outputPath = node_path.default.resolve(config.root, config.output.path);
1364
- if (config.output.format) await runToolPass({
1629
+ const toolPasses = [config.output.format && {
1365
1630
  toolValue: config.output.format,
1366
- detect: _kubb_core.detectFormatter,
1367
- toolMap: _kubb_core.formatters,
1631
+ detect: detectFormatter,
1632
+ toolMap: formatters,
1368
1633
  toolLabel: "formatter",
1369
1634
  successPrefix: "Formatting",
1370
- noToolMessage: "No formatter found (biome, prettier, or oxfmt). Skipping formatting.",
1371
- configName: config.name,
1372
- outputPath,
1373
- logLevel,
1374
- events,
1375
- onStart: () => events.emit("format:start"),
1376
- onEnd: () => events.emit("format:end")
1377
- });
1378
- if (config.output.lint) await runToolPass({
1635
+ noToolMessage: "No formatter found (oxfmt, biome, or prettier). Skipping formatting.",
1636
+ onStart: () => hooks.emit("kubb:format:start"),
1637
+ onEnd: () => hooks.emit("kubb:format:end")
1638
+ }, config.output.lint && {
1379
1639
  toolValue: config.output.lint,
1380
- detect: _kubb_core.detectLinter,
1381
- toolMap: _kubb_core.linters,
1640
+ detect: detectLinter,
1641
+ toolMap: linters,
1382
1642
  toolLabel: "linter",
1383
1643
  successPrefix: "Linting",
1384
- noToolMessage: "No linter found (biome, oxlint, or eslint). Skipping linting.",
1644
+ noToolMessage: "No linter found (oxlint, biome, or eslint). Skipping linting.",
1645
+ onStart: () => hooks.emit("kubb:lint:start"),
1646
+ onEnd: () => hooks.emit("kubb:lint:end")
1647
+ }].filter(Boolean);
1648
+ for (const pass of toolPasses) await runToolPass({
1649
+ ...pass,
1385
1650
  configName: config.name,
1386
1651
  outputPath,
1387
1652
  logLevel,
1388
- events,
1389
- onStart: () => events.emit("lint:start"),
1390
- onEnd: () => events.emit("lint:end")
1653
+ hooks
1391
1654
  });
1392
1655
  if (config.hooks) {
1393
- await events.emit("hooks:start");
1656
+ await hooks.emit("kubb:hooks:start");
1394
1657
  await executeHooks({
1395
- hooks: config.hooks,
1396
- events
1658
+ configHooks: config.hooks,
1659
+ hooks,
1660
+ makeSink
1397
1661
  });
1398
- await events.emit("hooks:end");
1662
+ await hooks.emit("kubb:hooks:end");
1399
1663
  }
1400
- await events.emit("generation:summary", config, {
1664
+ await hooks.emit("kubb:generation:summary", {
1665
+ config,
1401
1666
  failedPlugins,
1402
1667
  filesCreated: files.length,
1403
1668
  status: "success",
1404
1669
  hrStart,
1405
1670
  pluginTimings
1406
1671
  });
1407
- await require_telemetry.sendTelemetry(require_telemetry.buildTelemetryEvent({
1408
- command: "generate",
1409
- kubbVersion: require_package.version,
1410
- plugins: driver.plugins.map((p) => ({
1411
- name: p.name,
1412
- options: p.options
1413
- })),
1414
- hrStart,
1415
- filesCreated: files.length,
1416
- status: "success"
1417
- }));
1672
+ await reportTelemetry("success");
1418
1673
  }
1419
- async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }) {
1420
- const logLevel = _kubb_core.logLevel[logLevelKey] ?? _kubb_core.logLevel.info;
1421
- const events = new AsyncEventEmitter();
1422
- await setupLogger(events, { logLevel });
1674
+ async function checkForUpdate(hooks) {
1423
1675
  await require_telemetry.executeIfOnline(async () => {
1424
1676
  try {
1425
- const latestVersion = (await (await fetch(require_constants.KUBB_NPM_PACKAGE_URL)).json()).version;
1426
- if (latestVersion && require_package.version < latestVersion) await events.emit("version:new", require_package.version, latestVersion);
1677
+ const data = await (await fetch(require_constants.KUBB_NPM_PACKAGE_URL)).json();
1678
+ if (data.version && require_package.version < data.version) await hooks.emit("kubb:version:new", {
1679
+ currentVersion: require_package.version,
1680
+ latestVersion: data.version
1681
+ });
1427
1682
  } catch {}
1428
1683
  });
1684
+ }
1685
+ /**
1686
+ * Runs the full Kubb generation lifecycle for the given CLI options.
1687
+ * Sets up the logger, checks for a newer version, loads configs, and calls `generate` for each config entry.
1688
+ */
1689
+ async function run({ input, configPath, logLevel: logLevelKey, watch }) {
1690
+ const logLevel = _kubb_core.logLevel[logLevelKey] ?? _kubb_core.logLevel.info;
1691
+ const hooks = new AsyncEventEmitter();
1692
+ const makeSink = await setupLogger(hooks, { logLevel });
1693
+ await checkForUpdate(hooks);
1429
1694
  try {
1430
- const result = await getCosmiConfig("kubb", configPath);
1431
- const configs = await (0, _kubb_core.getConfigs)(result.config, { input });
1432
- await events.emit("config:start");
1433
- await events.emit("info", "Config loaded", node_path.default.relative(node_process.default.cwd(), result.filepath));
1434
- await events.emit("success", "Config loaded successfully", node_path.default.relative(node_process.default.cwd(), result.filepath));
1435
- await events.emit("config:end", configs);
1436
- await events.emit("lifecycle:start", require_package.version);
1695
+ const { configs, configPath: resolvedConfigPath } = await getConfigs({
1696
+ configPath,
1697
+ input
1698
+ });
1699
+ const relativeConfigPath = node_path.default.relative(node_process.default.cwd(), resolvedConfigPath);
1700
+ await hooks.emit("kubb:config:start");
1701
+ await hooks.emit("kubb:info", {
1702
+ message: "Config loaded",
1703
+ info: relativeConfigPath
1704
+ });
1705
+ await hooks.emit("kubb:success", {
1706
+ message: "Config loaded successfully",
1707
+ info: relativeConfigPath
1708
+ });
1709
+ await hooks.emit("kubb:config:end", { configs });
1710
+ await hooks.emit("kubb:lifecycle:start", { version: require_package.version });
1437
1711
  for (const config of configs) if ((0, _kubb_core.isInputPath)(config) && watch) await startWatcher([input || config.input.path], async (paths) => {
1438
- events.removeAll();
1712
+ hooks.removeAll();
1439
1713
  await generate({
1440
1714
  input,
1441
1715
  config,
1442
1716
  logLevel,
1443
- events
1717
+ hooks,
1718
+ makeSink
1444
1719
  });
1445
1720
  _clack_prompts.log.step((0, node_util.styleText)("yellow", `Watching for changes in ${paths.join(" and ")}`));
1721
+ }, {
1722
+ info: (msg) => _clack_prompts.log.info(msg),
1723
+ error: (msg) => _clack_prompts.log.error(msg)
1446
1724
  });
1447
1725
  else await generate({
1448
1726
  input,
1449
1727
  config,
1450
1728
  logLevel,
1451
- events
1729
+ hooks,
1730
+ makeSink
1452
1731
  });
1453
- await events.emit("lifecycle:end");
1732
+ await hooks.emit("kubb:lifecycle:end");
1454
1733
  } catch (error) {
1455
- await events.emit("error", require_errors.toError(error));
1734
+ await hooks.emit("kubb:error", { error: require_errors.toError(error) });
1456
1735
  node_process.default.exit(1);
1457
1736
  }
1458
1737
  }
1459
1738
  //#endregion
1460
- exports.runGenerateCommand = runGenerateCommand;
1739
+ exports.run = run;
1461
1740
 
1462
- //# sourceMappingURL=generate-Cq5RDTBL.cjs.map
1741
+ //# sourceMappingURL=run-CVlrIZoW.cjs.map