@kubb/cli 5.0.0-beta.14 → 5.0.0-beta.15

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 (42) hide show
  1. package/dist/{agent-SCGeyEIE.js → agent-BTvJ7IUq.js} +2 -2
  2. package/dist/{agent-SCGeyEIE.js.map → agent-BTvJ7IUq.js.map} +1 -1
  3. package/dist/{agent-Dn1Kfd_R.cjs → agent-vzaMEbPS.cjs} +2 -2
  4. package/dist/{agent-Dn1Kfd_R.cjs.map → agent-vzaMEbPS.cjs.map} +1 -1
  5. package/dist/{generate-CLYE3RWE.js → generate-BczcDWHS.js} +2 -2
  6. package/dist/{generate-CLYE3RWE.js.map → generate-BczcDWHS.js.map} +1 -1
  7. package/dist/{generate-z_83cgcO.cjs → generate-BkKnYYok.cjs} +2 -2
  8. package/dist/{generate-z_83cgcO.cjs.map → generate-BkKnYYok.cjs.map} +1 -1
  9. package/dist/index.cjs +6 -6
  10. package/dist/index.js +6 -6
  11. package/dist/{init-BvHHgquV.js → init-CGLK93Ny.js} +2 -2
  12. package/dist/{init-BvHHgquV.js.map → init-CGLK93Ny.js.map} +1 -1
  13. package/dist/{init-S-eLRaep.cjs → init-DH-tkhDa.cjs} +2 -2
  14. package/dist/{init-S-eLRaep.cjs.map → init-DH-tkhDa.cjs.map} +1 -1
  15. package/dist/{mcp-BkBwpF6r.cjs → mcp-BzuRPhN0.cjs} +2 -2
  16. package/dist/{mcp-BkBwpF6r.cjs.map → mcp-BzuRPhN0.cjs.map} +1 -1
  17. package/dist/{mcp-DEjoAmP2.js → mcp-CmSOtsiY.js} +2 -2
  18. package/dist/{mcp-DEjoAmP2.js.map → mcp-CmSOtsiY.js.map} +1 -1
  19. package/dist/package-B3fEVBWy.js +6 -0
  20. package/dist/package-B3fEVBWy.js.map +1 -0
  21. package/dist/{package-C7ZmbQd7.cjs → package-rMKbcyw1.cjs} +2 -2
  22. package/dist/package-rMKbcyw1.cjs.map +1 -0
  23. package/dist/{run-BNvaRFvz.js → run-BvsacB4B.js} +127 -72
  24. package/dist/run-BvsacB4B.js.map +1 -0
  25. package/dist/{run-DK-3QOOS.cjs → run-ftIEu5xK.cjs} +127 -72
  26. package/dist/run-ftIEu5xK.cjs.map +1 -0
  27. package/dist/{validate-C3hmnp-x.cjs → validate--WZ6E0gu.cjs} +2 -2
  28. package/dist/{validate-C3hmnp-x.cjs.map → validate--WZ6E0gu.cjs.map} +1 -1
  29. package/dist/{validate-qvniflGp.js → validate-D3p-TOu4.js} +2 -2
  30. package/dist/{validate-qvniflGp.js.map → validate-D3p-TOu4.js.map} +1 -1
  31. package/package.json +6 -6
  32. package/src/loggers/clackLogger.ts +45 -55
  33. package/src/loggers/githubActionsLogger.ts +64 -6
  34. package/src/loggers/plainLogger.ts +47 -11
  35. package/src/loggers/utils.ts +4 -1
  36. package/src/runners/generate/run.ts +20 -8
  37. package/src/runners/generate/utils.ts +2 -2
  38. package/dist/package-C7ZmbQd7.cjs.map +0 -1
  39. package/dist/package-D5eM8mZw.js +0 -6
  40. package/dist/package-D5eM8mZw.js.map +0 -1
  41. package/dist/run-BNvaRFvz.js.map +0 -1
  42. package/dist/run-DK-3QOOS.cjs.map +0 -1
@@ -1,5 +1,6 @@
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
5
  import { buildProgressLine, formatCommandWithArgs, formatMessage } from './utils.ts'
5
6
 
@@ -18,9 +19,12 @@ export const githubActionsLogger = defineLogger({
18
19
  processedFiles: 0,
19
20
  hrStart: process.hrtime(),
20
21
  currentConfigs: [] as Array<Config>,
22
+ hookStarts: new Map<string, [number, number]>(),
23
+ openGroupDepth: 0,
21
24
  }
22
25
 
23
26
  function reset() {
27
+ closeAllGroups()
24
28
  state.totalPlugins = 0
25
29
  state.completedPlugins = 0
26
30
  state.failedPlugins = 0
@@ -28,6 +32,7 @@ export const githubActionsLogger = defineLogger({
28
32
  state.processedFiles = 0
29
33
  state.hrStart = process.hrtime()
30
34
  state.currentConfigs = []
35
+ state.hookStarts.clear()
31
36
  }
32
37
 
33
38
  function showProgressStep() {
@@ -47,10 +52,19 @@ export const githubActionsLogger = defineLogger({
47
52
 
48
53
  function openGroup(name: string) {
49
54
  console.log(`::group::${name}`)
55
+ state.openGroupDepth++
50
56
  }
51
57
 
52
58
  function closeGroup(_name: string) {
53
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
+ }
54
68
  }
55
69
 
56
70
  context.on('kubb:info', ({ message, info = '' }) => {
@@ -86,6 +100,10 @@ export const githubActionsLogger = defineLogger({
86
100
  context.on('kubb:error', ({ error }) => {
87
101
  const caused = toCause(error)
88
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
+
89
107
  if (logLevel <= logLevelMap.silent) {
90
108
  return
91
109
  }
@@ -115,6 +133,10 @@ export const githubActionsLogger = defineLogger({
115
133
  reset()
116
134
  })
117
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
+
118
140
  context.on('kubb:config:start', () => {
119
141
  if (logLevel <= logLevelMap.silent) {
120
142
  return
@@ -306,11 +328,39 @@ export const githubActionsLogger = defineLogger({
306
328
  }
307
329
  })
308
330
 
309
- context.on('kubb:hook:start', ({ command, args }) => {
331
+ context.on('kubb:hooks:start', () => {
332
+ if (logLevel <= logLevelMap.silent) {
333
+ return
334
+ }
335
+
336
+ if (state.currentConfigs.length === 1) {
337
+ openGroup('Hooks')
338
+ }
339
+
340
+ console.log(getMessage('Hooks started'))
341
+ })
342
+
343
+ context.on('kubb:hooks:end', () => {
310
344
  if (logLevel <= logLevelMap.silent) {
311
345
  return
312
346
  }
313
347
 
348
+ console.log(getMessage('Hooks completed'))
349
+
350
+ if (state.currentConfigs.length === 1) {
351
+ closeGroup('Hooks')
352
+ }
353
+ })
354
+
355
+ context.on('kubb:hook:start', ({ id, command, args }) => {
356
+ if (logLevel <= logLevelMap.silent) {
357
+ return
358
+ }
359
+
360
+ if (id) {
361
+ state.hookStarts.set(id, process.hrtime())
362
+ }
363
+
314
364
  const commandWithArgs = formatCommandWithArgs(command, args)
315
365
  const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} started`)
316
366
 
@@ -320,15 +370,23 @@ export const githubActionsLogger = defineLogger({
320
370
  console.log(text)
321
371
  })
322
372
 
323
- context.on('kubb:hook:end', ({ command, args }) => {
373
+ context.on('kubb:hook:end', ({ id, command, args, success, error }) => {
324
374
  if (logLevel <= logLevelMap.silent) {
325
375
  return
326
376
  }
327
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
+
328
382
  const commandWithArgs = formatCommandWithArgs(command, args)
329
- const text = getMessage(`Hook ${styleText('dim', commandWithArgs)} completed`)
330
383
 
331
- console.log(text)
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
+ }
332
390
 
333
391
  if (state.currentConfigs.length === 1) {
334
392
  closeGroup(`Hook ${commandWithArgs}`)
@@ -359,7 +417,7 @@ export const githubActionsLogger = defineLogger({
359
417
  reset()
360
418
  })
361
419
 
362
- return (_commandWithArgs: string) => ({
420
+ return (_commandWithArgs: string, _hookId: string) => ({
363
421
  onStdout: logLevel > logLevelMap.silent ? (s: string) => console.log(s) : undefined,
364
422
  onStderr: logLevel > logLevelMap.silent ? (s: string) => console.error(`::error::${s}`) : undefined,
365
423
  })
@@ -1,5 +1,6 @@
1
1
  import { relative } from 'node:path'
2
- import { formatMs, toCause } from '@internals/utils'
2
+ import process from 'node:process'
3
+ import { formatMs, getElapsedMs, toCause } from '@internals/utils'
3
4
  import { defineLogger, logLevel as logLevelMap } from '@kubb/core'
4
5
  import { SUMMARY_SEPARATOR } from '../constants.ts'
5
6
  import { getSummary } from './utils.ts'
@@ -12,6 +13,7 @@ export const plainLogger = defineLogger({
12
13
  name: 'plain',
13
14
  install(context, options) {
14
15
  const logLevel = options?.logLevel ?? logLevelMap.info
16
+ const hookStarts = new Map<string, [number, number]>()
15
17
 
16
18
  function getMessage(message: string): string {
17
19
  return formatMessage(message, logLevel)
@@ -72,8 +74,16 @@ export const plainLogger = defineLogger({
72
74
  }
73
75
  })
74
76
 
75
- context.on('kubb:lifecycle:start', () => {
76
- console.log('Kubb CLI 🧩')
77
+ context.on('kubb:lifecycle:start', ({ version }) => {
78
+ console.log(`Kubb CLI v${version}`)
79
+ })
80
+
81
+ context.on('kubb:version:new', ({ currentVersion, latestVersion }) => {
82
+ if (logLevel <= logLevelMap.silent) {
83
+ return
84
+ }
85
+
86
+ console.log(getMessage(`Update available: v${currentVersion} → v${latestVersion}. Run \`npm install -g @kubb/cli\` to update.`))
77
87
  })
78
88
 
79
89
  context.on('kubb:config:start', () => {
@@ -198,26 +208,52 @@ export const plainLogger = defineLogger({
198
208
  console.log(text)
199
209
  })
200
210
 
201
- context.on('kubb:hook:start', ({ command, args }) => {
211
+ context.on('kubb:hooks:start', () => {
202
212
  if (logLevel <= logLevelMap.silent) {
203
213
  return
204
214
  }
205
215
 
206
- const commandWithArgs = formatCommandWithArgs(command, args)
207
- const text = getMessage(`Hook ${commandWithArgs} started`)
216
+ console.log(getMessage('Hooks started'))
217
+ })
208
218
 
209
- console.log(text)
219
+ context.on('kubb:hooks:end', () => {
220
+ if (logLevel <= logLevelMap.silent) {
221
+ return
222
+ }
223
+
224
+ console.log(getMessage('Hooks completed'))
210
225
  })
211
226
 
212
- context.on('kubb:hook:end', ({ command, args }) => {
227
+ context.on('kubb:hook:start', ({ id, command, args }) => {
213
228
  if (logLevel <= logLevelMap.silent) {
214
229
  return
215
230
  }
216
231
 
232
+ if (id) {
233
+ hookStarts.set(id, process.hrtime())
234
+ }
235
+
217
236
  const commandWithArgs = formatCommandWithArgs(command, args)
218
- const text = getMessage(`Hook ${commandWithArgs} completed`)
237
+ console.log(getMessage(`Hook ${commandWithArgs} started`))
238
+ })
219
239
 
220
- console.log(text)
240
+ context.on('kubb:hook:end', ({ id, command, args, success, error }) => {
241
+ if (logLevel <= logLevelMap.silent) {
242
+ return
243
+ }
244
+
245
+ const hrStart = id ? hookStarts.get(id) : undefined
246
+ if (id) hookStarts.delete(id)
247
+ const durationStr = hrStart ? ` in ${formatMs(getElapsedMs(hrStart))}` : ''
248
+
249
+ const commandWithArgs = formatCommandWithArgs(command, args)
250
+
251
+ if (success) {
252
+ console.log(getMessage(`✓ Hook ${commandWithArgs} completed${durationStr}`))
253
+ } else {
254
+ const reason = error?.message ? ` (${error.message})` : ''
255
+ console.log(getMessage(`✗ Hook ${commandWithArgs} failed${durationStr}${reason}`))
256
+ }
221
257
  })
222
258
 
223
259
  context.on('kubb:generation:summary', ({ config, pluginTimings, status, hrStart, failedPlugins, filesCreated }) => {
@@ -235,7 +271,7 @@ export const plainLogger = defineLogger({
235
271
  console.log(SUMMARY_SEPARATOR)
236
272
  })
237
273
 
238
- return (_commandWithArgs: string) => ({
274
+ return (_commandWithArgs: string, _hookId: string) => ({
239
275
  onStdout: logLevel > logLevelMap.silent ? (s: string) => console.log(s) : undefined,
240
276
  onStderr: logLevel > logLevelMap.silent ? (s: string) => console.error(s) : undefined,
241
277
  })
@@ -43,8 +43,11 @@ export type HookSinkOptions = HookOutputSink & {
43
43
  /**
44
44
  * Factory called once per hook command to build the output sink and streaming flag.
45
45
  * The function should set up any logger UI (e.g., spinner) and return callbacks that forward subprocess output to it.
46
+ *
47
+ * `hookId` is the same id passed to `kubb:hook:start` / `kubb:hook:end`, letting the logger
48
+ * correlate streamed output with the active UI element (e.g., a clack `taskLog`) it created in the start handler.
46
49
  */
47
- export type HookSinkFactory = (commandWithArgs: string) => HookSinkOptions | undefined
50
+ export type HookSinkFactory = (commandWithArgs: string, hookId: string) => HookSinkOptions | undefined
48
51
 
49
52
  /**
50
53
  * Logger variant that may return a {@link HookSinkFactory} from `install`.
@@ -33,6 +33,7 @@ type RunToolPassOptions = {
33
33
  outputPath: string
34
34
  logLevel: number
35
35
  hooks: AsyncEventEmitter<KubbHooks>
36
+ makeSink?: HookSinkFactory
36
37
  onStart: () => Promise<void>
37
38
  onEnd: () => Promise<void>
38
39
  }
@@ -67,6 +68,7 @@ async function runToolPass({
67
68
  outputPath,
68
69
  logLevel,
69
70
  hooks,
71
+ makeSink,
70
72
  onStart,
71
73
  onEnd,
72
74
  }: RunToolPassOptions) {
@@ -99,18 +101,21 @@ async function runToolPass({
99
101
 
100
102
  try {
101
103
  const hookArgs = toolConfig.args(outputPath)
104
+ const commandWithArgs = [toolConfig.command, ...hookArgs].join(' ')
102
105
  const hookEndPromise = waitForHookEnd(hooks, hookId, () => hooks.emit('kubb:success', { message: successMessage }), toolConfig.errorMessage)
103
106
 
104
107
  await hooks.emit('kubb:hook:start', { id: hookId, command: toolConfig.command, args: hookArgs })
105
108
 
109
+ const { stream = false, onLine, onStdout, onStderr } = makeSink?.(commandWithArgs, hookId) ?? {}
110
+
106
111
  runHook({
107
112
  id: hookId,
108
113
  command: toolConfig.command,
109
114
  args: hookArgs,
110
- commandWithArgs: [toolConfig.command, ...hookArgs].join(' '),
115
+ commandWithArgs,
111
116
  context: hooks,
112
- stream: false,
113
- sink: {},
117
+ stream,
118
+ sink: { onLine, onStdout, onStderr },
114
119
  }).catch(() => {})
115
120
 
116
121
  await hookEndPromise
@@ -141,10 +146,12 @@ async function generate(options: GenerateProps): Promise<boolean> {
141
146
  } satisfies Config
142
147
 
143
148
  const kubb = createKubb(config, { hooks })
144
- await kubb.setup()
145
149
 
146
150
  await hooks.emit('kubb:generation:start', { config })
147
151
  await hooks.emit('kubb:info', { message: config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', info: inputPath })
152
+
153
+ await kubb.setup()
154
+
148
155
  await hooks.emit('kubb:info', { message: config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', info: inputPath })
149
156
 
150
157
  const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild()
@@ -206,7 +213,7 @@ async function generate(options: GenerateProps): Promise<boolean> {
206
213
  ].filter(Boolean) as Omit<RunToolPassOptions, 'configName' | 'outputPath' | 'logLevel' | 'hooks'>[]
207
214
 
208
215
  for (const pass of toolPasses) {
209
- await runToolPass({ ...pass, configName: config.name, outputPath, logLevel, hooks })
216
+ await runToolPass({ ...pass, configName: config.name, outputPath, logLevel, hooks, makeSink })
210
217
  }
211
218
 
212
219
  if (config.hooks) {
@@ -252,17 +259,19 @@ export async function run({ input, configPath, logLevel: logLevelKey, watch }: G
252
259
 
253
260
  const makeSink = await setupLogger(hooks, { logLevel })
254
261
 
262
+ await hooks.emit('kubb:lifecycle:start', { version })
263
+
255
264
  await checkForUpdate(hooks)
256
265
 
257
266
  try {
267
+ await hooks.emit('kubb:config:start')
268
+
258
269
  const { configs, configPath: resolvedConfigPath } = await getConfigs({ configPath, input })
259
270
  const relativeConfigPath = path.relative(process.cwd(), resolvedConfigPath)
260
271
 
261
- await hooks.emit('kubb:config:start')
262
272
  await hooks.emit('kubb:info', { message: 'Config loaded', info: relativeConfigPath })
263
273
  await hooks.emit('kubb:success', { message: 'Config loaded successfully', info: relativeConfigPath })
264
274
  await hooks.emit('kubb:config:end', { configs })
265
- await hooks.emit('kubb:lifecycle:start', { version })
266
275
 
267
276
  let anyFailed = false
268
277
  for (const config of configs) {
@@ -270,7 +279,10 @@ export async function run({ input, configPath, logLevel: logLevelKey, watch }: G
270
279
  await startWatcher(
271
280
  [input || config.input.path],
272
281
  async (paths) => {
273
- hooks.removeAll()
282
+ // Don't removeAll() — that would also drop logger and lifecycle listeners.
283
+ // Plugin and middleware listeners are already disposed by safeBuild's
284
+ // setupResult.dispose() in its finally block, so re-running generate()
285
+ // on the same hooks emitter is safe.
274
286
  await generate({ input, config, logLevel, hooks, makeSink })
275
287
  clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
276
288
  },
@@ -107,7 +107,7 @@ export async function getConfigs({ configPath, input }: GetConfigsOptions): Prom
107
107
  type ExecuteHooksOptions = {
108
108
  configHooks: NonNullable<Config['hooks']>
109
109
  hooks: AsyncEventEmitter<KubbHooks>
110
- makeSink?: (commandWithArgs: string) => HookSinkOptions | undefined
110
+ makeSink?: (commandWithArgs: string, hookId: string) => HookSinkOptions | undefined
111
111
  }
112
112
 
113
113
  /**
@@ -125,7 +125,7 @@ export async function executeHooks({ configHooks, hooks, makeSink }: ExecuteHook
125
125
 
126
126
  await hooks.emit('kubb:hook:start', { id: hookId, command: cmd, args })
127
127
 
128
- const { stream = false, onLine, onStdout, onStderr } = makeSink?.(commandWithArgs) ?? {}
128
+ const { stream = false, onLine, onStdout, onStderr } = makeSink?.(commandWithArgs, hookId) ?? {}
129
129
  await runHook({ id: hookId, command: cmd, args, commandWithArgs, context: hooks, stream, sink: { onLine, onStdout, onStderr } })
130
130
  }
131
131
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"package-C7ZmbQd7.cjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
@@ -1,6 +0,0 @@
1
- //#region package.json
2
- var version = "5.0.0-beta.14";
3
- //#endregion
4
- export { version as t };
5
-
6
- //# sourceMappingURL=package-D5eM8mZw.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"package-D5eM8mZw.js","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}