@kubb/cli 5.0.0-beta.4 → 5.0.0-beta.40

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 (155) hide show
  1. package/README.md +198 -36
  2. package/dist/agent-DQerNyd8.cjs +70 -0
  3. package/dist/agent-DQerNyd8.cjs.map +1 -0
  4. package/dist/agent-ZKJkTTGN.js +68 -0
  5. package/dist/agent-ZKJkTTGN.js.map +1 -0
  6. package/dist/{chunk--u3MIqq1.js → chunk-CRm0XQPb.js} +1 -0
  7. package/dist/constants-84a47qA-.js +35 -0
  8. package/dist/constants-84a47qA-.js.map +1 -0
  9. package/dist/constants-AHhyFH15.cjs +139 -0
  10. package/dist/constants-AHhyFH15.cjs.map +1 -0
  11. package/dist/constants-BtmponZ3.cjs +58 -0
  12. package/dist/constants-BtmponZ3.cjs.map +1 -0
  13. package/dist/constants-C94RKp3A.js +116 -0
  14. package/dist/constants-C94RKp3A.js.map +1 -0
  15. package/dist/{define-Bdn8j5VM.cjs → define-C4AB3POr.cjs} +2 -2
  16. package/dist/{define-Bdn8j5VM.cjs.map → define-C4AB3POr.cjs.map} +1 -1
  17. package/dist/{define-Ctii4bel.js → define-DNG1U8ha.js} +2 -2
  18. package/dist/{define-Ctii4bel.js.map → define-DNG1U8ha.js.map} +1 -1
  19. package/dist/{errors-CjPmyZHy.js → errors-CoxrNXaA.js} +2 -2
  20. package/dist/{errors-CjPmyZHy.js.map → errors-CoxrNXaA.js.map} +1 -1
  21. package/dist/{errors-CLCjoSg0.cjs → errors-DykI11xo.cjs} +2 -2
  22. package/dist/{errors-CLCjoSg0.cjs.map → errors-DykI11xo.cjs.map} +1 -1
  23. package/dist/generate-40x9PP4o.js +77 -0
  24. package/dist/generate-40x9PP4o.js.map +1 -0
  25. package/dist/generate-DQLvFw4z.cjs +76 -0
  26. package/dist/generate-DQLvFw4z.cjs.map +1 -0
  27. package/dist/index.cjs +23 -14
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +23 -14
  31. package/dist/index.js.map +1 -1
  32. package/dist/init-CT9RChdK.js +53 -0
  33. package/dist/init-CT9RChdK.js.map +1 -0
  34. package/dist/init-sEaUN7Dj.cjs +53 -0
  35. package/dist/init-sEaUN7Dj.cjs.map +1 -0
  36. package/dist/mcp-Siyb6fTT.cjs +39 -0
  37. package/dist/mcp-Siyb6fTT.cjs.map +1 -0
  38. package/dist/mcp-cjPrOeot.js +39 -0
  39. package/dist/mcp-cjPrOeot.js.map +1 -0
  40. package/dist/{package-BapVyQ-w.cjs → package-BJ6qam2Y.cjs} +2 -2
  41. package/dist/package-BJ6qam2Y.cjs.map +1 -0
  42. package/dist/package-CR5vEK4K.js +6 -0
  43. package/dist/package-CR5vEK4K.js.map +1 -0
  44. package/dist/{agent-sdYBBgrd.js → run-BQ3Qj0xB.js} +46 -43
  45. package/dist/run-BQ3Qj0xB.js.map +1 -0
  46. package/dist/run-BQzoaxjR.js +32 -0
  47. package/dist/run-BQzoaxjR.js.map +1 -0
  48. package/dist/run-CGf0KEts.js +51 -0
  49. package/dist/run-CGf0KEts.js.map +1 -0
  50. package/dist/{generate-CNrRLY4n.js → run-CHZKHTv0.js} +832 -828
  51. package/dist/run-CHZKHTv0.js.map +1 -0
  52. package/dist/{init-CZ5Xq2Hd.cjs → run-C_NMctua.cjs} +107 -149
  53. package/dist/run-C_NMctua.cjs.map +1 -0
  54. package/dist/{generate-B1Pa2ho-.cjs → run-CcQawFNK.cjs} +784 -780
  55. package/dist/run-CcQawFNK.cjs.map +1 -0
  56. package/dist/run-CkTpemme.cjs +52 -0
  57. package/dist/run-CkTpemme.cjs.map +1 -0
  58. package/dist/run-Cl4SrSob.cjs +33 -0
  59. package/dist/run-Cl4SrSob.cjs.map +1 -0
  60. package/dist/{agent-B4cAAab2.cjs → run-D-s2LdlW.cjs} +46 -43
  61. package/dist/run-D-s2LdlW.cjs.map +1 -0
  62. package/dist/{init-eNRlotJK.js → run-D8dCWepS.js} +107 -149
  63. package/dist/run-D8dCWepS.js.map +1 -0
  64. package/dist/{shell-DLzN4fRo.js → shell-BrqyJdB7.js} +2 -2
  65. package/dist/{shell-DLzN4fRo.js.map → shell-BrqyJdB7.js.map} +1 -1
  66. package/dist/{shell-475fQKaX.cjs → shell-Lh-vLWwH.cjs} +2 -2
  67. package/dist/{shell-475fQKaX.cjs.map → shell-Lh-vLWwH.cjs.map} +1 -1
  68. package/dist/validate-8pgfxUTy.js +26 -0
  69. package/dist/validate-8pgfxUTy.js.map +1 -0
  70. package/dist/validate-CstV7Pc2.cjs +26 -0
  71. package/dist/validate-CstV7Pc2.cjs.map +1 -0
  72. package/package.json +16 -15
  73. package/src/commands/agent/start.ts +10 -7
  74. package/src/commands/agent.ts +3 -1
  75. package/src/commands/generate.ts +21 -13
  76. package/src/commands/init.ts +34 -3
  77. package/src/commands/mcp.ts +28 -4
  78. package/src/commands/validate.ts +6 -4
  79. package/src/constants.ts +3 -74
  80. package/src/index.ts +6 -4
  81. package/src/loggers/clackLogger.ts +143 -181
  82. package/src/loggers/githubActionsLogger.ts +119 -121
  83. package/src/loggers/plainLogger.ts +50 -100
  84. package/src/loggers/types.ts +6 -0
  85. package/src/loggers/utils.ts +190 -18
  86. package/src/runners/agent/run.ts +113 -0
  87. package/src/runners/agent/utils.ts +98 -0
  88. package/src/runners/generate/run.ts +411 -0
  89. package/src/runners/generate/utils.ts +222 -0
  90. package/src/runners/init/run.ts +212 -0
  91. package/src/{utils/packageManager.ts → runners/init/utils.ts} +12 -2
  92. package/src/runners/mcp/run.ts +37 -0
  93. package/src/runners/validate/run.ts +63 -0
  94. package/dist/agent-B4cAAab2.cjs.map +0 -1
  95. package/dist/agent-CR6Z96og.js +0 -56
  96. package/dist/agent-CR6Z96og.js.map +0 -1
  97. package/dist/agent-Dmxzqg4d.cjs +0 -58
  98. package/dist/agent-Dmxzqg4d.cjs.map +0 -1
  99. package/dist/agent-sdYBBgrd.js.map +0 -1
  100. package/dist/constants-CnDXa1R6.cjs +0 -148
  101. package/dist/constants-CnDXa1R6.cjs.map +0 -1
  102. package/dist/constants-aL3CP_Wq.js +0 -95
  103. package/dist/constants-aL3CP_Wq.js.map +0 -1
  104. package/dist/generate-B1Pa2ho-.cjs.map +0 -1
  105. package/dist/generate-BDGOOsBM.cjs +0 -65
  106. package/dist/generate-BDGOOsBM.cjs.map +0 -1
  107. package/dist/generate-CNrRLY4n.js.map +0 -1
  108. package/dist/generate-DuhxPLGr.js +0 -66
  109. package/dist/generate-DuhxPLGr.js.map +0 -1
  110. package/dist/init-CZ5Xq2Hd.cjs.map +0 -1
  111. package/dist/init-CnZXHrbq.js +0 -25
  112. package/dist/init-CnZXHrbq.js.map +0 -1
  113. package/dist/init-NYJSZJSb.cjs +0 -25
  114. package/dist/init-NYJSZJSb.cjs.map +0 -1
  115. package/dist/init-eNRlotJK.js.map +0 -1
  116. package/dist/mcp-CYOgxB82.cjs +0 -47
  117. package/dist/mcp-CYOgxB82.cjs.map +0 -1
  118. package/dist/mcp-CdFWyrwi.cjs +0 -16
  119. package/dist/mcp-CdFWyrwi.cjs.map +0 -1
  120. package/dist/mcp-DhSxuDMD.js +0 -16
  121. package/dist/mcp-DhSxuDMD.js.map +0 -1
  122. package/dist/mcp-DmJm3TrU.js +0 -46
  123. package/dist/mcp-DmJm3TrU.js.map +0 -1
  124. package/dist/package-BapVyQ-w.cjs.map +0 -1
  125. package/dist/package-DyJE-qNq.js +0 -6
  126. package/dist/package-DyJE-qNq.js.map +0 -1
  127. package/dist/telemetry-DN95_2pF.cjs +0 -282
  128. package/dist/telemetry-DN95_2pF.cjs.map +0 -1
  129. package/dist/telemetry-LgT_sdPe.js +0 -245
  130. package/dist/telemetry-LgT_sdPe.js.map +0 -1
  131. package/dist/validate-C6npXzel.cjs +0 -25
  132. package/dist/validate-C6npXzel.cjs.map +0 -1
  133. package/dist/validate-kLJoT_hi.js +0 -33
  134. package/dist/validate-kLJoT_hi.js.map +0 -1
  135. package/dist/validate-n38Rh-Y7.js +0 -25
  136. package/dist/validate-n38Rh-Y7.js.map +0 -1
  137. package/dist/validate-yKKzqEZ5.cjs +0 -34
  138. package/dist/validate-yKKzqEZ5.cjs.map +0 -1
  139. package/src/loggers/fileSystemLogger.ts +0 -138
  140. package/src/runners/agent.ts +0 -155
  141. package/src/runners/generate.ts +0 -333
  142. package/src/runners/init.ts +0 -296
  143. package/src/runners/mcp.ts +0 -51
  144. package/src/runners/validate.ts +0 -39
  145. package/src/types.ts +0 -11
  146. package/src/utils/Writables.ts +0 -17
  147. package/src/utils/executeHooks.ts +0 -45
  148. package/src/utils/flags.ts +0 -9
  149. package/src/utils/getConfig.ts +0 -10
  150. package/src/utils/getCosmiConfig.ts +0 -80
  151. package/src/utils/getSummary.ts +0 -68
  152. package/src/utils/runHook.ts +0 -91
  153. package/src/utils/telemetry.ts +0 -273
  154. package/src/utils/watcher.ts +0 -19
  155. /package/dist/{chunk-ByKO4r7w.cjs → chunk-Bx3C2hgW.cjs} +0 -0
@@ -2,12 +2,9 @@ import { relative } from 'node:path'
2
2
  import process from 'node:process'
3
3
  import { styleText } from 'node:util'
4
4
  import * as clack from '@clack/prompts'
5
- import { formatMs, formatMsWithColor, getIntro, toCause } from '@internals/utils'
6
- import { defineLogger, logLevel as logLevelMap } from '@kubb/core'
7
- import { getSummary } from '../utils/getSummary.ts'
8
- import { runHook } from '../utils/runHook.ts'
9
- import { ClackWritable } from '../utils/Writables.ts'
10
- import { buildProgressLine, formatCommandWithArgs, formatMessage } from './utils.ts'
5
+ import { formatMsWithColor, getElapsedMs, getIntro, toCause } from '@internals/utils'
6
+ import { defineLogger, Diagnostics, type KubbHooks, logLevel as logLevelMap } from '@kubb/core'
7
+ import { buildProgressLine, createProgressCounters, formatCommandWithArgs, formatMessage, recordPluginResult, resetProgressCounters } from './utils.ts'
11
8
 
12
9
  /**
13
10
  * TTY logger with beautiful UI and progress indicators for local development.
@@ -17,34 +14,39 @@ export const clackLogger = defineLogger({
17
14
  install(context, options) {
18
15
  const logLevel = options?.logLevel ?? logLevelMap.info
19
16
  const state = {
20
- totalPlugins: 0,
21
- completedPlugins: 0,
22
- failedPlugins: 0,
23
- totalFiles: 0,
24
- processedFiles: 0,
25
- hrStart: process.hrtime(),
17
+ ...createProgressCounters(),
26
18
  spinner: clack.spinner(),
27
19
  isSpinning: false,
20
+ runningPlugins: new Set<string>(),
28
21
  activeProgress: new Map<string, { interval?: NodeJS.Timeout; progressBar: clack.ProgressResult }>(),
22
+ activeHookLogs: new Map<string, { taskLog: ReturnType<typeof clack.taskLog>; hrStart: [number, number] }>(),
29
23
  }
30
24
 
31
- function reset() {
32
- for (const [_key, active] of state.activeProgress) {
25
+ // Clear every active progress bar's interval, stop it, and drop the map.
26
+ function stopActiveProgress() {
27
+ for (const [, active] of state.activeProgress) {
33
28
  if (active.interval) {
34
29
  clearInterval(active.interval)
35
30
  }
36
31
  active.progressBar?.stop()
37
32
  }
33
+ state.activeProgress.clear()
34
+ }
38
35
 
39
- state.totalPlugins = 0
40
- state.completedPlugins = 0
41
- state.failedPlugins = 0
42
- state.totalFiles = 0
43
- state.processedFiles = 0
44
- state.hrStart = process.hrtime()
36
+ function reset() {
37
+ stopActiveProgress()
38
+
39
+ resetProgressCounters(state)
45
40
  state.spinner = clack.spinner()
46
41
  state.isSpinning = false
47
- state.activeProgress.clear()
42
+ state.runningPlugins.clear()
43
+ state.activeHookLogs.clear()
44
+ }
45
+
46
+ // Label for the shared plugin bar, listing the plugins currently generating.
47
+ function pluginProgressText(): string {
48
+ const running = [...state.runningPlugins].map((name) => styleText('bold', name))
49
+ return getMessage(running.length > 0 ? `Generating ${running.join(', ')}` : 'Generating plugins')
48
50
  }
49
51
 
50
52
  function showProgressStep() {
@@ -62,12 +64,25 @@ export const clackLogger = defineLogger({
62
64
  return formatMessage(message, logLevel)
63
65
  }
64
66
 
67
+ // Registers a handler that prints a fixed step message, skipped at silent level.
68
+ function onStep<E extends keyof KubbHooks>(event: E, message: string): void {
69
+ context.on(event, () => {
70
+ if (logLevel <= logLevelMap.silent) {
71
+ return
72
+ }
73
+ clack.log.step(getMessage(message))
74
+ })
75
+ }
76
+
65
77
  function startSpinner(text?: string) {
66
78
  state.spinner.start(text)
67
79
  state.isSpinning = true
68
80
  }
69
81
 
70
82
  function stopSpinner(text?: string) {
83
+ if (!state.isSpinning) {
84
+ return
85
+ }
71
86
  state.spinner.stop(text)
72
87
  state.isSpinning = false
73
88
  }
@@ -77,13 +92,13 @@ export const clackLogger = defineLogger({
77
92
  return
78
93
  }
79
94
 
80
- const text = getMessage([styleText('blue', 'ℹ'), message, styleText('dim', info)].join(' '))
95
+ const text = getMessage([styleText('blue', 'ℹ'), message, info ? styleText('dim', info) : undefined].filter(Boolean).join(' '))
81
96
 
82
97
  if (state.isSpinning) {
83
98
  state.spinner.message(text)
84
- } else {
85
- clack.log.info(text)
99
+ return
86
100
  }
101
+ clack.log.info(text)
87
102
  })
88
103
 
89
104
  context.on('kubb:success', ({ message, info = '' }) => {
@@ -95,9 +110,9 @@ export const clackLogger = defineLogger({
95
110
 
96
111
  if (state.isSpinning) {
97
112
  stopSpinner(text)
98
- } else {
99
- clack.log.success(text)
113
+ return
100
114
  }
115
+ clack.log.success(text)
101
116
  })
102
117
 
103
118
  context.on('kubb:warn', ({ message, info }) => {
@@ -119,12 +134,12 @@ export const clackLogger = defineLogger({
119
134
 
120
135
  if (state.isSpinning) {
121
136
  stopSpinner(getMessage(text))
122
- } else {
123
- clack.log.error(getMessage(text))
137
+ return
124
138
  }
139
+ clack.log.error(getMessage(text))
125
140
 
126
- // Show stack trace in debug mode (first 3 frames)
127
- if (logLevel >= logLevelMap.debug && error.stack) {
141
+ // Show stack trace in verbose mode (first 3 frames)
142
+ if (logLevel >= logLevelMap.verbose && error.stack) {
128
143
  const frames = error.stack.split('\n').slice(1, 4)
129
144
  for (const frame of frames) {
130
145
  clack.log.message(getMessage(styleText('dim', frame.trim())))
@@ -141,14 +156,21 @@ export const clackLogger = defineLogger({
141
156
  }
142
157
  })
143
158
 
144
- context.on('kubb:version:new', ({ currentVersion, latestVersion }) => {
145
- if (logLevel <= logLevelMap.silent) {
159
+ context.on('kubb:diagnostic', ({ diagnostic }) => {
160
+ // Silent still surfaces errors so failures stay visible. It drops warnings and info.
161
+ if (logLevel <= logLevelMap.silent && diagnostic.severity !== 'error') {
146
162
  return
147
163
  }
148
164
 
149
- try {
165
+ stopSpinner()
166
+
167
+ // Stop any lingering progress UI so the multi-line block renders cleanly.
168
+ stopActiveProgress()
169
+
170
+ // The version-update notice keeps its own framed box instead of the diagnostic gutter.
171
+ if (Diagnostics.isUpdate(diagnostic)) {
150
172
  clack.box(
151
- `\`v${currentVersion}\` → \`v${latestVersion}\`
173
+ `\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\`
152
174
  Run \`npm install -g @kubb/cli\` to update`,
153
175
  'Update available for `Kubb`',
154
176
  {
@@ -160,14 +182,19 @@ Run \`npm install -g @kubb/cli\` to update`,
160
182
  titleAlign: 'center',
161
183
  },
162
184
  )
163
- } catch {
164
- console.log(`Update available for Kubb: v${currentVersion} → v${latestVersion}`)
165
- console.log('Run `npm install -g @kubb/cli` to update')
185
+
186
+ return
166
187
  }
188
+
189
+ // Hand the severity glyph to clack as the gutter `symbol`, then let it draw the
190
+ // bar on each detail line via the default `secondarySymbol`. The headline and
191
+ // details carry their own colors, so clack only owns the gutter.
192
+ const { symbol, headline, details } = Diagnostics.format(diagnostic)
193
+ clack.log.message([headline, ...details], { symbol })
167
194
  })
168
195
 
169
196
  context.on('kubb:lifecycle:start', async ({ version }) => {
170
- console.log(`\n${getIntro({ title: 'The ultimate toolkit for working with APIs', description: 'Ready to start', version, areEyesOpen: true })}\n`)
197
+ console.log(`\n${getIntro({ title: 'The meta framework for code generation', description: 'Ready to start', version, areEyesOpen: true })}\n`)
171
198
 
172
199
  reset()
173
200
  })
@@ -199,11 +226,17 @@ Run \`npm install -g @kubb/cli\` to update`,
199
226
  // Initialize progress tracking for this generation
200
227
  state.totalPlugins = config.plugins?.length ?? 0
201
228
 
229
+ if (logLevel <= logLevelMap.silent) {
230
+ return
231
+ }
232
+
202
233
  const text = getMessage(['Generation started', config.name ? `for ${styleText('dim', config.name)}` : undefined].filter(Boolean).join(' '))
203
234
 
204
235
  clack.intro(text)
205
236
  })
206
237
 
238
+ // Plugins run concurrently, so they share a single progress bar. A bar per plugin
239
+ // would make clack render them side by side and pile up keypress listeners.
207
240
  context.on('kubb:plugin:start', ({ plugin }) => {
208
241
  if (logLevel <= logLevelMap.silent) {
209
242
  return
@@ -211,50 +244,44 @@ Run \`npm install -g @kubb/cli\` to update`,
211
244
 
212
245
  stopSpinner()
213
246
 
247
+ state.runningPlugins.add(plugin.name)
248
+
249
+ const active = state.activeProgress.get('plugins')
250
+ if (active) {
251
+ active.progressBar.advance(0, pluginProgressText())
252
+ return
253
+ }
254
+
214
255
  const progressBar = clack.progress({
215
256
  style: 'block',
216
- max: 100,
257
+ max: Math.max(state.totalPlugins, 1),
217
258
  size: 30,
218
259
  })
219
- const text = getMessage(`Generating ${styleText('bold', plugin.name)}`)
220
- progressBar.start(text)
221
-
222
- const interval = setInterval(() => {
223
- progressBar.advance()
224
- }, 100)
225
-
226
- state.activeProgress.set(plugin.name, { progressBar, interval })
260
+ progressBar.start(pluginProgressText())
261
+ // Catch up to plugins already finished before this bar opened.
262
+ progressBar.advance(state.completedPlugins + state.failedPlugins, pluginProgressText())
263
+ state.activeProgress.set('plugins', { progressBar })
227
264
  })
228
265
 
229
- context.on('kubb:plugin:end', ({ plugin, duration, success }) => {
266
+ context.on('kubb:plugin:end', ({ plugin, success }) => {
230
267
  stopSpinner()
231
268
 
232
- const active = state.activeProgress.get(plugin.name)
269
+ const active = state.activeProgress.get('plugins')
233
270
 
234
271
  if (!active || logLevel === logLevelMap.silent) {
235
272
  return
236
273
  }
237
274
 
238
- clearInterval(active.interval)
275
+ state.runningPlugins.delete(plugin.name)
276
+ recordPluginResult(state, success)
277
+ active.progressBar.advance(1, pluginProgressText())
239
278
 
240
- if (success) {
241
- state.completedPlugins++
242
- } else {
243
- state.failedPlugins++
279
+ // Close the bar once nothing is generating, then print the progress step.
280
+ if (state.runningPlugins.size === 0) {
281
+ active.progressBar.stop(getMessage('Plugins generated'))
282
+ state.activeProgress.delete('plugins')
283
+ showProgressStep()
244
284
  }
245
-
246
- const durationStr = formatMsWithColor(duration)
247
- const text = getMessage(
248
- success
249
- ? `${styleText('bold', plugin.name)} completed in ${durationStr}`
250
- : `${styleText('bold', plugin.name)} failed in ${styleText('red', formatMs(duration))}`,
251
- )
252
-
253
- active.progressBar.stop(text)
254
- state.activeProgress.delete(plugin.name)
255
-
256
- // Show progress step after each plugin
257
- showProgressStep()
258
285
  })
259
286
 
260
287
  context.on('kubb:files:processing:start', ({ files }) => {
@@ -279,23 +306,20 @@ Run \`npm install -g @kubb/cli\` to update`,
279
306
  state.activeProgress.set('files', { progressBar })
280
307
  })
281
308
 
282
- context.on('kubb:file:processing:update', ({ file, config }) => {
309
+ context.on('kubb:files:processing:update', ({ files }) => {
283
310
  if (logLevel <= logLevelMap.silent) {
284
311
  return
285
312
  }
286
313
 
287
314
  stopSpinner()
288
315
 
289
- state.processedFiles++
290
-
291
- const text = `Writing ${relative(config.root, file.path)}`
292
316
  const active = state.activeProgress.get('files')
293
-
294
- if (!active) {
295
- return
317
+ for (const { file, config } of files) {
318
+ state.processedFiles++
319
+ if (active) {
320
+ active.progressBar.advance(undefined, `Writing ${relative(config.root, file.path)}`)
321
+ }
296
322
  }
297
-
298
- active.progressBar.advance(undefined, text)
299
323
  })
300
324
  context.on('kubb:files:processing:end', () => {
301
325
  if (logLevel <= logLevelMap.silent) {
@@ -319,140 +343,78 @@ Run \`npm install -g @kubb/cli\` to update`,
319
343
  })
320
344
 
321
345
  context.on('kubb:generation:end', ({ config }) => {
346
+ stopSpinner()
347
+
322
348
  const text = getMessage(config.name ? `Generation completed for ${styleText('dim', config.name)}` : 'Generation completed')
323
349
 
324
350
  clack.outro(text)
325
351
  })
326
352
 
327
- context.on('kubb:format:start', () => {
328
- if (logLevel <= logLevelMap.silent) {
329
- return
330
- }
353
+ onStep('kubb:format:start', 'Formatting')
354
+ onStep('kubb:lint:start', 'Linting')
355
+ onStep('kubb:hooks:start', 'Running hooks')
331
356
 
332
- const text = getMessage('Format started')
333
-
334
- clack.intro(text)
335
- })
336
-
337
- context.on('kubb:format:end', () => {
338
- if (logLevel <= logLevelMap.silent) {
357
+ context.on('kubb:hook:start', ({ id, command, args }) => {
358
+ if (logLevel <= logLevelMap.silent || !id) {
339
359
  return
340
360
  }
341
361
 
342
- const text = getMessage('Format completed')
362
+ stopSpinner()
343
363
 
344
- clack.outro(text)
364
+ const commandWithArgs = formatCommandWithArgs(command, args)
365
+ const title = getMessage(`Running ${styleText('dim', commandWithArgs)}`)
366
+ const taskLog = clack.taskLog({ title })
367
+
368
+ state.activeHookLogs.set(id, { taskLog, hrStart: process.hrtime() })
345
369
  })
346
370
 
347
- context.on('kubb:lint:start', () => {
348
- if (logLevel <= logLevelMap.silent) {
371
+ context.on('kubb:hook:end', ({ id, command, args, success, error }) => {
372
+ if (logLevel <= logLevelMap.silent || !id) {
349
373
  return
350
374
  }
351
375
 
352
- const text = getMessage('Lint started')
353
-
354
- clack.intro(text)
355
- })
356
-
357
- context.on('kubb:lint:end', () => {
358
- if (logLevel <= logLevelMap.silent) {
376
+ const active = state.activeHookLogs.get(id)
377
+ if (!active) {
359
378
  return
360
379
  }
380
+ state.activeHookLogs.delete(id)
361
381
 
362
- const text = getMessage('Lint completed')
363
-
364
- clack.outro(text)
365
- })
366
-
367
- context.on('kubb:hook:start', async ({ id, command, args }) => {
368
382
  const commandWithArgs = formatCommandWithArgs(command, args)
369
- const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} started`)
370
-
371
- // Skip hook execution if no id is provided (e.g., during benchmarks or tests)
372
- if (!id) {
373
- return
374
- }
383
+ const duration = formatMsWithColor(getElapsedMs(active.hrStart))
375
384
 
376
- if (logLevel <= logLevelMap.silent) {
377
- await runHook({
378
- id,
379
- command,
380
- args,
381
- commandWithArgs,
382
- context,
383
- sink: {
384
- onStderr: (s) => console.error(s),
385
- onStdout: (s) => console.log(s),
386
- },
387
- })
388
- return
385
+ if (success) {
386
+ active.taskLog.success(getMessage(`${styleText('dim', commandWithArgs)} completed in ${duration}`))
387
+ } else {
388
+ const reason = error?.message ? ` (${error.message})` : ''
389
+ active.taskLog.error(getMessage(`${styleText('dim', commandWithArgs)} failed${reason}`), { showLog: true })
389
390
  }
391
+ })
390
392
 
391
- clack.intro(text)
392
-
393
- const logger = clack.taskLog({
394
- title: getMessage(['Executing hook', logLevel >= logLevelMap.info ? styleText('dim', commandWithArgs) : undefined].filter(Boolean).join(' ')),
395
- })
396
-
397
- const writable = new ClackWritable(logger)
398
-
399
- await runHook({
400
- id,
401
- command,
402
- args,
403
- commandWithArgs,
404
- context,
405
- stream: true,
406
- sink: {
407
- onLine: (line) => writable.write(line),
408
- onStderr: (s) => logger.error(s),
409
- onStdout: (s) => logger.message(s),
410
- },
411
- })
393
+ context.on('kubb:lifecycle:end', () => {
394
+ reset()
412
395
  })
413
396
 
414
- context.on('kubb:hook:end', ({ command, args }) => {
397
+ return (_commandWithArgs: string, hookId: string) => {
415
398
  if (logLevel <= logLevelMap.silent) {
416
- return
399
+ return {
400
+ onStdout: (s: string) => console.log(s),
401
+ onStderr: (s: string) => console.error(s),
402
+ }
417
403
  }
418
404
 
419
- const commandWithArgs = formatCommandWithArgs(command, args)
420
- const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} successfully executed`)
405
+ const active = state.activeHookLogs.get(hookId)
406
+ if (!active) {
407
+ return null
408
+ }
421
409
 
422
- clack.outro(text)
423
- })
410
+ const { taskLog } = active
424
411
 
425
- context.on('kubb:generation:summary', ({ config, pluginTimings, failedPlugins, filesCreated, status, hrStart }) => {
426
- const summary = getSummary({
427
- failedPlugins,
428
- filesCreated,
429
- config,
430
- status,
431
- hrStart,
432
- pluginTimings: logLevel >= logLevelMap.verbose ? pluginTimings : undefined,
433
- })
434
- const title = config.name || ''
435
-
436
- summary.unshift('\n')
437
- summary.push('\n')
438
-
439
- const borderColor = status === 'success' ? 'green' : 'red'
440
- try {
441
- clack.box(summary.join('\n'), getMessage(title), {
442
- width: 'auto',
443
- formatBorder: (s: string) => styleText(borderColor, s),
444
- rounded: true,
445
- withGuide: false,
446
- contentAlign: 'left',
447
- titleAlign: 'center',
448
- })
449
- } catch {
450
- console.log(summary.join('\n'))
412
+ return {
413
+ stream: true,
414
+ onLine: (line: string) => taskLog.message(styleText('dim', line)),
415
+ onStdout: (s: string) => taskLog.message(s),
416
+ onStderr: (s: string) => taskLog.message(styleText('red', s)),
451
417
  }
452
- })
453
-
454
- context.on('kubb:lifecycle:end', () => {
455
- reset()
456
- })
418
+ }
457
419
  },
458
420
  })