@kubb/cli 4.12.6 → 4.12.8

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 (35) hide show
  1. package/dist/{generate-DrdKyOOH.cjs → generate-BIq4hG_7.cjs} +350 -176
  2. package/dist/generate-BIq4hG_7.cjs.map +1 -0
  3. package/dist/{generate-M-5j2zqa.js → generate-DbvDQY54.js} +351 -177
  4. package/dist/generate-DbvDQY54.js.map +1 -0
  5. package/dist/index.cjs +3 -5
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.js +3 -5
  8. package/dist/index.js.map +1 -1
  9. package/dist/mcp-B5FWDVjp.js.map +1 -1
  10. package/dist/mcp-D7d_8Bma.cjs.map +1 -1
  11. package/dist/{package-4h1lfDoB.cjs → package-DIKLcg4Q.cjs} +2 -2
  12. package/dist/package-DIKLcg4Q.cjs.map +1 -0
  13. package/dist/package-sWmJhaYS.js +6 -0
  14. package/dist/package-sWmJhaYS.js.map +1 -0
  15. package/dist/validate-Bt922JN-.js.map +1 -1
  16. package/dist/validate-D9LPzg3-.cjs.map +1 -1
  17. package/package.json +4 -4
  18. package/src/commands/generate.ts +10 -0
  19. package/src/commands/mcp.ts +1 -3
  20. package/src/commands/validate.ts +1 -3
  21. package/src/index.ts +0 -9
  22. package/src/loggers/clackLogger.ts +152 -86
  23. package/src/loggers/fileSystemLogger.ts +81 -15
  24. package/src/loggers/githubActionsLogger.ts +137 -57
  25. package/src/loggers/plainLogger.ts +33 -36
  26. package/src/runners/generate.ts +56 -45
  27. package/src/utils/Writables.ts +0 -8
  28. package/src/utils/executeHooks.ts +7 -4
  29. package/src/utils/getSummary.ts +3 -3
  30. package/dist/generate-DrdKyOOH.cjs.map +0 -1
  31. package/dist/generate-M-5j2zqa.js.map +0 -1
  32. package/dist/package-4h1lfDoB.cjs.map +0 -1
  33. package/dist/package-B857a-xb.js +0 -6
  34. package/dist/package-B857a-xb.js.map +0 -1
  35. package/src/utils/parseHrtimeToSeconds.ts +0 -4
@@ -1,10 +1,11 @@
1
1
  import { relative } from 'node:path'
2
+ import process from 'node:process'
2
3
  import * as clack from '@clack/prompts'
3
4
  import { defineLogger, LogLevel } from '@kubb/core'
5
+ import { formatHrtime, formatMs } from '@kubb/core/utils'
4
6
  import { execa } from 'execa'
5
7
  import { default as gradientString } from 'gradient-string'
6
8
  import pc from 'picocolors'
7
-
8
9
  import { getSummary } from '../utils/getSummary.ts'
9
10
  import { ClackWritable } from '../utils/Writables.ts'
10
11
 
@@ -16,9 +17,62 @@ export const clackLogger = defineLogger({
16
17
  name: 'clack',
17
18
  install(context, options) {
18
19
  const logLevel = options?.logLevel || LogLevel.info
19
- const activeProgress = new Map<string, { interval?: NodeJS.Timeout; progressBar: clack.ProgressResult }>()
20
- const spinner = clack.spinner()
21
- let isSpinning = false
20
+ const state = {
21
+ totalPlugins: 0,
22
+ completedPlugins: 0,
23
+ failedPlugins: 0,
24
+ totalFiles: 0,
25
+ processedFiles: 0,
26
+ hrStart: process.hrtime(),
27
+ spinner: clack.spinner(),
28
+ isSpinning: false,
29
+ activeProgress: new Map<string, { interval?: NodeJS.Timeout; progressBar: clack.ProgressResult }>(),
30
+ }
31
+
32
+ function reset() {
33
+ for (const [_key, active] of state.activeProgress) {
34
+ if (active.interval) {
35
+ clearInterval(active.interval)
36
+ }
37
+ active.progressBar?.stop()
38
+ }
39
+
40
+ state.totalPlugins = 0
41
+ state.completedPlugins = 0
42
+ state.failedPlugins = 0
43
+ state.totalFiles = 0
44
+ state.processedFiles = 0
45
+ state.hrStart = process.hrtime()
46
+ state.spinner = clack.spinner()
47
+ state.isSpinning = false
48
+ state.activeProgress.clear()
49
+ }
50
+
51
+ function showProgressStep() {
52
+ if (logLevel <= LogLevel.silent) {
53
+ return
54
+ }
55
+
56
+ const parts: string[] = []
57
+ const duration = formatHrtime(state.hrStart)
58
+
59
+ if (state.totalPlugins > 0) {
60
+ const pluginStr =
61
+ state.failedPlugins > 0
62
+ ? `Plugins ${pc.green(state.completedPlugins.toString())}/${state.totalPlugins} ${pc.red(`(${state.failedPlugins} failed)`)}`
63
+ : `Plugins ${pc.green(state.completedPlugins.toString())}/${state.totalPlugins}`
64
+ parts.push(pluginStr)
65
+ }
66
+
67
+ if (state.totalFiles > 0) {
68
+ parts.push(`Files ${pc.green(state.processedFiles.toString())}/${state.totalFiles}`)
69
+ }
70
+
71
+ if (parts.length > 0) {
72
+ parts.push(`${pc.green(duration)} elapsed`)
73
+ clack.log.step(getMessage(parts.join(pc.dim(' | '))))
74
+ }
75
+ }
22
76
 
23
77
  function getMessage(message: string): string {
24
78
  if (logLevel >= LogLevel.verbose) {
@@ -36,13 +90,13 @@ export const clackLogger = defineLogger({
36
90
  }
37
91
 
38
92
  function startSpinner(text?: string) {
39
- spinner.start(text)
40
- isSpinning = true
93
+ state.spinner.start(text)
94
+ state.isSpinning = true
41
95
  }
42
96
 
43
97
  function stopSpinner(text?: string) {
44
- spinner.stop(text)
45
- isSpinning = false
98
+ state.spinner.stop(text)
99
+ state.isSpinning = false
46
100
  }
47
101
 
48
102
  context.on('info', (message, info = '') => {
@@ -52,8 +106,8 @@ export const clackLogger = defineLogger({
52
106
 
53
107
  const text = getMessage([pc.blue('ℹ'), message, pc.dim(info)].join(' '))
54
108
 
55
- if (isSpinning) {
56
- spinner.message(text)
109
+ if (state.isSpinning) {
110
+ state.spinner.message(text)
57
111
  } else {
58
112
  clack.log.info(text)
59
113
  }
@@ -66,7 +120,7 @@ export const clackLogger = defineLogger({
66
120
 
67
121
  const text = getMessage([pc.blue('✓'), message, logLevel >= LogLevel.info ? pc.dim(info) : undefined].filter(Boolean).join(' '))
68
122
 
69
- if (isSpinning) {
123
+ if (state.isSpinning) {
70
124
  stopSpinner(text)
71
125
  } else {
72
126
  clack.log.success(text)
@@ -88,7 +142,7 @@ export const clackLogger = defineLogger({
88
142
 
89
143
  const text = [pc.red('✗'), error.message].join(' ')
90
144
 
91
- if (isSpinning) {
145
+ if (state.isSpinning) {
92
146
  stopSpinner(getMessage(text))
93
147
  } else {
94
148
  clack.log.error(getMessage(text))
@@ -134,6 +188,7 @@ Run \`npm install -g @kubb/cli\` to update`,
134
188
 
135
189
  context.on('lifecycle:start', (version) => {
136
190
  console.log(gradientString(['#F58517', '#F5A217', '#F55A17'])(`Kubb ${version} 🧩`))
191
+ reset()
137
192
  })
138
193
 
139
194
  context.on('config:start', () => {
@@ -147,7 +202,7 @@ Run \`npm install -g @kubb/cli\` to update`,
147
202
  startSpinner(getMessage('Configuration loading'))
148
203
  })
149
204
 
150
- context.on('config:end', () => {
205
+ context.on('config:end', (_configs) => {
151
206
  if (logLevel <= LogLevel.silent) {
152
207
  return
153
208
  }
@@ -158,9 +213,13 @@ Run \`npm install -g @kubb/cli\` to update`,
158
213
  })
159
214
 
160
215
  context.on('generation:start', (config) => {
216
+ // Initialize progress tracking
217
+ state.totalPlugins = config.plugins?.length || 0
218
+
161
219
  const text = getMessage(['Generation started', config.name ? `for ${pc.dim(config.name)}` : undefined].filter(Boolean).join(' '))
162
220
 
163
221
  clack.intro(text)
222
+ reset()
164
223
  })
165
224
 
166
225
  context.on('plugin:start', (plugin) => {
@@ -180,15 +239,15 @@ Run \`npm install -g @kubb/cli\` to update`,
180
239
 
181
240
  const interval = setInterval(() => {
182
241
  progressBar.advance()
183
- }, 50)
242
+ }, 100)
184
243
 
185
- activeProgress.set(plugin.name, { progressBar, interval })
244
+ state.activeProgress.set(plugin.name, { progressBar, interval })
186
245
  })
187
246
 
188
- context.on('plugin:end', (plugin, duration) => {
247
+ context.on('plugin:end', (plugin, { duration, success }) => {
189
248
  stopSpinner()
190
249
 
191
- const active = activeProgress.get(plugin.name)
250
+ const active = state.activeProgress.get(plugin.name)
192
251
 
193
252
  if (!active || logLevel === LogLevel.silent) {
194
253
  return
@@ -196,11 +255,22 @@ Run \`npm install -g @kubb/cli\` to update`,
196
255
 
197
256
  clearInterval(active.interval)
198
257
 
199
- const durationStr = duration >= 1000 ? `${(duration / 1000).toFixed(2)}s` : `${duration}ms`
200
- const text = getMessage(`${pc.bold(plugin.name)} completed in ${pc.green(durationStr)}`)
258
+ if (success) {
259
+ state.completedPlugins++
260
+ } else {
261
+ state.failedPlugins++
262
+ }
263
+
264
+ const durationStr = formatMs(duration)
265
+ const text = getMessage(
266
+ success ? `${pc.bold(plugin.name)} completed in ${pc.green(durationStr)}` : `${pc.bold(plugin.name)} failed in ${pc.red(durationStr)}`,
267
+ )
201
268
 
202
269
  active.progressBar.stop(text)
203
- activeProgress.delete(plugin.name)
270
+ state.activeProgress.delete(plugin.name)
271
+
272
+ // Show progress step after each plugin
273
+ showProgressStep()
204
274
  })
205
275
 
206
276
  context.on('files:processing:start', (files) => {
@@ -210,6 +280,9 @@ Run \`npm install -g @kubb/cli\` to update`,
210
280
 
211
281
  stopSpinner()
212
282
 
283
+ state.totalFiles = files.length
284
+ state.processedFiles = 0
285
+
213
286
  const text = `Writing ${files.length} files`
214
287
  const progressBar = clack.progress({
215
288
  style: 'block',
@@ -219,7 +292,7 @@ Run \`npm install -g @kubb/cli\` to update`,
219
292
 
220
293
  context.emit('info', text)
221
294
  progressBar.start(getMessage(text))
222
- activeProgress.set('files', { progressBar })
295
+ state.activeProgress.set('files', { progressBar })
223
296
  })
224
297
 
225
298
  context.on('file:processing:update', ({ file, config }) => {
@@ -229,8 +302,10 @@ Run \`npm install -g @kubb/cli\` to update`,
229
302
 
230
303
  stopSpinner()
231
304
 
305
+ state.processedFiles++
306
+
232
307
  const text = `Writing ${relative(config.root, file.path)}`
233
- const active = activeProgress.get('files')
308
+ const active = state.activeProgress.get('files')
234
309
 
235
310
  if (!active) {
236
311
  return
@@ -246,14 +321,17 @@ Run \`npm install -g @kubb/cli\` to update`,
246
321
  stopSpinner()
247
322
 
248
323
  const text = getMessage('Files written successfully')
249
- const active = activeProgress.get('files')
324
+ const active = state.activeProgress.get('files')
250
325
 
251
326
  if (!active) {
252
327
  return
253
328
  }
254
329
 
255
330
  active.progressBar.stop(text)
256
- activeProgress.delete('files')
331
+ state.activeProgress.delete('files')
332
+
333
+ // Show final progress step after files are written
334
+ showProgressStep()
257
335
  })
258
336
 
259
337
  context.on('generation:end', (config) => {
@@ -262,7 +340,49 @@ Run \`npm install -g @kubb/cli\` to update`,
262
340
  clack.outro(text)
263
341
  })
264
342
 
265
- context.on('hook:execute', async ({ command, args }, cb) => {
343
+ context.on('format:start', () => {
344
+ if (logLevel <= LogLevel.silent) {
345
+ return
346
+ }
347
+
348
+ const text = getMessage('Format started')
349
+
350
+ clack.intro(text)
351
+ })
352
+
353
+ context.on('format:end', () => {
354
+ if (logLevel <= LogLevel.silent) {
355
+ return
356
+ }
357
+
358
+ const text = getMessage('Format completed')
359
+
360
+ clack.outro(text)
361
+ })
362
+
363
+ context.on('lint:start', () => {
364
+ if (logLevel <= LogLevel.silent) {
365
+ return
366
+ }
367
+
368
+ const text = getMessage('Lint started')
369
+
370
+ clack.intro(text)
371
+ })
372
+
373
+ context.on('lint:end', () => {
374
+ if (logLevel <= LogLevel.silent) {
375
+ return
376
+ }
377
+
378
+ const text = getMessage('Lint completed')
379
+
380
+ clack.outro(text)
381
+ })
382
+
383
+ context.on('hook:start', async ({ id, command, args }) => {
384
+ const text = getMessage(`Hook ${pc.dim(command)} started`)
385
+
266
386
  if (logLevel <= LogLevel.silent) {
267
387
  try {
268
388
  const result = await execa(command, args, {
@@ -275,7 +395,7 @@ Run \`npm install -g @kubb/cli\` to update`,
275
395
  logs: [result.stdout],
276
396
  })
277
397
 
278
- cb()
398
+ await context.emit('hook:end', { command, id })
279
399
  } catch (err) {
280
400
  const error = new Error('Hook execute failed')
281
401
  error.cause = err
@@ -291,6 +411,8 @@ Run \`npm install -g @kubb/cli\` to update`,
291
411
  return
292
412
  }
293
413
 
414
+ clack.intro(text)
415
+
294
416
  const logger = clack.taskLog({
295
417
  title: getMessage(['Executing hook', logLevel >= LogLevel.info ? pc.dim(`${command} ${args?.join(' ')}`) : undefined].filter(Boolean).join(' ')),
296
418
  })
@@ -309,7 +431,7 @@ Run \`npm install -g @kubb/cli\` to update`,
309
431
  logs: [result.stdout],
310
432
  })
311
433
 
312
- cb()
434
+ await context.emit('hook:end', { command, id })
313
435
  } catch (err) {
314
436
  const error = new Error('Hook execute failed')
315
437
  error.cause = err
@@ -323,62 +445,12 @@ Run \`npm install -g @kubb/cli\` to update`,
323
445
  }
324
446
  })
325
447
 
326
- context.on('format:start', () => {
327
- if (logLevel <= LogLevel.silent) {
328
- return
329
- }
330
-
331
- const text = getMessage('Format started')
332
-
333
- clack.intro(text)
334
- })
335
-
336
- context.on('format:end', () => {
337
- if (logLevel <= LogLevel.silent) {
338
- return
339
- }
340
-
341
- const text = getMessage('Format completed')
342
-
343
- clack.outro(text)
344
- })
345
-
346
- context.on('lint:start', () => {
347
- if (logLevel <= LogLevel.silent) {
348
- return
349
- }
350
-
351
- const text = getMessage('Lint started')
352
-
353
- clack.intro(text)
354
- })
355
-
356
- context.on('lint:end', () => {
357
- if (logLevel <= LogLevel.silent) {
358
- return
359
- }
360
-
361
- const text = getMessage('Lint completed')
362
-
363
- clack.outro(text)
364
- })
365
-
366
- context.on('hook:start', (command) => {
367
- if (logLevel <= LogLevel.silent) {
368
- return
369
- }
370
-
371
- const text = getMessage(`Hook ${pc.dim(command)} started`)
372
-
373
- clack.intro(text)
374
- })
375
-
376
- context.on('hook:end', (command) => {
448
+ context.on('hook:end', ({ command }) => {
377
449
  if (logLevel <= LogLevel.silent) {
378
450
  return
379
451
  }
380
452
 
381
- const text = getMessage(`Hook ${pc.dim(command)} completed`)
453
+ const text = getMessage(`Hook ${pc.dim(command)} successfully executed`)
382
454
 
383
455
  clack.outro(text)
384
456
  })
@@ -421,13 +493,7 @@ Run \`npm install -g @kubb/cli\` to update`,
421
493
  })
422
494
 
423
495
  context.on('lifecycle:end', () => {
424
- for (const [_key, active] of activeProgress) {
425
- if (active.interval) {
426
- clearInterval(active.interval)
427
- }
428
- active.progressBar?.stop()
429
- }
430
- activeProgress.clear()
496
+ reset()
431
497
  })
432
498
  },
433
499
  })
@@ -1,6 +1,7 @@
1
1
  import { resolve } from 'node:path'
2
2
  import { defineLogger } from '@kubb/core'
3
3
  import { write } from '@kubb/core/fs'
4
+ import { formatMs } from '@kubb/core/utils'
4
5
 
5
6
  type CachedEvent = {
6
7
  date: Date
@@ -18,26 +19,34 @@ type CachedEvent = {
18
19
  export const fileSystemLogger = defineLogger({
19
20
  name: 'filesystem',
20
21
  install(context) {
21
- const cachedLogs: Set<CachedEvent> = new Set()
22
- const startDate = Date.now()
22
+ const state = {
23
+ cachedLogs: new Set<CachedEvent>(),
24
+ startDate: Date.now(),
25
+ }
26
+
27
+ function reset() {
28
+ state.cachedLogs = new Set<CachedEvent>()
29
+ state.startDate = Date.now()
30
+ }
23
31
 
24
- async function writeLogs() {
25
- if (cachedLogs.size === 0) {
32
+ async function writeLogs(name?: string) {
33
+ if (state.cachedLogs.size === 0) {
26
34
  return
27
35
  }
28
36
 
29
37
  const files: Record<string, string[]> = {}
30
38
 
31
- for (const log of cachedLogs) {
32
- const fileName = resolve(process.cwd(), '.kubb', log.fileName || `kubb-${startDate}.log`)
39
+ for (const log of state.cachedLogs) {
40
+ const baseName = log.fileName || `${['kubb', name, state.startDate].filter(Boolean).join('-')}.log`
41
+ const pathName = resolve(process.cwd(), '.kubb', baseName)
33
42
 
34
- if (!files[fileName]) {
35
- files[fileName] = []
43
+ if (!files[pathName]) {
44
+ files[pathName] = []
36
45
  }
37
46
 
38
47
  if (log.logs.length > 0) {
39
48
  const timestamp = log.date.toLocaleString()
40
- files[fileName].push(`[${timestamp}]\n${log.logs.join('\n')}`)
49
+ files[pathName].push(`[${timestamp}]\n${log.logs.join('\n')}`)
41
50
  }
42
51
  }
43
52
 
@@ -46,26 +55,83 @@ export const fileSystemLogger = defineLogger({
46
55
  return write(fileName, logs.join('\n\n'))
47
56
  }),
48
57
  )
49
-
50
- cachedLogs.clear()
51
58
  }
52
59
 
60
+ context.on('info', (message, info) => {
61
+ state.cachedLogs.add({
62
+ date: new Date(),
63
+ logs: [`ℹ ${message} ${info}`],
64
+ fileName: undefined,
65
+ })
66
+ })
67
+
68
+ context.on('success', (message, info) => {
69
+ state.cachedLogs.add({
70
+ date: new Date(),
71
+ logs: [`✓ ${message} ${info}`],
72
+ fileName: undefined,
73
+ })
74
+ })
75
+
76
+ context.on('warn', (message, info) => {
77
+ state.cachedLogs.add({
78
+ date: new Date(),
79
+ logs: [`⚠ ${message} ${info}`],
80
+ fileName: undefined,
81
+ })
82
+ })
83
+
84
+ context.on('error', (error) => {
85
+ state.cachedLogs.add({
86
+ date: new Date(),
87
+ logs: [`✗ ${error.message}`, error.stack || 'unknown stack'],
88
+ fileName: undefined,
89
+ })
90
+ })
91
+
53
92
  context.on('debug', (message) => {
54
- cachedLogs.add({
93
+ state.cachedLogs.add({
55
94
  date: new Date(),
56
95
  logs: message.logs,
57
96
  fileName: undefined,
58
97
  })
59
98
  })
60
99
 
61
- context.on('lifecycle:end', async () => {
62
- await writeLogs()
100
+ context.on('plugin:start', (plugin) => {
101
+ state.cachedLogs.add({
102
+ date: new Date(),
103
+ logs: [`Generating ${plugin.name}`],
104
+ fileName: undefined,
105
+ })
106
+ })
107
+
108
+ context.on('plugin:end', (plugin, { duration, success }) => {
109
+ const durationStr = formatMs(duration)
110
+
111
+ state.cachedLogs.add({
112
+ date: new Date(),
113
+ logs: [success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`],
114
+ fileName: undefined,
115
+ })
116
+ })
117
+
118
+ context.on('files:processing:start', (files) => {
119
+ state.cachedLogs.add({
120
+ date: new Date(),
121
+ logs: [`Start ${files.length} writing:`, ...files.map((file) => file.path)],
122
+ fileName: undefined,
123
+ })
124
+ })
125
+
126
+ context.on('generation:end', async (config) => {
127
+ await writeLogs(config.name)
128
+ reset()
63
129
  })
64
130
 
65
131
  // Fallback: Write logs on process exit to handle crashes
66
132
  const exitHandler = () => {
67
133
  // Synchronous write on exit - best effort
68
- if (cachedLogs.size > 0) {
134
+ if (state.cachedLogs.size > 0) {
69
135
  writeLogs().catch(() => {
70
136
  // Ignore errors on exit
71
137
  })