@kubb/cli 5.0.0-beta.2 → 5.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +177 -26
  2. package/dist/agent-B1y251-b.cjs +70 -0
  3. package/dist/agent-B1y251-b.cjs.map +1 -0
  4. package/dist/agent-jGZu4zRF.js +68 -0
  5. package/dist/agent-jGZu4zRF.js.map +1 -0
  6. package/dist/{chunk--u3MIqq1.js → chunk-BvFE5Tac.js} +1 -0
  7. package/dist/constants-B2JTeRBb.js +42 -0
  8. package/dist/constants-B2JTeRBb.js.map +1 -0
  9. package/dist/constants-BINTA5VZ.cjs +77 -0
  10. package/dist/constants-BINTA5VZ.cjs.map +1 -0
  11. package/dist/constants-BYGmiFs0.cjs +139 -0
  12. package/dist/constants-BYGmiFs0.cjs.map +1 -0
  13. package/dist/constants-DSJ-Xrbv.js +116 -0
  14. package/dist/constants-DSJ-Xrbv.js.map +1 -0
  15. package/dist/define-Bdn8j5VM.cjs.map +1 -1
  16. package/dist/{define-Ctii4bel.js → define-m_fp-Aqm.js} +2 -2
  17. package/dist/{define-Ctii4bel.js.map → define-m_fp-Aqm.js.map} +1 -1
  18. package/dist/{errors-CjPmyZHy.js → errors-CINO1EIv.js} +2 -2
  19. package/dist/{errors-CjPmyZHy.js.map → errors-CINO1EIv.js.map} +1 -1
  20. package/dist/errors-CLCjoSg0.cjs.map +1 -1
  21. package/dist/{generate-DL_7a7Wd.cjs → generate-CszBbEx_.cjs} +10 -4
  22. package/dist/generate-CszBbEx_.cjs.map +1 -0
  23. package/dist/{generate-B3PZ6Dp-.js → generate-Dqk3rinP.js} +12 -6
  24. package/dist/generate-Dqk3rinP.js.map +1 -0
  25. package/dist/index.cjs +20 -11
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +22 -13
  29. package/dist/index.js.map +1 -1
  30. package/dist/init-BSfLyes_.js +53 -0
  31. package/dist/init-BSfLyes_.js.map +1 -0
  32. package/dist/init-JrYOUBes.cjs +53 -0
  33. package/dist/init-JrYOUBes.cjs.map +1 -0
  34. package/dist/mcp-BiLUSMgD.js +39 -0
  35. package/dist/mcp-BiLUSMgD.js.map +1 -0
  36. package/dist/mcp-DY_TKMoS.cjs +39 -0
  37. package/dist/mcp-DY_TKMoS.cjs.map +1 -0
  38. package/dist/package-C_LGzz8A.js +6 -0
  39. package/dist/package-C_LGzz8A.js.map +1 -0
  40. package/dist/{package-D8wlStAg.cjs → package-CyNXMTr8.cjs} +2 -2
  41. package/dist/package-CyNXMTr8.cjs.map +1 -0
  42. package/dist/run-B11-UaUs.cjs +33 -0
  43. package/dist/run-B11-UaUs.cjs.map +1 -0
  44. package/dist/{init-eNRlotJK.js → run-BNqMQygv.js} +107 -149
  45. package/dist/run-BNqMQygv.js.map +1 -0
  46. package/dist/{init-CZ5Xq2Hd.cjs → run-BnGfi7Cp.cjs} +105 -147
  47. package/dist/run-BnGfi7Cp.cjs.map +1 -0
  48. package/dist/{generate-Dt_r0ELY.js → run-BryUpjjc.js} +484 -410
  49. package/dist/run-BryUpjjc.js.map +1 -0
  50. package/dist/{agent-Ev5hU5hH.js → run-BzpYYOQs.js} +53 -44
  51. package/dist/run-BzpYYOQs.js.map +1 -0
  52. package/dist/run-CCZ24VKk.js +51 -0
  53. package/dist/run-CCZ24VKk.js.map +1 -0
  54. package/dist/run-CQbj3ley.cjs +52 -0
  55. package/dist/run-CQbj3ley.cjs.map +1 -0
  56. package/dist/{generate-B3jl4ukb.cjs → run-CmiuJkLG.cjs} +481 -407
  57. package/dist/run-CmiuJkLG.cjs.map +1 -0
  58. package/dist/{agent-B_pirbeB.cjs → run-DwdAwnLG.cjs} +51 -42
  59. package/dist/run-DwdAwnLG.cjs.map +1 -0
  60. package/dist/run-PSA9X7ci.js +32 -0
  61. package/dist/run-PSA9X7ci.js.map +1 -0
  62. package/dist/shell-475fQKaX.cjs.map +1 -1
  63. package/dist/{shell-DLzN4fRo.js → shell-CN6DNqeC.js} +2 -2
  64. package/dist/{shell-DLzN4fRo.js.map → shell-CN6DNqeC.js.map} +1 -1
  65. package/dist/{telemetry-DN95_2pF.cjs → telemetry-B2iWkY5e.cjs} +5 -7
  66. package/dist/telemetry-B2iWkY5e.cjs.map +1 -0
  67. package/dist/{telemetry-LgT_sdPe.js → telemetry-BkektVz6.js} +6 -8
  68. package/dist/telemetry-BkektVz6.js.map +1 -0
  69. package/dist/validate-8VIogDlj.cjs +26 -0
  70. package/dist/validate-8VIogDlj.cjs.map +1 -0
  71. package/dist/validate-KufVWuQ1.js +26 -0
  72. package/dist/validate-KufVWuQ1.js.map +1 -0
  73. package/package.json +23 -15
  74. package/src/commands/agent/start.ts +10 -7
  75. package/src/commands/agent.ts +3 -1
  76. package/src/commands/generate.ts +5 -3
  77. package/src/commands/init.ts +34 -3
  78. package/src/commands/mcp.ts +28 -4
  79. package/src/commands/validate.ts +6 -4
  80. package/src/constants.ts +2 -58
  81. package/src/index.ts +5 -3
  82. package/src/loggers/clackLogger.ts +72 -81
  83. package/src/loggers/fileSystemLogger.ts +26 -13
  84. package/src/loggers/githubActionsLogger.ts +74 -28
  85. package/src/loggers/plainLogger.ts +55 -30
  86. package/src/loggers/types.ts +6 -0
  87. package/src/loggers/utils.ts +158 -9
  88. package/src/runners/agent/run.ts +113 -0
  89. package/src/runners/agent/utils.ts +98 -0
  90. package/src/runners/generate/run.ts +316 -0
  91. package/src/runners/generate/utils.ts +216 -0
  92. package/src/runners/init/run.ts +212 -0
  93. package/src/{utils/packageManager.ts → runners/init/utils.ts} +10 -0
  94. package/src/runners/mcp/run.ts +37 -0
  95. package/src/runners/validate/run.ts +63 -0
  96. package/src/{utils/telemetry.ts → telemetry.ts} +12 -5
  97. package/dist/agent-0Nk--lcr.cjs +0 -58
  98. package/dist/agent-0Nk--lcr.cjs.map +0 -1
  99. package/dist/agent-B_pirbeB.cjs.map +0 -1
  100. package/dist/agent-DKeVuiUC.js +0 -56
  101. package/dist/agent-DKeVuiUC.js.map +0 -1
  102. package/dist/agent-Ev5hU5hH.js.map +0 -1
  103. package/dist/constants-CnDXa1R6.cjs +0 -148
  104. package/dist/constants-CnDXa1R6.cjs.map +0 -1
  105. package/dist/constants-aL3CP_Wq.js +0 -95
  106. package/dist/constants-aL3CP_Wq.js.map +0 -1
  107. package/dist/generate-B3PZ6Dp-.js.map +0 -1
  108. package/dist/generate-B3jl4ukb.cjs.map +0 -1
  109. package/dist/generate-DL_7a7Wd.cjs.map +0 -1
  110. package/dist/generate-Dt_r0ELY.js.map +0 -1
  111. package/dist/init-Bj94Nvt8.js +0 -25
  112. package/dist/init-Bj94Nvt8.js.map +0 -1
  113. package/dist/init-CZ5Xq2Hd.cjs.map +0 -1
  114. package/dist/init-CyN1oyTF.cjs +0 -25
  115. package/dist/init-CyN1oyTF.cjs.map +0 -1
  116. package/dist/init-eNRlotJK.js.map +0 -1
  117. package/dist/mcp-BzW703d7.js +0 -16
  118. package/dist/mcp-BzW703d7.js.map +0 -1
  119. package/dist/mcp-CLcDV4Jm.cjs +0 -41
  120. package/dist/mcp-CLcDV4Jm.cjs.map +0 -1
  121. package/dist/mcp-D7EIR9fR.js +0 -40
  122. package/dist/mcp-D7EIR9fR.js.map +0 -1
  123. package/dist/mcp-ZY-ONTOp.cjs +0 -16
  124. package/dist/mcp-ZY-ONTOp.cjs.map +0 -1
  125. package/dist/package-D8wlStAg.cjs.map +0 -1
  126. package/dist/package-Yo-9_m5C.js +0 -6
  127. package/dist/package-Yo-9_m5C.js.map +0 -1
  128. package/dist/telemetry-DN95_2pF.cjs.map +0 -1
  129. package/dist/telemetry-LgT_sdPe.js.map +0 -1
  130. package/dist/validate-Dplr99xO.js +0 -25
  131. package/dist/validate-Dplr99xO.js.map +0 -1
  132. package/dist/validate-_8mBa63G.cjs +0 -25
  133. package/dist/validate-_8mBa63G.cjs.map +0 -1
  134. package/dist/validate-kLJoT_hi.js +0 -33
  135. package/dist/validate-kLJoT_hi.js.map +0 -1
  136. package/dist/validate-yKKzqEZ5.cjs +0 -34
  137. package/dist/validate-yKKzqEZ5.cjs.map +0 -1
  138. package/src/runners/agent.ts +0 -149
  139. package/src/runners/generate.ts +0 -333
  140. package/src/runners/init.ts +0 -296
  141. package/src/runners/mcp.ts +0 -45
  142. package/src/runners/validate.ts +0 -39
  143. package/src/types.ts +0 -11
  144. package/src/utils/Writables.ts +0 -17
  145. package/src/utils/executeHooks.ts +0 -45
  146. package/src/utils/flags.ts +0 -9
  147. package/src/utils/getConfig.ts +0 -10
  148. package/src/utils/getCosmiConfig.ts +0 -80
  149. package/src/utils/getSummary.ts +0 -68
  150. package/src/utils/runHook.ts +0 -91
  151. package/src/utils/watcher.ts +0 -19
@@ -2,11 +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'
5
+ import { formatMs, formatMsWithColor, getElapsedMs, getIntro, toCause } from '@internals/utils'
6
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'
7
+ import { getSummary } from './utils.ts'
10
8
  import { buildProgressLine, formatCommandWithArgs, formatMessage } from './utils.ts'
11
9
 
12
10
  /**
@@ -26,6 +24,7 @@ export const clackLogger = defineLogger({
26
24
  spinner: clack.spinner(),
27
25
  isSpinning: false,
28
26
  activeProgress: new Map<string, { interval?: NodeJS.Timeout; progressBar: clack.ProgressResult }>(),
27
+ activeHookLogs: new Map<string, { taskLog: ReturnType<typeof clack.taskLog>; hrStart: [number, number] }>(),
29
28
  }
30
29
 
31
30
  function reset() {
@@ -45,6 +44,7 @@ export const clackLogger = defineLogger({
45
44
  state.spinner = clack.spinner()
46
45
  state.isSpinning = false
47
46
  state.activeProgress.clear()
47
+ state.activeHookLogs.clear()
48
48
  }
49
49
 
50
50
  function showProgressStep() {
@@ -68,6 +68,9 @@ export const clackLogger = defineLogger({
68
68
  }
69
69
 
70
70
  function stopSpinner(text?: string) {
71
+ if (!state.isSpinning) {
72
+ return
73
+ }
71
74
  state.spinner.stop(text)
72
75
  state.isSpinning = false
73
76
  }
@@ -81,9 +84,9 @@ export const clackLogger = defineLogger({
81
84
 
82
85
  if (state.isSpinning) {
83
86
  state.spinner.message(text)
84
- } else {
85
- clack.log.info(text)
87
+ return
86
88
  }
89
+ clack.log.info(text)
87
90
  })
88
91
 
89
92
  context.on('kubb:success', ({ message, info = '' }) => {
@@ -95,9 +98,9 @@ export const clackLogger = defineLogger({
95
98
 
96
99
  if (state.isSpinning) {
97
100
  stopSpinner(text)
98
- } else {
99
- clack.log.success(text)
101
+ return
100
102
  }
103
+ clack.log.success(text)
101
104
  })
102
105
 
103
106
  context.on('kubb:warn', ({ message, info }) => {
@@ -119,9 +122,9 @@ export const clackLogger = defineLogger({
119
122
 
120
123
  if (state.isSpinning) {
121
124
  stopSpinner(getMessage(text))
122
- } else {
123
- clack.log.error(getMessage(text))
125
+ return
124
126
  }
127
+ clack.log.error(getMessage(text))
125
128
 
126
129
  // Show stack trace in debug mode (first 3 frames)
127
130
  if (logLevel >= logLevelMap.debug && error.stack) {
@@ -199,6 +202,10 @@ Run \`npm install -g @kubb/cli\` to update`,
199
202
  // Initialize progress tracking for this generation
200
203
  state.totalPlugins = config.plugins?.length ?? 0
201
204
 
205
+ if (logLevel <= logLevelMap.silent) {
206
+ return
207
+ }
208
+
202
209
  const text = getMessage(['Generation started', config.name ? `for ${styleText('dim', config.name)}` : undefined].filter(Boolean).join(' '))
203
210
 
204
211
  clack.intro(text)
@@ -279,23 +286,20 @@ Run \`npm install -g @kubb/cli\` to update`,
279
286
  state.activeProgress.set('files', { progressBar })
280
287
  })
281
288
 
282
- context.on('kubb:file:processing:update', ({ file, config }) => {
289
+ context.on('kubb:files:processing:update', ({ files }) => {
283
290
  if (logLevel <= logLevelMap.silent) {
284
291
  return
285
292
  }
286
293
 
287
294
  stopSpinner()
288
295
 
289
- state.processedFiles++
290
-
291
- const text = `Writing ${relative(config.root, file.path)}`
292
296
  const active = state.activeProgress.get('files')
293
-
294
- if (!active) {
295
- return
297
+ for (const { file, config } of files) {
298
+ state.processedFiles++
299
+ if (active) {
300
+ active.progressBar.advance(undefined, `Writing ${relative(config.root, file.path)}`)
301
+ }
296
302
  }
297
-
298
- active.progressBar.advance(undefined, text)
299
303
  })
300
304
  context.on('kubb:files:processing:end', () => {
301
305
  if (logLevel <= logLevelMap.silent) {
@@ -319,6 +323,8 @@ Run \`npm install -g @kubb/cli\` to update`,
319
323
  })
320
324
 
321
325
  context.on('kubb:generation:end', ({ config }) => {
326
+ stopSpinner()
327
+
322
328
  const text = getMessage(config.name ? `Generation completed for ${styleText('dim', config.name)}` : 'Generation completed')
323
329
 
324
330
  clack.outro(text)
@@ -329,97 +335,59 @@ Run \`npm install -g @kubb/cli\` to update`,
329
335
  return
330
336
  }
331
337
 
332
- const text = getMessage('Format started')
333
-
334
- clack.intro(text)
338
+ clack.log.step(getMessage('Formatting'))
335
339
  })
336
340
 
337
- context.on('kubb:format:end', () => {
341
+ context.on('kubb:lint:start', () => {
338
342
  if (logLevel <= logLevelMap.silent) {
339
343
  return
340
344
  }
341
345
 
342
- const text = getMessage('Format completed')
343
-
344
- clack.outro(text)
346
+ clack.log.step(getMessage('Linting'))
345
347
  })
346
348
 
347
- context.on('kubb:lint:start', () => {
349
+ context.on('kubb:hooks:start', () => {
348
350
  if (logLevel <= logLevelMap.silent) {
349
351
  return
350
352
  }
351
353
 
352
- const text = getMessage('Lint started')
353
-
354
- clack.intro(text)
354
+ clack.log.step(getMessage('Running hooks'))
355
355
  })
356
356
 
357
- context.on('kubb:lint:end', () => {
358
- if (logLevel <= logLevelMap.silent) {
357
+ context.on('kubb:hook:start', ({ id, command, args }) => {
358
+ if (logLevel <= logLevelMap.silent || !id) {
359
359
  return
360
360
  }
361
361
 
362
- const text = getMessage('Lint completed')
363
-
364
- clack.outro(text)
365
- })
362
+ stopSpinner()
366
363
 
367
- context.on('kubb:hook:start', async ({ id, command, args }) => {
368
364
  const commandWithArgs = formatCommandWithArgs(command, args)
369
- const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} started`)
365
+ const title = getMessage(`Running ${styleText('dim', commandWithArgs)}`)
366
+ const taskLog = clack.taskLog({ title })
370
367
 
371
- // Skip hook execution if no id is provided (e.g., during benchmarks or tests)
372
- if (!id) {
373
- return
374
- }
368
+ state.activeHookLogs.set(id, { taskLog, hrStart: process.hrtime() })
369
+ })
375
370
 
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
- })
371
+ context.on('kubb:hook:end', ({ id, command, args, success, error }) => {
372
+ if (logLevel <= logLevelMap.silent || !id) {
388
373
  return
389
374
  }
390
375
 
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
- })
412
- })
413
-
414
- context.on('kubb:hook:end', ({ command, args }) => {
415
- if (logLevel <= logLevelMap.silent) {
376
+ const active = state.activeHookLogs.get(id)
377
+ if (!active) {
416
378
  return
417
379
  }
380
+ state.activeHookLogs.delete(id)
418
381
 
419
382
  const commandWithArgs = formatCommandWithArgs(command, args)
420
- const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} successfully executed`)
383
+ const duration = formatMsWithColor(getElapsedMs(active.hrStart))
421
384
 
422
- clack.outro(text)
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 })
390
+ }
423
391
  })
424
392
 
425
393
  context.on('kubb:generation:summary', ({ config, pluginTimings, failedPlugins, filesCreated, status, hrStart }) => {
@@ -454,5 +422,28 @@ Run \`npm install -g @kubb/cli\` to update`,
454
422
  context.on('kubb:lifecycle:end', () => {
455
423
  reset()
456
424
  })
425
+
426
+ return (_commandWithArgs: string, hookId: string) => {
427
+ if (logLevel <= logLevelMap.silent) {
428
+ return {
429
+ onStdout: (s: string) => console.log(s),
430
+ onStderr: (s: string) => console.error(s),
431
+ }
432
+ }
433
+
434
+ const active = state.activeHookLogs.get(hookId)
435
+ if (!active) {
436
+ return null
437
+ }
438
+
439
+ const { taskLog } = active
440
+
441
+ return {
442
+ stream: true,
443
+ onLine: (line: string) => taskLog.message(styleText('dim', line)),
444
+ onStdout: (s: string) => taskLog.message(s),
445
+ onStderr: (s: string) => taskLog.message(styleText('red', s)),
446
+ }
447
+ }
457
448
  },
458
449
  })
@@ -4,14 +4,24 @@ import { formatMs, write } from '@internals/utils'
4
4
  import { defineLogger } from '@kubb/core'
5
5
 
6
6
  type CachedEvent = {
7
+ /**
8
+ * Timestamp when this event was captured, used to derive the log filename.
9
+ */
7
10
  date: Date
11
+ /**
12
+ * Accumulated log lines for this event.
13
+ */
8
14
  logs: string[]
15
+ /**
16
+ * Optional override for the output filename inside `.kubb/`. When omitted, the filename is derived from `date`.
17
+ */
9
18
  fileName?: string
10
19
  }
11
20
 
12
21
  /**
13
22
  * FileSystem logger that captures debug events and writes them to `.kubb` directory files.
14
- * Note: Logs write on `lifecycle:end` or process exit. Cached logs may be lost if the process crashes before these events.
23
+ *
24
+ * @note Logs are written on `kubb:lifecycle:end` or process exit. Cached logs may be lost if the process crashes before either event.
15
25
  */
16
26
  export const fileSystemLogger = defineLogger({
17
27
  name: 'filesystem',
@@ -42,13 +52,15 @@ export const fileSystemLogger = defineLogger({
42
52
  }
43
53
 
44
54
  if (log.logs.length > 0) {
45
- const timestamp = log.date.toLocaleString()
46
- files[pathName].push(`[${timestamp}]\n${log.logs.join('\n')}`)
55
+ const prefix = `[${log.date.toLocaleString()}] `
56
+ const indent = ' '.repeat(prefix.length)
57
+ const [first, ...rest] = log.logs
58
+ files[pathName].push([prefix + first, ...rest.map((line) => indent + line)].join('\n'))
47
59
  }
48
60
  }
49
61
 
50
62
  for (const [fileName, logs] of Object.entries(files)) {
51
- await write(fileName, logs.join('\n\n'))
63
+ await write(fileName, logs.join('\n'))
52
64
  }
53
65
 
54
66
  return Object.keys(files)
@@ -57,21 +69,21 @@ export const fileSystemLogger = defineLogger({
57
69
  context.on('kubb:info', ({ message, info }) => {
58
70
  state.cachedLogs.add({
59
71
  date: new Date(),
60
- logs: [`ℹ ${message} ${info}`],
72
+ logs: [`ℹ ${[message, info].filter(Boolean).join(' ')}`],
61
73
  })
62
74
  })
63
75
 
64
76
  context.on('kubb:success', ({ message, info }) => {
65
77
  state.cachedLogs.add({
66
78
  date: new Date(),
67
- logs: [`✓ ${message} ${info}`],
79
+ logs: [`✓ ${[message, info].filter(Boolean).join(' ')}`],
68
80
  })
69
81
  })
70
82
 
71
83
  context.on('kubb:warn', ({ message, info }) => {
72
84
  state.cachedLogs.add({
73
85
  date: new Date(),
74
- logs: [`⚠ ${message} ${info}`],
86
+ logs: [`⚠ ${[message, info].filter(Boolean).join(' ')}`],
75
87
  })
76
88
  })
77
89
 
@@ -82,17 +94,18 @@ export const fileSystemLogger = defineLogger({
82
94
  })
83
95
  })
84
96
 
85
- context.on('kubb:debug', (message) => {
97
+ context.on('kubb:debug', ({ date, fileName, logs }) => {
86
98
  state.cachedLogs.add({
87
- date: new Date(),
88
- logs: message.logs,
99
+ date,
100
+ fileName,
101
+ logs,
89
102
  })
90
103
  })
91
104
 
92
105
  context.on('kubb:plugin:start', ({ plugin }) => {
93
106
  state.cachedLogs.add({
94
107
  date: new Date(),
95
- logs: [`Generating ${plugin.name}`],
108
+ logs: [`► Generating ${plugin.name}`],
96
109
  })
97
110
  })
98
111
 
@@ -101,14 +114,14 @@ export const fileSystemLogger = defineLogger({
101
114
 
102
115
  state.cachedLogs.add({
103
116
  date: new Date(),
104
- logs: [success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`],
117
+ logs: [success ? `✓ ${plugin.name} completed in ${durationStr}` : `✗ ${plugin.name} failed in ${durationStr}`],
105
118
  })
106
119
  })
107
120
 
108
121
  context.on('kubb:files:processing:start', ({ files }) => {
109
122
  state.cachedLogs.add({
110
123
  date: new Date(),
111
- logs: [`Start ${files.length} writing:`, ...files.map((file) => file.path)],
124
+ logs: [`► Writing ${files.length} files`, ...files.map((file) => ` ${file.path}`)],
112
125
  })
113
126
  })
114
127
 
@@ -1,7 +1,7 @@
1
+ import process from 'node:process'
1
2
  import { styleText } from 'node:util'
2
- import { formatHrtime, formatMs, formatMsWithColor, toCause } from '@internals/utils'
3
+ import { formatHrtime, formatMs, formatMsWithColor, getElapsedMs, toCause } from '@internals/utils'
3
4
  import { type Config, defineLogger, logLevel as logLevelMap } from '@kubb/core'
4
- import { runHook } from '../utils/runHook.ts'
5
5
  import { buildProgressLine, formatCommandWithArgs, formatMessage } from './utils.ts'
6
6
 
7
7
  /**
@@ -19,9 +19,12 @@ export const githubActionsLogger = defineLogger({
19
19
  processedFiles: 0,
20
20
  hrStart: process.hrtime(),
21
21
  currentConfigs: [] as Array<Config>,
22
+ hookStarts: new Map<string, [number, number]>(),
23
+ openGroupDepth: 0,
22
24
  }
23
25
 
24
26
  function reset() {
27
+ closeAllGroups()
25
28
  state.totalPlugins = 0
26
29
  state.completedPlugins = 0
27
30
  state.failedPlugins = 0
@@ -29,6 +32,7 @@ export const githubActionsLogger = defineLogger({
29
32
  state.processedFiles = 0
30
33
  state.hrStart = process.hrtime()
31
34
  state.currentConfigs = []
35
+ state.hookStarts.clear()
32
36
  }
33
37
 
34
38
  function showProgressStep() {
@@ -48,10 +52,19 @@ export const githubActionsLogger = defineLogger({
48
52
 
49
53
  function openGroup(name: string) {
50
54
  console.log(`::group::${name}`)
55
+ state.openGroupDepth++
51
56
  }
52
57
 
53
58
  function closeGroup(_name: string) {
54
59
  console.log('::endgroup::')
60
+ if (state.openGroupDepth > 0) state.openGroupDepth--
61
+ }
62
+
63
+ function closeAllGroups() {
64
+ while (state.openGroupDepth > 0) {
65
+ console.log('::endgroup::')
66
+ state.openGroupDepth--
67
+ }
55
68
  }
56
69
 
57
70
  context.on('kubb:info', ({ message, info = '' }) => {
@@ -87,6 +100,10 @@ export const githubActionsLogger = defineLogger({
87
100
  context.on('kubb:error', ({ error }) => {
88
101
  const caused = toCause(error)
89
102
 
103
+ // Always release any unclosed groups so a thrown :start without a matching :end
104
+ // (e.g., when getConfigs or kubb.setup throws) doesn't leak an open section.
105
+ closeAllGroups()
106
+
90
107
  if (logLevel <= logLevelMap.silent) {
91
108
  return
92
109
  }
@@ -116,6 +133,10 @@ export const githubActionsLogger = defineLogger({
116
133
  reset()
117
134
  })
118
135
 
136
+ context.on('kubb:version:new', ({ currentVersion, latestVersion }) => {
137
+ console.log(`::notice::Update available for Kubb: v${currentVersion} → v${latestVersion}. Run \`npm install -g @kubb/cli\` to update.`)
138
+ })
139
+
119
140
  context.on('kubb:config:start', () => {
120
141
  if (logLevel <= logLevelMap.silent) {
121
142
  return
@@ -235,12 +256,12 @@ export const githubActionsLogger = defineLogger({
235
256
  showProgressStep()
236
257
  })
237
258
 
238
- context.on('kubb:file:processing:update', () => {
259
+ context.on('kubb:files:processing:update', ({ files }) => {
239
260
  if (logLevel <= logLevelMap.silent) {
240
261
  return
241
262
  }
242
263
 
243
- state.processedFiles++
264
+ state.processedFiles += files.length
244
265
  })
245
266
 
246
267
  context.on('kubb:generation:end', ({ config }) => {
@@ -307,45 +328,65 @@ export const githubActionsLogger = defineLogger({
307
328
  }
308
329
  })
309
330
 
310
- context.on('kubb:hook:start', async ({ id, command, args }) => {
311
- const commandWithArgs = formatCommandWithArgs(command, args)
312
- const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} started`)
331
+ context.on('kubb:hooks:start', () => {
332
+ if (logLevel <= logLevelMap.silent) {
333
+ return
334
+ }
313
335
 
314
- if (logLevel > logLevelMap.silent) {
315
- if (state.currentConfigs.length === 1) {
316
- openGroup(`Hook ${commandWithArgs}`)
317
- }
318
- console.log(text)
336
+ if (state.currentConfigs.length === 1) {
337
+ openGroup('Hooks')
319
338
  }
320
339
 
321
- // Skip hook execution if no id is provided (e.g., during benchmarks or tests)
322
- if (!id) {
340
+ console.log(getMessage('Hooks started'))
341
+ })
342
+
343
+ context.on('kubb:hooks:end', () => {
344
+ if (logLevel <= logLevelMap.silent) {
323
345
  return
324
346
  }
325
347
 
326
- await runHook({
327
- id,
328
- command,
329
- args,
330
- commandWithArgs,
331
- context,
332
- sink: {
333
- // GHA formats errors with the ::error:: annotation
334
- onStdout: logLevel > logLevelMap.silent ? (s) => console.log(s) : undefined,
335
- onStderr: logLevel > logLevelMap.silent ? (s) => console.error(`::error::${s}`) : undefined,
336
- },
337
- })
348
+ console.log(getMessage('Hooks completed'))
349
+
350
+ if (state.currentConfigs.length === 1) {
351
+ closeGroup('Hooks')
352
+ }
338
353
  })
339
354
 
340
- context.on('kubb:hook:end', ({ command, args }) => {
355
+ context.on('kubb:hook:start', ({ id, command, args }) => {
341
356
  if (logLevel <= logLevelMap.silent) {
342
357
  return
343
358
  }
344
359
 
360
+ if (id) {
361
+ state.hookStarts.set(id, process.hrtime())
362
+ }
363
+
345
364
  const commandWithArgs = formatCommandWithArgs(command, args)
346
- const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} completed`)
365
+ const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} started`)
347
366
 
367
+ if (state.currentConfigs.length === 1) {
368
+ openGroup(`Hook ${commandWithArgs}`)
369
+ }
348
370
  console.log(text)
371
+ })
372
+
373
+ context.on('kubb:hook:end', ({ id, command, args, success, error }) => {
374
+ if (logLevel <= logLevelMap.silent) {
375
+ return
376
+ }
377
+
378
+ const hrStart = id ? state.hookStarts.get(id) : undefined
379
+ if (id) state.hookStarts.delete(id)
380
+ const durationStr = hrStart ? ` in ${formatMsWithColor(getElapsedMs(hrStart))}` : ''
381
+
382
+ const commandWithArgs = formatCommandWithArgs(command, args)
383
+
384
+ if (success) {
385
+ console.log(getMessage(`${styleText('green', '✓')} Hook ${styleText('dim', commandWithArgs)} completed${durationStr}`))
386
+ } else {
387
+ const reason = error?.message ? ` (${error.message})` : ''
388
+ console.log(`::error::Hook ${commandWithArgs} failed${durationStr}${reason}`)
389
+ }
349
390
 
350
391
  if (state.currentConfigs.length === 1) {
351
392
  closeGroup(`Hook ${commandWithArgs}`)
@@ -375,5 +416,10 @@ export const githubActionsLogger = defineLogger({
375
416
  context.on('kubb:lifecycle:end', () => {
376
417
  reset()
377
418
  })
419
+
420
+ return (_commandWithArgs: string, _hookId: string) => ({
421
+ onStdout: logLevel > logLevelMap.silent ? (s: string) => console.log(s) : undefined,
422
+ onStderr: logLevel > logLevelMap.silent ? (s: string) => console.error(`::error::${s}`) : undefined,
423
+ })
378
424
  },
379
425
  })