@kubb/cli 5.0.0-beta.84 → 5.0.0-beta.86

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 (76) hide show
  1. package/dist/{Telemetry-ugO-B77y.js → Telemetry-Cuzoes9Y.js} +6 -2
  2. package/dist/Telemetry-Cuzoes9Y.js.map +1 -0
  3. package/dist/{Telemetry-jtbchpQE.cjs → Telemetry-CwQYBgsg.cjs} +11 -1
  4. package/dist/Telemetry-CwQYBgsg.cjs.map +1 -0
  5. package/dist/{generate-DDgZnlMs.js → generate-BO6xVUEL.js} +34 -24
  6. package/dist/generate-BO6xVUEL.js.map +1 -0
  7. package/dist/{generate-Diob_zqj.cjs → generate-BW9yIfgS.cjs} +34 -23
  8. package/dist/generate-BW9yIfgS.cjs.map +1 -0
  9. package/dist/index.cjs +28 -192
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +29 -193
  12. package/dist/index.js.map +1 -1
  13. package/dist/{init-uOQx7jSE.cjs → init-Cx4n_O5X.cjs} +9 -10
  14. package/dist/init-Cx4n_O5X.cjs.map +1 -0
  15. package/dist/{init-Cn3jZVkf.js → init-_NdYuc6u.js} +10 -10
  16. package/dist/init-_NdYuc6u.js.map +1 -0
  17. package/dist/{mcp-n-IOoFZL.js → mcp-BuhCkjE_.js} +6 -6
  18. package/dist/mcp-BuhCkjE_.js.map +1 -0
  19. package/dist/{mcp-BxhOXHGD.cjs → mcp-DJd2cm9I.cjs} +5 -6
  20. package/dist/mcp-DJd2cm9I.cjs.map +1 -0
  21. package/dist/{package-BBBOfxZz.cjs → package-Bar6zPkT.cjs} +2 -2
  22. package/dist/package-Bar6zPkT.cjs.map +1 -0
  23. package/dist/package-D24TC3pS.js +6 -0
  24. package/dist/package-D24TC3pS.js.map +1 -0
  25. package/dist/{run-DXEMoDFa.cjs → run-3qq_Q4Tg.cjs} +4 -4
  26. package/dist/run-3qq_Q4Tg.cjs.map +1 -0
  27. package/dist/{run-Bz6ZUrhp.cjs → run-B9FN0sHW.cjs} +2 -2
  28. package/dist/run-B9FN0sHW.cjs.map +1 -0
  29. package/dist/{run-CPBFfbI7.js → run-BFhNcn8b.js} +4 -10
  30. package/dist/run-BFhNcn8b.js.map +1 -0
  31. package/dist/{run-D8TRnpBB.js → run-BxMfDivH.js} +291 -333
  32. package/dist/run-BxMfDivH.js.map +1 -0
  33. package/dist/{run-BBLhhWZu.js → run-D0Gs_UzJ.js} +2 -2
  34. package/dist/run-D0Gs_UzJ.js.map +1 -0
  35. package/dist/{run-DYkciQ4u.cjs → run-DYzUPE7Z.cjs} +4 -10
  36. package/dist/run-DYzUPE7Z.cjs.map +1 -0
  37. package/dist/{run-ChSqYuc1.cjs → run-lAxAF-4t.cjs} +292 -333
  38. package/dist/run-lAxAF-4t.cjs.map +1 -0
  39. package/dist/{run-44Zxh8Ec.js → run-yMRylOST.js} +3 -3
  40. package/dist/run-yMRylOST.js.map +1 -0
  41. package/dist/{tools-D0UogMU3.js → tools-B23ltmWP.js} +3 -3
  42. package/dist/tools-B23ltmWP.js.map +1 -0
  43. package/dist/{tools-CZT9wSA6.cjs → tools-CeIABV0T.cjs} +3 -3
  44. package/dist/tools-CeIABV0T.cjs.map +1 -0
  45. package/dist/{validate-CYIieoc4.cjs → validate-D0doE06k.cjs} +8 -9
  46. package/dist/validate-D0doE06k.cjs.map +1 -0
  47. package/dist/{validate-BoG8ZZgE.js → validate-DZXin3M6.js} +9 -9
  48. package/dist/validate-DZXin3M6.js.map +1 -0
  49. package/package.json +8 -7
  50. package/dist/Telemetry-jtbchpQE.cjs.map +0 -1
  51. package/dist/Telemetry-ugO-B77y.js.map +0 -1
  52. package/dist/defineCommand-Bo3yZTWI.cjs +0 -36
  53. package/dist/defineCommand-Bo3yZTWI.cjs.map +0 -1
  54. package/dist/defineCommand-DMEeqliP.js +0 -31
  55. package/dist/defineCommand-DMEeqliP.js.map +0 -1
  56. package/dist/generate-DDgZnlMs.js.map +0 -1
  57. package/dist/generate-Diob_zqj.cjs.map +0 -1
  58. package/dist/init-Cn3jZVkf.js.map +0 -1
  59. package/dist/init-uOQx7jSE.cjs.map +0 -1
  60. package/dist/mcp-BxhOXHGD.cjs.map +0 -1
  61. package/dist/mcp-n-IOoFZL.js.map +0 -1
  62. package/dist/package-BBBOfxZz.cjs.map +0 -1
  63. package/dist/package-DKwwXnAz.js +0 -6
  64. package/dist/package-DKwwXnAz.js.map +0 -1
  65. package/dist/run-44Zxh8Ec.js.map +0 -1
  66. package/dist/run-BBLhhWZu.js.map +0 -1
  67. package/dist/run-Bz6ZUrhp.cjs.map +0 -1
  68. package/dist/run-CPBFfbI7.js.map +0 -1
  69. package/dist/run-ChSqYuc1.cjs.map +0 -1
  70. package/dist/run-D8TRnpBB.js.map +0 -1
  71. package/dist/run-DXEMoDFa.cjs.map +0 -1
  72. package/dist/run-DYkciQ4u.cjs.map +0 -1
  73. package/dist/tools-CZT9wSA6.cjs.map +0 -1
  74. package/dist/tools-D0UogMU3.js.map +0 -1
  75. package/dist/validate-BoG8ZZgE.js.map +0 -1
  76. package/dist/validate-CYIieoc4.cjs.map +0 -1
@@ -1,129 +1,21 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-Bx3C2hgW.cjs");
2
+ const require_Telemetry = require("./Telemetry-CwQYBgsg.cjs");
2
3
  const require_errors = require("./errors-gxFK0vrp.cjs");
3
- const require_Telemetry = require("./Telemetry-jtbchpQE.cjs");
4
- const require_tools = require("./tools-CZT9wSA6.cjs");
5
- const require_package = require("./package-BBBOfxZz.cjs");
4
+ const require_package = require("./package-Bar6zPkT.cjs");
5
+ const require_tools = require("./tools-CeIABV0T.cjs");
6
6
  let node_util = require("node:util");
7
- let node_events = require("node:events");
8
- let node_crypto = require("node:crypto");
7
+ require("node:crypto");
8
+ let node_process = require("node:process");
9
+ node_process = require_rolldown_runtime.__toESM(node_process, 1);
9
10
  let node_fs = require("node:fs");
10
11
  let node_path = require("node:path");
11
12
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
12
- let node_process = require("node:process");
13
- node_process = require_rolldown_runtime.__toESM(node_process, 1);
14
13
  let _clack_prompts = require("@clack/prompts");
15
14
  _clack_prompts = require_rolldown_runtime.__toESM(_clack_prompts, 1);
16
15
  let _kubb_core = require("@kubb/core");
17
16
  let jiti = require("jiti");
18
17
  let tinyexec = require("tinyexec");
19
18
  let unconfig = require("unconfig");
20
- //#region ../../internals/utils/src/asyncEventEmitter.ts
21
- /**
22
- * Typed `EventEmitter` that awaits all async listeners before resolving.
23
- * Wraps Node's `EventEmitter` with full TypeScript event-map inference.
24
- *
25
- * @example
26
- * ```ts
27
- * const emitter = new AsyncEventEmitter<{ build: [name: string] }>()
28
- * emitter.on('build', async (name) => { console.log(name) })
29
- * await emitter.emit('build', 'petstore') // all listeners awaited
30
- * ```
31
- */
32
- var AsyncEventEmitter = class {
33
- /**
34
- * Maximum number of listeners per event before Node emits a memory-leak warning.
35
- * @default 10
36
- */
37
- constructor(maxListener = 10) {
38
- this.#emitter.setMaxListeners(maxListener);
39
- }
40
- #emitter = new node_events.EventEmitter();
41
- /**
42
- * Emits `eventName` and awaits all registered listeners sequentially.
43
- * Throws if any listener rejects, wrapping the cause with the event name and serialized arguments.
44
- *
45
- * @example
46
- * ```ts
47
- * await emitter.emit('build', 'petstore')
48
- * ```
49
- */
50
- emit(eventName, ...eventArgs) {
51
- const listeners = this.#emitter.listeners(eventName);
52
- if (listeners.length === 0) return;
53
- return this.#emitAll(eventName, listeners, eventArgs);
54
- }
55
- async #emitAll(eventName, listeners, eventArgs) {
56
- for (const listener of listeners) try {
57
- await listener(...eventArgs);
58
- } catch (err) {
59
- let serializedArgs;
60
- try {
61
- serializedArgs = JSON.stringify(eventArgs);
62
- } catch {
63
- serializedArgs = String(eventArgs);
64
- }
65
- throw new Error(`Error in async listener for "${eventName}" with eventArgs ${serializedArgs}`, { cause: require_errors.toError(err) });
66
- }
67
- }
68
- /**
69
- * Registers a persistent listener for `eventName`.
70
- *
71
- * @example
72
- * ```ts
73
- * emitter.on('build', async (name) => { console.log(name) })
74
- * ```
75
- */
76
- on(eventName, handler) {
77
- this.#emitter.on(eventName, handler);
78
- }
79
- /**
80
- * Removes a previously registered listener.
81
- *
82
- * @example
83
- * ```ts
84
- * emitter.off('build', handler)
85
- * ```
86
- */
87
- off(eventName, handler) {
88
- this.#emitter.off(eventName, handler);
89
- }
90
- /**
91
- * Returns the number of listeners registered for `eventName`.
92
- *
93
- * @example
94
- * ```ts
95
- * emitter.on('build', handler)
96
- * emitter.listenerCount('build') // 1
97
- * ```
98
- */
99
- listenerCount(eventName) {
100
- return this.#emitter.listenerCount(eventName);
101
- }
102
- /**
103
- * Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
104
- * Set this above the expected listener count when many listeners attach by design.
105
- *
106
- * @example
107
- * ```ts
108
- * emitter.setMaxListeners(40)
109
- * ```
110
- */
111
- setMaxListeners(max) {
112
- this.#emitter.setMaxListeners(max);
113
- }
114
- /**
115
- * Removes all listeners from every event channel.
116
- *
117
- * @example
118
- * ```ts
119
- * emitter.removeAll()
120
- * ```
121
- */
122
- removeAll() {
123
- this.#emitter.removeAllListeners();
124
- }
125
- };
126
- //#endregion
127
19
  //#region ../../internals/utils/src/time.ts
128
20
  /**
129
21
  * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
@@ -268,6 +160,43 @@ function formatMsWithColor(ms) {
268
160
  return (0, node_util.styleText)("red", formatted);
269
161
  }
270
162
  //#endregion
163
+ //#region ../../internals/utils/src/promise.ts
164
+ /**
165
+ * Wraps `run` so invocations never overlap: a trigger that lands while a run is in flight
166
+ * marks it dirty and runs once more after it finishes, no matter how many triggers arrived.
167
+ * Useful for event-driven reruns (a file watcher, a queue drain) where bursts should
168
+ * coalesce into a single trailing run.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * const rebuild = createSerialRunner({
173
+ * run: () => build(),
174
+ * onError: (error) => log.error(error.message),
175
+ * })
176
+ * watcher.on('change', () => void rebuild())
177
+ * ```
178
+ */
179
+ function createSerialRunner({ run, onError }) {
180
+ let running = false;
181
+ let dirty = false;
182
+ return async () => {
183
+ if (running) {
184
+ dirty = true;
185
+ return;
186
+ }
187
+ running = true;
188
+ do {
189
+ dirty = false;
190
+ try {
191
+ await run();
192
+ } catch (error) {
193
+ onError(require_errors.toError(error));
194
+ }
195
+ } while (dirty);
196
+ running = false;
197
+ };
198
+ }
199
+ //#endregion
271
200
  //#region src/loggers/defineLogger.ts
272
201
  /**
273
202
  * Type-safe helper for authoring a {@link Logger}.
@@ -277,7 +206,7 @@ function formatMsWithColor(ms) {
277
206
  * export const myLogger = defineLogger({
278
207
  * name: 'my-logger',
279
208
  * install(context) {
280
- * context.on('kubb:info', ({ message }) => console.log('ℹ', message))
209
+ * context.hook('kubb:info', ({ message }) => console.log('ℹ', message))
281
210
  * },
282
211
  * })
283
212
  * ```
@@ -329,8 +258,8 @@ const clackLogger = defineLogger({
329
258
  function getMessage(message) {
330
259
  return formatMessage(message, logLevel);
331
260
  }
332
- function onStep(event, message) {
333
- context.on(event, () => {
261
+ function onStep(hook, message) {
262
+ context.hook(hook, () => {
334
263
  if (logLevel <= _kubb_core.logLevel.silent) return;
335
264
  _clack_prompts.log.step(getMessage(message));
336
265
  });
@@ -340,7 +269,7 @@ const clackLogger = defineLogger({
340
269
  state.spinner.stop(text);
341
270
  state.isSpinning = false;
342
271
  }
343
- context.on("kubb:info", ({ message, info = "" }) => {
272
+ context.hook("kubb:info", ({ message, info = "" }) => {
344
273
  if (logLevel <= _kubb_core.logLevel.silent) return;
345
274
  const text = getMessage([
346
275
  (0, node_util.styleText)("blue", "ℹ"),
@@ -353,7 +282,7 @@ const clackLogger = defineLogger({
353
282
  }
354
283
  _clack_prompts.log.info(text);
355
284
  });
356
- context.on("kubb:success", ({ message, info = "" }) => {
285
+ context.hook("kubb:success", ({ message, info = "" }) => {
357
286
  if (logLevel <= _kubb_core.logLevel.silent) return;
358
287
  const text = getMessage([
359
288
  (0, node_util.styleText)("blue", "✓"),
@@ -366,7 +295,7 @@ const clackLogger = defineLogger({
366
295
  }
367
296
  _clack_prompts.log.success(text);
368
297
  });
369
- context.on("kubb:warn", ({ message, info }) => {
298
+ context.hook("kubb:warn", ({ message, info }) => {
370
299
  if (logLevel < _kubb_core.logLevel.warn) return;
371
300
  const text = getMessage([
372
301
  (0, node_util.styleText)("yellow", "⚠"),
@@ -375,25 +304,23 @@ const clackLogger = defineLogger({
375
304
  ].filter(Boolean).join(" "));
376
305
  _clack_prompts.log.warn(text);
377
306
  });
378
- context.on("kubb:error", ({ error }) => {
379
- const caused = require_errors.toCause(error);
307
+ context.hook("kubb:error", ({ error }) => {
380
308
  const text = [(0, node_util.styleText)("red", "✗"), error.message].join(" ");
381
309
  if (state.isSpinning) {
382
310
  stopSpinner(getMessage(text));
383
311
  return;
384
312
  }
385
313
  _clack_prompts.log.error(getMessage(text));
386
- if (logLevel >= _kubb_core.logLevel.verbose && error.stack) {
387
- const frames = error.stack.split("\n").slice(1, 4);
388
- for (const frame of frames) _clack_prompts.log.message(getMessage((0, node_util.styleText)("dim", frame.trim())));
389
- if (caused?.stack) {
390
- _clack_prompts.log.message((0, node_util.styleText)("dim", `└─ caused by ${caused.message}`));
391
- const frames = caused.stack.split("\n").slice(1, 4);
392
- for (const frame of frames) _clack_prompts.log.message(getMessage(` ${(0, node_util.styleText)("dim", frame.trim())}`));
314
+ const frames = logLevel >= _kubb_core.logLevel.verbose ? formatErrorFrames(error) : null;
315
+ if (frames) {
316
+ for (const frame of frames.frames) _clack_prompts.log.message(getMessage((0, node_util.styleText)("dim", frame)));
317
+ if (frames.cause) {
318
+ _clack_prompts.log.message((0, node_util.styleText)("dim", frames.cause.header));
319
+ for (const frame of frames.cause.frames) _clack_prompts.log.message(getMessage(` ${(0, node_util.styleText)("dim", frame)}`));
393
320
  }
394
321
  }
395
322
  });
396
- context.on("kubb:diagnostic", ({ diagnostic }) => {
323
+ context.hook("kubb:diagnostic", ({ diagnostic }) => {
397
324
  if (logLevel <= _kubb_core.logLevel.silent && diagnostic.severity !== "error") return;
398
325
  stopSpinner();
399
326
  stopActiveProgress();
@@ -415,7 +342,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
415
342
  secondarySymbol: ""
416
343
  });
417
344
  });
418
- context.on("kubb:lifecycle:start", async ({ version }) => {
345
+ context.hook("kubb:lifecycle:start", async ({ version }) => {
419
346
  console.log(`\n${getIntro({
420
347
  title: "The meta framework for code generation",
421
348
  description: "Ready to start",
@@ -424,14 +351,14 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
424
351
  })}\n`);
425
352
  reset();
426
353
  });
427
- context.on("kubb:generation:start", ({ config }) => {
354
+ context.hook("kubb:generation:start", ({ config }) => {
428
355
  reset();
429
356
  state.totalPlugins = config.plugins?.length ?? 0;
430
357
  if (logLevel <= _kubb_core.logLevel.silent) return;
431
358
  const text = getMessage(["Generation started", config.name ? `for ${(0, node_util.styleText)("dim", config.name)}` : void 0].filter(Boolean).join(" "));
432
359
  _clack_prompts.intro(text);
433
360
  });
434
- context.on("kubb:plugin:start", ({ plugin }) => {
361
+ context.hook("kubb:plugin:start", ({ plugin }) => {
435
362
  if (logLevel <= _kubb_core.logLevel.silent) return;
436
363
  stopSpinner();
437
364
  state.runningPlugins.add(plugin.name);
@@ -449,7 +376,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
449
376
  progressBar.advance(state.completedPlugins + state.failedPlugins, pluginProgressText());
450
377
  state.activeProgress.set("plugins", { progressBar });
451
378
  });
452
- context.on("kubb:plugin:end", ({ plugin, success }) => {
379
+ context.hook("kubb:plugin:end", ({ plugin, success }) => {
453
380
  stopSpinner();
454
381
  const active = state.activeProgress.get("plugins");
455
382
  if (!active || logLevel === _kubb_core.logLevel.silent) return;
@@ -462,7 +389,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
462
389
  showProgressStep();
463
390
  }
464
391
  });
465
- context.on("kubb:files:processing:start", ({ files }) => {
392
+ context.hook("kubb:files:processing:start", ({ files }) => {
466
393
  if (logLevel <= _kubb_core.logLevel.silent) return;
467
394
  stopSpinner();
468
395
  state.totalFiles = files.length;
@@ -473,11 +400,11 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
473
400
  max: files.length,
474
401
  size: 30
475
402
  });
476
- context.emit("kubb:info", { message: text });
403
+ context.callHook("kubb:info", { message: text });
477
404
  progressBar.start(getMessage(text));
478
405
  state.activeProgress.set("files", { progressBar });
479
406
  });
480
- context.on("kubb:files:processing:update", ({ files }) => {
407
+ context.hook("kubb:files:processing:update", ({ files }) => {
481
408
  if (logLevel <= _kubb_core.logLevel.silent) return;
482
409
  stopSpinner();
483
410
  const active = state.activeProgress.get("files");
@@ -486,7 +413,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
486
413
  if (active) active.progressBar.advance(void 0, `Writing ${(0, node_path.relative)(config.root, file.path)}`);
487
414
  }
488
415
  });
489
- context.on("kubb:files:processing:end", () => {
416
+ context.hook("kubb:files:processing:end", () => {
490
417
  if (logLevel <= _kubb_core.logLevel.silent) return;
491
418
  stopSpinner();
492
419
  const text = getMessage("Files written successfully");
@@ -496,7 +423,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
496
423
  state.activeProgress.delete("files");
497
424
  showProgressStep();
498
425
  });
499
- context.on("kubb:generation:end", ({ config }) => {
426
+ context.hook("kubb:generation:end", ({ config }) => {
500
427
  stopSpinner();
501
428
  const text = getMessage(config.name ? `Generation completed for ${(0, node_util.styleText)("dim", config.name)}` : "Generation completed");
502
429
  _clack_prompts.outro(text);
@@ -504,7 +431,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
504
431
  onStep("kubb:format:start", "Formatting");
505
432
  onStep("kubb:lint:start", "Linting");
506
433
  onStep("kubb:hooks:start", "Running hooks");
507
- context.on("kubb:hook:start", ({ id, command, args }) => {
434
+ context.hook("kubb:hook:start", ({ id, command, args }) => {
508
435
  if (logLevel <= _kubb_core.logLevel.silent || !id) return;
509
436
  stopSpinner();
510
437
  const title = getMessage(`Running ${(0, node_util.styleText)("dim", formatCommandWithArgs(command, args))}`);
@@ -514,10 +441,10 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
514
441
  hrStart: node_process.default.hrtime()
515
442
  });
516
443
  });
517
- if (logLevel > _kubb_core.logLevel.silent) context.on("kubb:hook:line", ({ id, line }) => {
444
+ if (logLevel > _kubb_core.logLevel.silent) context.hook("kubb:hook:line", ({ id, line }) => {
518
445
  state.activeHookLogs.get(id)?.taskLog.message((0, node_util.styleText)("dim", line));
519
446
  });
520
- context.on("kubb:hook:end", ({ id, command, args, success, error, stdout, stderr }) => {
447
+ context.hook("kubb:hook:end", ({ id, command, args, success, error, stdout, stderr }) => {
521
448
  if (!id) return;
522
449
  if (logLevel <= _kubb_core.logLevel.silent) {
523
450
  if (!success) {
@@ -537,7 +464,7 @@ Run \`npm install -g @kubb/cli\` to update`, "Update available for `Kubb`", {
537
464
  active.taskLog.error(getMessage(`${(0, node_util.styleText)("dim", commandWithArgs)} failed${reason}`), { showLog: true });
538
465
  }
539
466
  });
540
- context.on("kubb:lifecycle:end", () => {
467
+ context.hook("kubb:lifecycle:end", () => {
541
468
  reset();
542
469
  });
543
470
  }
@@ -555,13 +482,13 @@ const plainLogger = defineLogger({
555
482
  function getMessage(message) {
556
483
  return formatMessage(message, logLevel);
557
484
  }
558
- function onStep(event, message) {
559
- context.on(event, () => {
485
+ function onStep(hook, message) {
486
+ context.hook(hook, () => {
560
487
  if (logLevel <= _kubb_core.logLevel.silent) return;
561
488
  console.log(getMessage(message));
562
489
  });
563
490
  }
564
- context.on("kubb:info", ({ message, info }) => {
491
+ context.hook("kubb:info", ({ message, info }) => {
565
492
  if (logLevel <= _kubb_core.logLevel.silent) return;
566
493
  const text = getMessage([
567
494
  "ℹ",
@@ -570,7 +497,7 @@ const plainLogger = defineLogger({
570
497
  ].join(" "));
571
498
  console.log(text);
572
499
  });
573
- context.on("kubb:success", ({ message, info = "" }) => {
500
+ context.hook("kubb:success", ({ message, info = "" }) => {
574
501
  if (logLevel <= _kubb_core.logLevel.silent) return;
575
502
  const text = getMessage([
576
503
  "✓",
@@ -579,7 +506,7 @@ const plainLogger = defineLogger({
579
506
  ].filter(Boolean).join(" "));
580
507
  console.log(text);
581
508
  });
582
- context.on("kubb:warn", ({ message, info }) => {
509
+ context.hook("kubb:warn", ({ message, info }) => {
583
510
  if (logLevel < _kubb_core.logLevel.warn) return;
584
511
  const text = getMessage([
585
512
  "⚠",
@@ -588,57 +515,55 @@ const plainLogger = defineLogger({
588
515
  ].filter(Boolean).join(" "));
589
516
  console.log(text);
590
517
  });
591
- context.on("kubb:error", ({ error }) => {
592
- const caused = require_errors.toCause(error);
518
+ context.hook("kubb:error", ({ error }) => {
593
519
  const text = getMessage(["✗", error.message].join(" "));
594
520
  console.log(text);
595
- if (logLevel >= _kubb_core.logLevel.verbose && error.stack) {
596
- const frames = error.stack.split("\n").slice(1, 4);
597
- for (const frame of frames) console.log(getMessage(frame.trim()));
598
- if (caused?.stack) {
599
- console.log(`└─ caused by ${caused.message}`);
600
- const frames = caused.stack.split("\n").slice(1, 4);
601
- for (const frame of frames) console.log(getMessage(` ${frame.trim()}`));
521
+ const frames = logLevel >= _kubb_core.logLevel.verbose ? formatErrorFrames(error) : null;
522
+ if (frames) {
523
+ for (const frame of frames.frames) console.log(getMessage(frame));
524
+ if (frames.cause) {
525
+ console.log(frames.cause.header);
526
+ for (const frame of frames.cause.frames) console.log(getMessage(` ${frame}`));
602
527
  }
603
528
  }
604
529
  });
605
- context.on("kubb:diagnostic", ({ diagnostic }) => {
530
+ context.hook("kubb:diagnostic", ({ diagnostic }) => {
606
531
  if (logLevel <= _kubb_core.logLevel.silent && diagnostic.severity !== "error") return;
607
532
  console.log(getMessage(_kubb_core.Diagnostics.formatLines(diagnostic).join("\n")));
608
533
  });
609
- context.on("kubb:lifecycle:start", ({ version }) => {
534
+ context.hook("kubb:lifecycle:start", ({ version }) => {
610
535
  console.log(`Kubb CLI v${version}`);
611
536
  });
612
- context.on("kubb:generation:start", () => {
537
+ context.hook("kubb:generation:start", () => {
613
538
  const text = getMessage("Generation started");
614
539
  console.log(text);
615
540
  });
616
- context.on("kubb:plugin:start", ({ plugin }) => {
541
+ context.hook("kubb:plugin:start", ({ plugin }) => {
617
542
  if (logLevel <= _kubb_core.logLevel.silent) return;
618
543
  const text = getMessage(`Generating ${plugin.name}`);
619
544
  console.log(text);
620
545
  });
621
- context.on("kubb:plugin:end", ({ plugin, duration, success }) => {
546
+ context.hook("kubb:plugin:end", ({ plugin, duration, success }) => {
622
547
  if (logLevel <= _kubb_core.logLevel.silent) return;
623
548
  const durationStr = formatMs(duration);
624
549
  const text = getMessage(success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`);
625
550
  console.log(text);
626
551
  });
627
- context.on("kubb:files:processing:start", ({ files }) => {
552
+ context.hook("kubb:files:processing:start", ({ files }) => {
628
553
  if (logLevel <= _kubb_core.logLevel.silent) return;
629
554
  const text = getMessage(`Writing ${files.length} files`);
630
555
  console.log(text);
631
556
  });
632
- context.on("kubb:files:processing:update", ({ files }) => {
557
+ context.hook("kubb:files:processing:update", ({ files }) => {
633
558
  if (logLevel <= _kubb_core.logLevel.silent) return;
634
559
  for (const { file, config } of files) console.log(getMessage(`Writing ${(0, node_path.relative)(config.root, file.path)}`));
635
560
  });
636
- context.on("kubb:files:processing:end", () => {
561
+ context.hook("kubb:files:processing:end", () => {
637
562
  if (logLevel <= _kubb_core.logLevel.silent) return;
638
563
  const text = getMessage("Files written successfully");
639
564
  console.log(text);
640
565
  });
641
- context.on("kubb:generation:end", ({ config }) => {
566
+ context.hook("kubb:generation:end", ({ config }) => {
642
567
  const text = getMessage(config.name ? `Generation completed for ${config.name}` : "Generation completed");
643
568
  console.log(text);
644
569
  });
@@ -648,13 +573,13 @@ const plainLogger = defineLogger({
648
573
  onStep("kubb:lint:end", "Lint completed");
649
574
  onStep("kubb:hooks:start", "Hooks started");
650
575
  onStep("kubb:hooks:end", "Hooks completed");
651
- context.on("kubb:hook:start", ({ id, command, args }) => {
576
+ context.hook("kubb:hook:start", ({ id, command, args }) => {
652
577
  if (logLevel <= _kubb_core.logLevel.silent) return;
653
578
  if (id) hookTimer.start(id);
654
579
  const commandWithArgs = formatCommandWithArgs(command, args);
655
580
  console.log(getMessage(`Hook ${commandWithArgs} started`));
656
581
  });
657
- context.on("kubb:hook:end", ({ id, command, args, success, error, stdout, stderr }) => {
582
+ context.hook("kubb:hook:end", ({ id, command, args, success, error, stdout, stderr }) => {
658
583
  if (logLevel <= _kubb_core.logLevel.silent) return;
659
584
  const ms = id ? hookTimer.end(id) : void 0;
660
585
  const durationStr = ms !== void 0 ? ` in ${formatMs(ms)}` : "";
@@ -685,6 +610,22 @@ function formatMessage(message, logLevel) {
685
610
  return message;
686
611
  }
687
612
  /**
613
+ * First stack frames for verbose error output, including an optional `cause` chain.
614
+ */
615
+ function formatErrorFrames(error) {
616
+ if (!error.stack) return null;
617
+ const frames = error.stack.split("\n").slice(1, 4).map((frame) => frame.trim());
618
+ const caused = require_errors.toCause(error);
619
+ if (!caused?.stack) return { frames };
620
+ return {
621
+ frames,
622
+ cause: {
623
+ header: `└─ caused by ${caused.message}`,
624
+ frames: caused.stack.split("\n").slice(1, 4).map((frame) => frame.trim())
625
+ }
626
+ };
627
+ }
628
+ /**
688
629
  * Build the progress summary line shown by the clack logger.
689
630
  * Returns null when there is nothing to display.
690
631
  */
@@ -745,9 +686,6 @@ function createHookTimer() {
745
686
  if (!hrStart) return;
746
687
  starts.delete(id);
747
688
  return getElapsedMs(hrStart);
748
- },
749
- clear() {
750
- starts.clear();
751
689
  }
752
690
  };
753
691
  }
@@ -760,20 +698,12 @@ function createHookTimer() {
760
698
  function formatCommandWithArgs(command, args) {
761
699
  return args?.length ? `${command} ${args.join(" ")}` : command;
762
700
  }
763
- function detectLogger() {
764
- if (require_Telemetry.canUseTTY()) return "clack";
765
- return "plain";
766
- }
767
- const logMapper = {
768
- clack: clackLogger,
769
- plain: plainLogger
770
- };
771
701
  /**
772
- * Bridges a {@link Reporter} onto the run's event emitter: calls `report` with each config's
702
+ * Bridges a {@link Reporter} onto the run's hook emitter: calls `report` with each config's
773
703
  * {@link GenerationResult} on `kubb:generation:end`. The reporter never touches the emitter.
774
704
  */
775
705
  function installReporter(context, reporter, ctx) {
776
- context.on("kubb:generation:end", async ({ config, diagnostics = [], filesCreated = 0, status = "success", hrStart = node_process.default.hrtime() }) => {
706
+ context.hook("kubb:generation:end", async ({ config, diagnostics = [], filesCreated = 0, status = "success", hrStart = node_process.default.hrtime() }) => {
777
707
  await reporter.report({
778
708
  config,
779
709
  diagnostics,
@@ -782,7 +712,7 @@ function installReporter(context, reporter, ctx) {
782
712
  hrStart
783
713
  }, ctx);
784
714
  });
785
- if (reporter.drain) context.on("kubb:lifecycle:end", () => reporter.drain?.(ctx));
715
+ if (reporter.drain) context.hook("kubb:lifecycle:end", () => reporter.drain?.(ctx));
786
716
  }
787
717
  /**
788
718
  * Installs the live logger (the TUI view) and the given reporters (the output). The reporters are
@@ -801,10 +731,7 @@ async function setupReporters(context, { logLevel, reporters }) {
801
731
  for (const reporter of reporters) {
802
732
  if (reporter.name === "cli") {
803
733
  if (hasJson) continue;
804
- const type = detectLogger();
805
- const logger = logMapper[type];
806
- if (!logger) throw new Error(`Unknown adapter type: ${type}`);
807
- await logger.install(context, { logLevel });
734
+ await (require_Telemetry.canUseTTY() ? clackLogger : plainLogger).install(context, { logLevel });
808
735
  }
809
736
  installReporter(context, reporter, ctx);
810
737
  }
@@ -915,6 +842,34 @@ async function getConfigs({ configPath, input, watch, logLevel }) {
915
842
  }))
916
843
  };
917
844
  }
845
+ let hookSequence = 0;
846
+ /**
847
+ * Returns a process-unique id that correlates a hook's `kubb:hook:start` and `kubb:hook:end`
848
+ * emissions, which loggers use to key their per-hook state.
849
+ */
850
+ function createHookId() {
851
+ hookSequence += 1;
852
+ return `hook-${hookSequence}`;
853
+ }
854
+ /**
855
+ * Returns `true` when `latest` is a newer semver version than `current`. Compares each numeric
856
+ * part, so `5.10.0` beats `5.9.0` where a plain string comparison would not. Prerelease
857
+ * suffixes are ignored, and a malformed version never reports an update.
858
+ *
859
+ * @example Double-digit minor
860
+ * `isNewerVersion('5.9.0', '5.10.0') // true`
861
+ *
862
+ * @example String comparison would get this wrong the other way
863
+ * `isNewerVersion('5.10.0', '5.9.0') // false`
864
+ */
865
+ function isNewerVersion(current, latest) {
866
+ const release = (value) => value.split("-")[0] ?? "";
867
+ const isNumeric = (value) => /^\d+(\.\d+)*$/.test(value);
868
+ const currentRelease = release(current);
869
+ const latestRelease = release(latest);
870
+ if (!isNumeric(currentRelease) || !isNumeric(latestRelease)) return false;
871
+ return currentRelease.localeCompare(latestRelease, void 0, { numeric: true }) < 0;
872
+ }
918
873
  /**
919
874
  * Tokenizes a shell command string, respecting single and double quotes.
920
875
  *
@@ -928,65 +883,74 @@ function tokenize(command) {
928
883
  return (command.match(/[^\s"']+|"([^"]*)"|'([^']*)'/g) ?? []).map((token) => token.replace(/^["']|["']$/g, ""));
929
884
  }
930
885
  /**
931
- * Runs the `done` hooks defined in a Kubb config in sequence.
886
+ * Runs the `done` hooks defined in a Kubb config in sequence and returns each hook's outcome,
887
+ * so the caller can turn failures into diagnostics.
932
888
  */
933
889
  async function executeHooks({ configHooks, hooks }) {
934
890
  const commands = Array.isArray(configHooks.done) ? configHooks.done : [configHooks.done].filter(Boolean);
891
+ const results = [];
935
892
  for (const command of commands) {
936
893
  const [cmd, ...args] = tokenize(command);
937
894
  if (!cmd) continue;
938
- const hookId = (0, node_crypto.hash)("sha256", command, "hex");
895
+ const hookId = createHookId();
939
896
  const commandWithArgs = [cmd, ...args].join(" ");
940
- await hooks.emit("kubb:hook:start", {
897
+ await hooks.callHook("kubb:hook:start", {
941
898
  id: hookId,
942
899
  command: cmd,
943
900
  args
944
901
  });
945
- await runHook({
902
+ results.push(await runHook({
946
903
  id: hookId,
947
904
  command: cmd,
948
905
  args,
949
906
  commandWithArgs,
950
907
  hooks
951
- });
908
+ }));
952
909
  }
910
+ return results;
953
911
  }
954
912
  /**
955
- * Spawns a hook command and reports its outcome through `kubb:hook:end`.
956
- * A non-zero exit signals failure via `kubb:hook:end` rather than throwing, so the caller can turn
913
+ * Spawns a hook command and returns its outcome, mirroring it through `kubb:hook:end` for the
914
+ * loggers. A non-zero exit returns `success: false` rather than throwing, so the caller can turn
957
915
  * it into a diagnostic. Other spawn errors do the same. Output is streamed through `kubb:hook:line`
958
916
  * only while a listener is attached.
959
917
  */
960
918
  async function runHook({ id, command, args, commandWithArgs, hooks }) {
961
- const emitEnd = (success, error, output) => hooks.emit("kubb:hook:end", {
962
- command,
963
- args,
964
- id,
965
- success,
966
- error,
967
- ...output
968
- });
919
+ const emitEnd = async (result) => {
920
+ await hooks.callHook("kubb:hook:end", {
921
+ command,
922
+ args,
923
+ id,
924
+ ...result
925
+ });
926
+ return result;
927
+ };
969
928
  const stream = hooks.listenerCount("kubb:hook:line") > 0;
970
929
  try {
971
930
  const proc = (0, tinyexec.x)(command, [...args ?? []], {
972
931
  nodeOptions: { detached: node_process.default.platform !== "win32" },
973
932
  throwOnError: true
974
933
  });
975
- if (stream) for await (const line of proc) await hooks.emit("kubb:hook:line", {
934
+ if (stream) for await (const line of proc) await hooks.callHook("kubb:hook:line", {
976
935
  id,
977
936
  line
978
937
  });
979
938
  await proc;
980
- await hooks.emit("kubb:success", { message: `${(0, node_util.styleText)("dim", commandWithArgs)} successfully executed` });
981
- await emitEnd(true, null);
939
+ await hooks.callHook("kubb:success", { message: `${(0, node_util.styleText)("dim", commandWithArgs)} successfully executed` });
940
+ return emitEnd({
941
+ success: true,
942
+ error: null
943
+ });
982
944
  } catch (err) {
983
- if (!(err instanceof tinyexec.NonZeroExitError)) {
984
- await emitEnd(false, require_errors.toError(err));
985
- return;
986
- }
945
+ if (!(err instanceof tinyexec.NonZeroExitError)) return emitEnd({
946
+ success: false,
947
+ error: require_errors.toError(err)
948
+ });
987
949
  const stderr = err.output?.stderr ?? "";
988
950
  const stdout = err.output?.stdout ?? "";
989
- await emitEnd(false, /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`), {
951
+ return emitEnd({
952
+ success: false,
953
+ error: /* @__PURE__ */ new Error(`Hook execute failed: ${commandWithArgs}`),
990
954
  stdout,
991
955
  stderr
992
956
  });
@@ -994,7 +958,9 @@ async function runHook({ id, command, args, commandWithArgs, hooks }) {
994
958
  }
995
959
  /**
996
960
  * Starts a file watcher on the given paths and calls `cb` on any change.
997
- * Ignores `.git` and `node_modules` directories.
961
+ * Ignores `.git` and `node_modules` directories. Event bursts (an editor save emits several)
962
+ * are debounced into one build, and builds never overlap: changes during a build queue exactly
963
+ * one rebuild.
998
964
  */
999
965
  async function startWatcher(path, cb, log = {
1000
966
  info: console.log,
@@ -1003,7 +969,8 @@ async function startWatcher(path, cb, log = {
1003
969
  const { watch } = await import("chokidar");
1004
970
  const watcher = watch(path, {
1005
971
  ignorePermissionErrors: true,
1006
- ignored: require_Telemetry.WATCHER_IGNORED_PATHS
972
+ ignored: require_Telemetry.WATCHER_IGNORED_PATHS,
973
+ ignoreInitial: true
1007
974
  });
1008
975
  node_process.default.once("SIGINT", () => {
1009
976
  watcher.close();
@@ -1011,78 +978,70 @@ async function startWatcher(path, cb, log = {
1011
978
  node_process.default.once("SIGTERM", () => {
1012
979
  watcher.close();
1013
980
  });
1014
- watcher.on("all", async (type, file) => {
981
+ const runBuild = createSerialRunner({
982
+ run: () => cb(path),
983
+ onError: () => log.error((0, node_util.styleText)("red", "Watcher failed"))
984
+ });
985
+ let debounceTimer = null;
986
+ watcher.on("all", (type, file) => {
1015
987
  log.info((0, node_util.styleText)("yellow", (0, node_util.styleText)("bold", `Change detected: ${type} ${file}`)));
1016
- try {
1017
- await cb(path);
1018
- } catch (_e) {
1019
- log.error((0, node_util.styleText)("red", "Watcher failed"));
1020
- }
988
+ if (debounceTimer) clearTimeout(debounceTimer);
989
+ debounceTimer = setTimeout(() => {
990
+ debounceTimer = null;
991
+ runBuild();
992
+ }, 100);
1021
993
  });
1022
994
  }
1023
995
  //#endregion
1024
996
  //#region src/runners/generate/run.ts
1025
997
  /**
1026
- * Registers a one-shot `kubb:hook:end` listener for `hookId` before the caller emits `kubb:hook:start`,
1027
- * so a synchronous emitter cannot fire end before the listener is attached.
998
+ * Runs one formatter or linter pass over the output directory. Returns the failure instead of
999
+ * throwing, so the caller can turn it into a coded diagnostic. Failures never render here:
1000
+ * the caller emits them through `Diagnostics.emit`, like every other diagnostic.
1028
1001
  */
1029
- function waitForHookEnd(hooks, hookId, onSuccess, fallbackErrorMessage) {
1030
- return new Promise((resolve, reject) => {
1031
- const handler = (ctx) => {
1032
- if (ctx.id !== hookId) return;
1033
- hooks.off("kubb:hook:end", handler);
1034
- if (!ctx.success) {
1035
- reject(ctx.error ?? new Error(fallbackErrorMessage));
1036
- return;
1037
- }
1038
- Promise.resolve(onSuccess()).then(resolve).catch(reject);
1039
- };
1040
- hooks.on("kubb:hook:end", handler);
1041
- });
1042
- }
1043
- async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefix, noToolMessage, configName, outputPath, logLevel, hooks, onStart, onEnd }) {
1002
+ async function runToolPass({ toolValue, detect, toolMap, toolLabel, successPrefix, noToolMessage, outputPath, logLevel, hooks, onStart, onEnd }) {
1044
1003
  await onStart();
1045
1004
  let resolvedTool = toolValue;
1046
1005
  if (resolvedTool === "auto") {
1047
1006
  const detected = await detect();
1048
- if (!detected) await hooks.emit("kubb:warn", { message: noToolMessage });
1007
+ if (!detected) await hooks.callHook("kubb:warn", { message: noToolMessage });
1049
1008
  else {
1050
1009
  resolvedTool = detected;
1051
- await hooks.emit("kubb:info", { message: `Auto-detected ${toolLabel}: ${(0, node_util.styleText)("dim", resolvedTool)}` });
1010
+ await hooks.callHook("kubb:info", { message: `Auto-detected ${toolLabel}: ${(0, node_util.styleText)("dim", resolvedTool)}` });
1052
1011
  }
1053
1012
  }
1054
- let toolError;
1013
+ let toolError = null;
1055
1014
  if (resolvedTool && resolvedTool !== "auto" && resolvedTool in toolMap && (0, node_fs.existsSync)(outputPath)) {
1056
1015
  const toolConfig = toolMap[resolvedTool];
1057
- const hookId = (0, node_crypto.hash)("sha256", [configName, resolvedTool].filter(Boolean).join("-"), "hex");
1058
1016
  const successMessage = [
1059
1017
  `${successPrefix} with ${(0, node_util.styleText)("dim", resolvedTool)}`,
1060
1018
  logLevel >= _kubb_core.logLevel.info ? `on ${(0, node_util.styleText)("dim", outputPath)}` : void 0,
1061
1019
  "successfully"
1062
1020
  ].filter(Boolean).join(" ");
1063
1021
  try {
1022
+ const hookId = createHookId();
1064
1023
  const hookArgs = toolConfig.args(outputPath);
1065
1024
  const commandWithArgs = [toolConfig.command, ...hookArgs].join(" ");
1066
- const hookEndPromise = waitForHookEnd(hooks, hookId, () => hooks.emit("kubb:success", { message: successMessage }), toolConfig.errorMessage);
1067
- await hooks.emit("kubb:hook:start", {
1025
+ await hooks.callHook("kubb:hook:start", {
1068
1026
  id: hookId,
1069
1027
  command: toolConfig.command,
1070
1028
  args: hookArgs
1071
1029
  });
1072
- runHook({
1030
+ const result = await runHook({
1073
1031
  id: hookId,
1074
1032
  command: toolConfig.command,
1075
1033
  args: hookArgs,
1076
1034
  commandWithArgs,
1077
1035
  hooks
1078
- }).catch(() => {});
1079
- await hookEndPromise;
1036
+ });
1037
+ if (result.success) await hooks.callHook("kubb:success", { message: successMessage });
1038
+ else toolError = result.error ?? new Error(toolConfig.errorMessage);
1080
1039
  } catch (caughtError) {
1081
1040
  toolError = require_errors.toError(caughtError);
1082
1041
  }
1083
1042
  }
1084
1043
  await onEnd();
1085
- if (toolError) throw toolError;
1044
+ return toolError;
1086
1045
  }
1087
1046
  async function generate(options) {
1088
1047
  const { input, hooks, logLevel } = options;
@@ -1093,22 +1052,21 @@ async function generate(options) {
1093
1052
  input: inputPath ? {
1094
1053
  ...options.config.input,
1095
1054
  path: inputPath
1096
- } : options.config.input,
1097
- ...options.config.output
1055
+ } : options.config.input
1098
1056
  };
1099
1057
  const kubb = (0, _kubb_core.createKubb)(config, { hooks });
1100
- await hooks.emit("kubb:generation:start", { config });
1101
- await hooks.emit("kubb:info", {
1058
+ await hooks.callHook("kubb:generation:start", { config });
1059
+ await hooks.callHook("kubb:info", {
1102
1060
  message: config.name ? `Setup generation ${(0, node_util.styleText)("bold", config.name)}` : "Setup generation",
1103
1061
  info: inputPath
1104
1062
  });
1105
1063
  await kubb.setup();
1106
- await hooks.emit("kubb:info", {
1064
+ await hooks.callHook("kubb:info", {
1107
1065
  message: config.name ? `Build generation ${(0, node_util.styleText)("bold", config.name)}` : "Build generation",
1108
1066
  info: inputPath
1109
1067
  });
1110
1068
  const { files, diagnostics, driver } = await kubb.safeBuild();
1111
- await hooks.emit("kubb:info", { message: "Load summary" });
1069
+ await hooks.callHook("kubb:info", { message: "Load summary" });
1112
1070
  const telemetryPlugins = Array.from(driver.plugins.values(), (p) => ({
1113
1071
  name: p.name,
1114
1072
  options: p.options
@@ -1124,11 +1082,11 @@ async function generate(options) {
1124
1082
  for (const diagnostic of diagnostics) {
1125
1083
  if (!_kubb_core.Diagnostics.isProblem(diagnostic)) continue;
1126
1084
  const unknown = _kubb_core.Diagnostics.narrow(diagnostic, _kubb_core.Diagnostics.code.unknown);
1127
- if (unknown) await hooks.emit("kubb:error", { error: unknown.cause ?? new Error(unknown.message) });
1085
+ if (unknown) await hooks.callHook("kubb:error", { error: unknown.cause ?? new Error(unknown.message) });
1128
1086
  else await _kubb_core.Diagnostics.emit(hooks, diagnostic);
1129
1087
  }
1130
1088
  if (_kubb_core.Diagnostics.hasError(diagnostics)) {
1131
- await hooks.emit("kubb:generation:end", {
1089
+ await hooks.callHook("kubb:generation:end", {
1132
1090
  config,
1133
1091
  storage: kubb.storage,
1134
1092
  diagnostics,
@@ -1141,77 +1099,70 @@ async function generate(options) {
1141
1099
  }
1142
1100
  const outputPath = node_path.default.resolve(config.root, config.output.path);
1143
1101
  const outputDiagnostics = [];
1144
- const toolPasses = [config.output.format && {
1145
- code: _kubb_core.Diagnostics.code.formatFailed,
1146
- toolValue: config.output.format,
1147
- detect: () => require_tools.detectTool([
1148
- "oxfmt",
1149
- "biome",
1150
- "prettier"
1151
- ]),
1152
- toolMap: require_tools.formatters,
1153
- toolLabel: "formatter",
1154
- successPrefix: "Formatting",
1155
- noToolMessage: "No formatter found (oxfmt, biome, or prettier). Skipping formatting.",
1156
- onStart: () => hooks.emit("kubb:format:start"),
1157
- onEnd: () => hooks.emit("kubb:format:end")
1158
- }, config.output.lint && {
1159
- code: _kubb_core.Diagnostics.code.lintFailed,
1160
- toolValue: config.output.lint,
1161
- detect: () => require_tools.detectTool([
1162
- "oxlint",
1163
- "biome",
1164
- "eslint"
1165
- ]),
1166
- toolMap: require_tools.linters,
1167
- toolLabel: "linter",
1168
- successPrefix: "Linting",
1169
- noToolMessage: "No linter found (oxlint, biome, or eslint). Skipping linting.",
1170
- onStart: () => hooks.emit("kubb:lint:start"),
1171
- onEnd: () => hooks.emit("kubb:lint:end")
1172
- }].filter(Boolean);
1173
- for (const { code, ...pass } of toolPasses) try {
1174
- await runToolPass({
1175
- ...pass,
1176
- configName: config.name,
1102
+ const reportOutputFailure = async (code, label, error) => {
1103
+ const diagnostic = outputDiagnostic(code, label, error);
1104
+ outputDiagnostics.push(diagnostic);
1105
+ await _kubb_core.Diagnostics.emit(hooks, diagnostic);
1106
+ };
1107
+ if (config.output.format) {
1108
+ const error = await runToolPass({
1109
+ toolValue: config.output.format,
1110
+ detect: () => require_tools.detectTool([
1111
+ "oxfmt",
1112
+ "biome",
1113
+ "prettier"
1114
+ ]),
1115
+ toolMap: require_tools.formatters,
1116
+ toolLabel: "formatter",
1117
+ successPrefix: "Formatting",
1118
+ noToolMessage: "No formatter found (oxfmt, biome, or prettier). Skipping formatting.",
1119
+ onStart: () => hooks.callHook("kubb:format:start"),
1120
+ onEnd: () => hooks.callHook("kubb:format:end"),
1177
1121
  outputPath,
1178
1122
  logLevel,
1179
1123
  hooks
1180
1124
  });
1181
- } catch (caughtError) {
1182
- const diagnostic = outputDiagnostic(code, pass.toolLabel, caughtError);
1183
- outputDiagnostics.push(diagnostic);
1184
- await _kubb_core.Diagnostics.emit(hooks, diagnostic);
1125
+ if (error) await reportOutputFailure(_kubb_core.Diagnostics.code.formatFailed, "formatter", error);
1126
+ }
1127
+ if (config.output.lint) {
1128
+ const error = await runToolPass({
1129
+ toolValue: config.output.lint,
1130
+ detect: () => require_tools.detectTool([
1131
+ "oxlint",
1132
+ "biome",
1133
+ "eslint"
1134
+ ]),
1135
+ toolMap: require_tools.linters,
1136
+ toolLabel: "linter",
1137
+ successPrefix: "Linting",
1138
+ noToolMessage: "No linter found (oxlint, biome, or eslint). Skipping linting.",
1139
+ onStart: () => hooks.callHook("kubb:lint:start"),
1140
+ onEnd: () => hooks.callHook("kubb:lint:end"),
1141
+ outputPath,
1142
+ logLevel,
1143
+ hooks
1144
+ });
1145
+ if (error) await reportOutputFailure(_kubb_core.Diagnostics.code.lintFailed, "linter", error);
1185
1146
  }
1186
1147
  if (config.hooks) {
1187
- await hooks.emit("kubb:hooks:start");
1188
- const hookFailures = [];
1189
- const onHookEnd = (ctx) => {
1190
- if (!ctx.success) hookFailures.push(ctx.error ?? /* @__PURE__ */ new Error("Post-generate hook failed"));
1191
- };
1192
- hooks.on("kubb:hook:end", onHookEnd);
1193
- try {
1194
- await executeHooks({
1195
- configHooks: config.hooks,
1196
- hooks
1197
- });
1198
- } finally {
1199
- hooks.off("kubb:hook:end", onHookEnd);
1200
- }
1201
- for (const error of hookFailures) {
1202
- const diagnostic = outputDiagnostic(_kubb_core.Diagnostics.code.hookFailed, "Post-generate hook", error);
1203
- outputDiagnostics.push(diagnostic);
1204
- await _kubb_core.Diagnostics.emit(hooks, diagnostic);
1148
+ await hooks.callHook("kubb:hooks:start");
1149
+ const hookResults = await executeHooks({
1150
+ configHooks: config.hooks,
1151
+ hooks
1152
+ });
1153
+ for (const result of hookResults) {
1154
+ if (result.success) continue;
1155
+ await reportOutputFailure(_kubb_core.Diagnostics.code.hookFailed, "Post-generate hook", result.error ?? /* @__PURE__ */ new Error("Post-generate hook failed"));
1205
1156
  }
1206
- await hooks.emit("kubb:hooks:end");
1157
+ await hooks.callHook("kubb:hooks:end");
1207
1158
  }
1208
1159
  const finalDiagnostics = [...diagnostics, ...outputDiagnostics];
1209
1160
  const failed = _kubb_core.Diagnostics.hasError(outputDiagnostics);
1210
- if (!failed) await hooks.emit("kubb:success", {
1161
+ if (!failed) await hooks.callHook("kubb:success", {
1211
1162
  message: "Generation succeeded",
1212
1163
  info: inputPath
1213
1164
  });
1214
- await hooks.emit("kubb:generation:end", {
1165
+ await hooks.callHook("kubb:generation:end", {
1215
1166
  config,
1216
1167
  storage: kubb.storage,
1217
1168
  diagnostics: finalDiagnostics,
@@ -1238,8 +1189,8 @@ function outputDiagnostic(code, label, caughtError) {
1238
1189
  }
1239
1190
  async function checkForUpdate(hooks) {
1240
1191
  try {
1241
- const data = await (await fetch(require_Telemetry.KUBB_NPM_PACKAGE_URL)).json();
1242
- if (data.version && require_package.version < data.version) await _kubb_core.Diagnostics.emit(hooks, _kubb_core.Diagnostics.update({
1192
+ const data = await (await fetch(require_Telemetry.KUBB_NPM_PACKAGE_URL, { signal: AbortSignal.timeout(require_Telemetry.UPDATE_CHECK_TIMEOUT_MS) })).json();
1193
+ if (data.version && isNewerVersion(require_package.version, data.version)) await _kubb_core.Diagnostics.emit(hooks, _kubb_core.Diagnostics.update({
1243
1194
  currentVersion: require_package.version,
1244
1195
  latestVersion: data.version
1245
1196
  }));
@@ -1252,7 +1203,7 @@ async function checkForUpdate(hooks) {
1252
1203
  */
1253
1204
  async function run({ input, configPath, logLevel: logLevelKey, watch, reporters: cliReporters }) {
1254
1205
  const logLevel = _kubb_core.logLevel[logLevelKey] ?? _kubb_core.logLevel.info;
1255
- const hooks = new AsyncEventEmitter();
1206
+ const hooks = new _kubb_core.Hookable();
1256
1207
  let configs;
1257
1208
  let resolvedConfigPath;
1258
1209
  try {
@@ -1269,7 +1220,7 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1269
1220
  logLevel,
1270
1221
  reporters: [_kubb_core.cliReporter]
1271
1222
  });
1272
- await hooks.emit("kubb:error", { error: require_errors.toError(error) });
1223
+ await hooks.callHook("kubb:error", { error: require_errors.toError(error) });
1273
1224
  node_process.default.exit(1);
1274
1225
  }
1275
1226
  const requestedNames = cliReporters?.length ? cliReporters : ["cli"];
@@ -1277,32 +1228,40 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1277
1228
  logLevel,
1278
1229
  reporters: selectReporters(configs[0]?.reporters ?? [], requestedNames)
1279
1230
  });
1280
- await hooks.emit("kubb:lifecycle:start", { version: require_package.version });
1231
+ await hooks.callHook("kubb:lifecycle:start", { version: require_package.version });
1281
1232
  await checkForUpdate(hooks);
1282
1233
  try {
1283
1234
  const relativeConfigPath = node_path.default.relative(node_process.default.cwd(), resolvedConfigPath);
1284
- await hooks.emit("kubb:info", {
1235
+ await hooks.callHook("kubb:info", {
1285
1236
  message: "Config loaded",
1286
1237
  info: relativeConfigPath
1287
1238
  });
1288
- await hooks.emit("kubb:success", {
1239
+ await hooks.callHook("kubb:success", {
1289
1240
  message: "Config loaded successfully",
1290
1241
  info: relativeConfigPath
1291
1242
  });
1292
1243
  let anyFailed = false;
1293
- for (const config of configs) if (config.input && "path" in config.input && watch) await startWatcher([input || config.input.path], async (paths) => {
1294
- await generate({
1295
- input,
1296
- config,
1297
- logLevel,
1298
- hooks
1244
+ for (const config of configs) if (config.input && "path" in config.input && watch) {
1245
+ const watchedPaths = [input || config.input.path];
1246
+ const build = async (paths) => {
1247
+ await generate({
1248
+ input,
1249
+ config,
1250
+ logLevel,
1251
+ hooks
1252
+ });
1253
+ _clack_prompts.log.step((0, node_util.styleText)("yellow", `Watching for changes in ${paths.join(" and ")}`));
1254
+ };
1255
+ try {
1256
+ await build(watchedPaths);
1257
+ } catch (buildError) {
1258
+ await hooks.callHook("kubb:error", { error: require_errors.toError(buildError) });
1259
+ }
1260
+ await startWatcher(watchedPaths, build, {
1261
+ info: (msg) => _clack_prompts.log.info(msg),
1262
+ error: (msg) => _clack_prompts.log.error(msg)
1299
1263
  });
1300
- _clack_prompts.log.step((0, node_util.styleText)("yellow", `Watching for changes in ${paths.join(" and ")}`));
1301
- }, {
1302
- info: (msg) => _clack_prompts.log.info(msg),
1303
- error: (msg) => _clack_prompts.log.error(msg)
1304
- });
1305
- else try {
1264
+ } else try {
1306
1265
  if (!await generate({
1307
1266
  input,
1308
1267
  config,
@@ -1310,17 +1269,17 @@ async function run({ input, configPath, logLevel: logLevelKey, watch, reporters:
1310
1269
  hooks
1311
1270
  })) anyFailed = true;
1312
1271
  } catch (configError) {
1313
- await hooks.emit("kubb:error", { error: require_errors.toError(configError) });
1272
+ await hooks.callHook("kubb:error", { error: require_errors.toError(configError) });
1314
1273
  anyFailed = true;
1315
1274
  }
1316
- await hooks.emit("kubb:lifecycle:end");
1275
+ await hooks.callHook("kubb:lifecycle:end");
1317
1276
  if (anyFailed) node_process.default.exit(1);
1318
1277
  } catch (error) {
1319
- await hooks.emit("kubb:error", { error: require_errors.toError(error) });
1278
+ await hooks.callHook("kubb:error", { error: require_errors.toError(error) });
1320
1279
  node_process.default.exit(1);
1321
1280
  }
1322
1281
  }
1323
1282
  //#endregion
1324
1283
  exports.run = run;
1325
1284
 
1326
- //# sourceMappingURL=run-ChSqYuc1.cjs.map
1285
+ //# sourceMappingURL=run-lAxAF-4t.cjs.map