@kubb/cli 5.0.0-beta.62 → 5.0.0-beta.64

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 (93) hide show
  1. package/bin/kubb.js +9 -3
  2. package/dist/{Telemetry-DrppRqqW.cjs → Telemetry-CfPP7MYq.cjs} +6 -5
  3. package/dist/Telemetry-CfPP7MYq.cjs.map +1 -0
  4. package/dist/{Telemetry-CVdyJarO.js → Telemetry-uAr3lK_-.js} +4 -3
  5. package/dist/Telemetry-uAr3lK_-.js.map +1 -0
  6. package/dist/{define-C4AB3POr.cjs → define-Cq9hOxpK.cjs} +2 -2
  7. package/dist/{define-C4AB3POr.cjs.map → define-Cq9hOxpK.cjs.map} +1 -1
  8. package/dist/{define-C63T4jp6.js → define-GYXr00ky.js} +2 -2
  9. package/dist/{define-C63T4jp6.js.map → define-GYXr00ky.js.map} +1 -1
  10. package/dist/{errors-BsemQCMn.js → errors-Dc_d7BfX.js} +2 -2
  11. package/dist/{errors-BsemQCMn.js.map → errors-Dc_d7BfX.js.map} +1 -1
  12. package/dist/{errors-DykI11xo.cjs → errors-gxFK0vrp.cjs} +2 -2
  13. package/dist/{errors-DykI11xo.cjs.map → errors-gxFK0vrp.cjs.map} +1 -1
  14. package/dist/{generate-BI0qGM4M.js → generate-DHQnl_F1.js} +4 -4
  15. package/dist/{generate-BI0qGM4M.js.map → generate-DHQnl_F1.js.map} +1 -1
  16. package/dist/{generate-C4BcNMRq.cjs → generate-svfhwhEK.cjs} +4 -4
  17. package/dist/{generate-C4BcNMRq.cjs.map → generate-svfhwhEK.cjs.map} +1 -1
  18. package/dist/index.cjs +13 -8
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.ts +6 -1
  21. package/dist/index.js +13 -8
  22. package/dist/index.js.map +1 -1
  23. package/dist/{init-BzS2oVRl.js → init-B02NN_88.js} +5 -5
  24. package/dist/{init-BzS2oVRl.js.map → init-B02NN_88.js.map} +1 -1
  25. package/dist/{init-DbmOhX2-.cjs → init-Bg_68Ok6.cjs} +5 -5
  26. package/dist/{init-DbmOhX2-.cjs.map → init-Bg_68Ok6.cjs.map} +1 -1
  27. package/dist/{mcp-AyUriTgO.cjs → mcp-DUzIXdRV.cjs} +7 -25
  28. package/dist/mcp-DUzIXdRV.cjs.map +1 -0
  29. package/dist/mcp-ZJQksVUU.js +21 -0
  30. package/dist/mcp-ZJQksVUU.js.map +1 -0
  31. package/dist/package-BoXPsuNH.js +6 -0
  32. package/dist/package-BoXPsuNH.js.map +1 -0
  33. package/dist/{package-DXJ46ia8.cjs → package-Cdu8eJMb.cjs} +2 -2
  34. package/dist/package-Cdu8eJMb.cjs.map +1 -0
  35. package/dist/{run-DpKny2hT.cjs → run-B4rIAnch.cjs} +7 -7
  36. package/dist/run-B4rIAnch.cjs.map +1 -0
  37. package/dist/{run-9ZhHuNZQ.cjs → run-BM1t8CSM.cjs} +10 -10
  38. package/dist/run-BM1t8CSM.cjs.map +1 -0
  39. package/dist/{run-h8NTawHO.js → run-BlFVmIJl.js} +9 -9
  40. package/dist/run-BlFVmIJl.js.map +1 -0
  41. package/dist/{run-BQO_tPlc.cjs → run-CBc896in.cjs} +5 -5
  42. package/dist/{run-BQO_tPlc.cjs.map → run-CBc896in.cjs.map} +1 -1
  43. package/dist/{run-Drs-S6sa.cjs → run-CD3BE8yP.cjs} +70 -70
  44. package/dist/run-CD3BE8yP.cjs.map +1 -0
  45. package/dist/{run-CYnDu3ch.js → run-D_dWNFhX.js} +4 -4
  46. package/dist/{run-CYnDu3ch.js.map → run-D_dWNFhX.js.map} +1 -1
  47. package/dist/{run-C_7kOXnc.js → run-DkW2VQBq.js} +68 -68
  48. package/dist/run-DkW2VQBq.js.map +1 -0
  49. package/dist/{run-BG7Giryi.js → run-DzYeru-0.js} +3 -3
  50. package/dist/run-DzYeru-0.js.map +1 -0
  51. package/dist/{tools-_Xp8-_zy.cjs → tools-CZT9wSA6.cjs} +2 -2
  52. package/dist/{tools-_Xp8-_zy.cjs.map → tools-CZT9wSA6.cjs.map} +1 -1
  53. package/dist/{tools-BU99bhi8.js → tools-D0UogMU3.js} +2 -2
  54. package/dist/{tools-BU99bhi8.js.map → tools-D0UogMU3.js.map} +1 -1
  55. package/dist/{validate-CfdPbbz_.cjs → validate-B3EwltHu.cjs} +5 -5
  56. package/dist/{validate-CfdPbbz_.cjs.map → validate-B3EwltHu.cjs.map} +1 -1
  57. package/dist/{validate-jZWMwN4r.js → validate-DXYcZCm_.js} +5 -5
  58. package/dist/{validate-jZWMwN4r.js.map → validate-DXYcZCm_.js.map} +1 -1
  59. package/package.json +7 -8
  60. package/dist/Telemetry-CVdyJarO.js.map +0 -1
  61. package/dist/Telemetry-DrppRqqW.cjs.map +0 -1
  62. package/dist/mcp-AyUriTgO.cjs.map +0 -1
  63. package/dist/mcp-BY4D1lHt.js +0 -39
  64. package/dist/mcp-BY4D1lHt.js.map +0 -1
  65. package/dist/package-BSv0yWfQ.js +0 -6
  66. package/dist/package-BSv0yWfQ.js.map +0 -1
  67. package/dist/package-DXJ46ia8.cjs.map +0 -1
  68. package/dist/run-9ZhHuNZQ.cjs.map +0 -1
  69. package/dist/run-BG7Giryi.js.map +0 -1
  70. package/dist/run-C_7kOXnc.js.map +0 -1
  71. package/dist/run-DpKny2hT.cjs.map +0 -1
  72. package/dist/run-Drs-S6sa.cjs.map +0 -1
  73. package/dist/run-h8NTawHO.js.map +0 -1
  74. package/src/Telemetry.ts +0 -297
  75. package/src/commands/generate.ts +0 -65
  76. package/src/commands/init.ts +0 -51
  77. package/src/commands/mcp.ts +0 -36
  78. package/src/commands/validate.ts +0 -22
  79. package/src/constants.ts +0 -19
  80. package/src/index.ts +0 -27
  81. package/src/loggers/clackLogger.ts +0 -392
  82. package/src/loggers/defineLogger.ts +0 -59
  83. package/src/loggers/plainLogger.ts +0 -199
  84. package/src/loggers/types.ts +0 -6
  85. package/src/loggers/utils.ts +0 -249
  86. package/src/runners/generate/run.ts +0 -399
  87. package/src/runners/generate/utils.ts +0 -229
  88. package/src/runners/init/run.ts +0 -210
  89. package/src/runners/init/utils.ts +0 -39
  90. package/src/runners/mcp/run.ts +0 -37
  91. package/src/runners/validate/run.ts +0 -63
  92. /package/dist/{chunk-Bx3C2hgW.cjs → rolldown-runtime-Bx3C2hgW.cjs} +0 -0
  93. /package/dist/{chunk-C0LytTxp.js → rolldown-runtime-C0LytTxp.js} +0 -0
@@ -1,51 +0,0 @@
1
- import { defineCommand } from '@internals/utils'
2
- import { version } from '../../package.json'
3
-
4
- export const command = defineCommand({
5
- name: 'init',
6
- description:
7
- 'Scaffold a kubb.config.ts and install plugins for code generation from an OpenAPI spec. Run without flags for interactive setup, or pass --input, --output, and --plugins to skip the prompts.',
8
- examples: [
9
- 'kubb init',
10
- 'kubb init --yes',
11
- 'kubb init --input ./openapi.yaml --output ./src/gen --plugins plugin-ts,plugin-zod',
12
- 'kubb init --plugins plugin-ts,plugin-client,plugin-react-query',
13
- ],
14
- options: {
15
- yes: {
16
- type: 'boolean',
17
- description: 'Skip prompts and use default options',
18
- short: 'y',
19
- default: false,
20
- },
21
- input: {
22
- type: 'string',
23
- description: 'Path to the OpenAPI specification',
24
- short: 'i',
25
- hint: 'path',
26
- },
27
- output: {
28
- type: 'string',
29
- description: 'Output directory for generated files',
30
- short: 'o',
31
- hint: 'path',
32
- },
33
- plugins: {
34
- type: 'string',
35
- description:
36
- 'Comma-separated list of plugins to use (plugin-ts, plugin-client, plugin-react-query, plugin-vue-query, plugin-zod, plugin-faker, plugin-msw, plugin-cypress, plugin-mcp, plugin-redoc)',
37
- hint: 'plugin-ts,plugin-zod,...',
38
- },
39
- },
40
- async run({ values }) {
41
- const { run } = await import('../runners/init/run.ts')
42
-
43
- await run({
44
- yes: values.yes,
45
- version,
46
- input: values.input,
47
- output: values.output,
48
- plugins: values.plugins,
49
- })
50
- },
51
- })
@@ -1,36 +0,0 @@
1
- import { defineCommand } from '@internals/utils'
2
- import { version } from '../../package.json'
3
-
4
- export const command = defineCommand({
5
- name: 'mcp',
6
- description:
7
- 'Start a Model Context Protocol (MCP) server that exposes Kubb code generation as tools for AI assistants. Once running, configure your AI client (Claude, Cursor, Windsurf, etc.) to connect to it — the assistant can then call kubb generate directly without leaving the chat.',
8
- examples: [
9
- 'kubb mcp',
10
- 'kubb mcp --port 3001',
11
- '# Then add to your MCP client config:',
12
- '# { "mcpServers": { "kubb": { "command": "npx", "args": ["kubb", "mcp"] } } }',
13
- ],
14
- options: {
15
- port: {
16
- type: 'string',
17
- short: 'p',
18
- description: 'Port for HTTP MCP server (omit for stdio)',
19
- hint: 'number',
20
- },
21
- host: {
22
- type: 'string',
23
- description: 'Hostname to bind to (HTTP mode only)',
24
- default: 'localhost',
25
- },
26
- },
27
- async run({ values }) {
28
- const { run } = await import('../runners/mcp/run.ts')
29
-
30
- await run({
31
- version,
32
- port: values.port,
33
- host: values.host,
34
- })
35
- },
36
- })
@@ -1,22 +0,0 @@
1
- import { defineCommand } from '@internals/utils'
2
- import { version } from '../../package.json'
3
-
4
- export const command = defineCommand({
5
- name: 'validate',
6
- description:
7
- 'Parse and validate an OpenAPI/Swagger file for structural correctness. Reports schema errors, missing required fields, and malformed references. Use this before running generate to catch spec issues early.',
8
- examples: ['kubb validate --input ./openapi.yaml', 'kubb validate --input https://petstore3.swagger.io/api/v3/openapi.json'],
9
- options: {
10
- input: {
11
- type: 'string',
12
- description: 'Path or URL to the OpenAPI/Swagger file to validate',
13
- short: 'i',
14
- required: true,
15
- },
16
- },
17
- async run({ values }) {
18
- const { run } = await import('../runners/validate/run.ts')
19
-
20
- await run({ input: values.input, version })
21
- },
22
- })
package/src/constants.ts DELETED
@@ -1,19 +0,0 @@
1
- /**
2
- * NPM registry endpoint used to check for @kubb/cli updates.
3
- */
4
- export const KUBB_NPM_PACKAGE_URL = 'https://registry.npmjs.org/@kubb/cli/latest' as const
5
-
6
- /**
7
- * OpenTelemetry ingestion endpoint for anonymous usage telemetry.
8
- */
9
- export const OTLP_ENDPOINT = 'https://otlp.kubb.dev' as const
10
-
11
- /**
12
- * Glob pattern for paths the file watcher ignores.
13
- */
14
- export const WATCHER_IGNORED_PATHS = '**/{.git,node_modules}/**' as const
15
-
16
- /**
17
- * Flags that short-circuit execution (help and version). The telemetry notice is suppressed for these.
18
- */
19
- export const QUIET_FLAGS = new Set<string>(['--help', '-h', '--version', '-v'])
package/src/index.ts DELETED
@@ -1,27 +0,0 @@
1
- import { styleText } from 'node:util'
2
- import { createCLI } from '@internals/utils'
3
- import { Telemetry } from './Telemetry.ts'
4
- import { version } from '../package.json'
5
- import { QUIET_FLAGS } from './constants.ts'
6
-
7
- const cli = createCLI()
8
- export async function run(argv: Array<string> = process.argv): Promise<void> {
9
- const isQuietFlag = argv.some((arg) => QUIET_FLAGS.has(arg))
10
-
11
- if (!Telemetry.isDisabled && !isQuietFlag) {
12
- console.log(
13
- `${styleText('yellow', 'Notice:')} Kubb collects anonymous telemetry data to help improve the tool. No personal data or file contents are collected. \nTo disable, set ${styleText('cyan', 'KUBB_DISABLE_TELEMETRY=1')}.\n`,
14
- )
15
- }
16
-
17
- const { command: generateCommand } = await import('./commands/generate.ts')
18
- const { command: validateCommand } = await import('./commands/validate.ts')
19
- const { command: mcpCommand } = await import('./commands/mcp.ts')
20
- const { command: initCommand } = await import('./commands/init.ts')
21
-
22
- await cli.run([generateCommand, validateCommand, mcpCommand, initCommand], argv, {
23
- programName: 'kubb',
24
- defaultCommandName: 'generate',
25
- version,
26
- })
27
- }
@@ -1,392 +0,0 @@
1
- import { relative } from 'node:path'
2
- import process from 'node:process'
3
- import { styleText } from 'node:util'
4
- import * as clack from '@clack/prompts'
5
- import { formatMsWithColor, getElapsedMs, getIntro, toCause } from '@internals/utils'
6
- import { Diagnostics, type KubbHooks, logLevel as logLevelMap } from '@kubb/core'
7
- import { defineLogger } from './defineLogger.ts'
8
- import { buildProgressLine, createProgressCounters, formatCommandWithArgs, formatMessage, recordPluginResult, resetProgressCounters } from './utils.ts'
9
-
10
- /**
11
- * TTY logger for local development, with spinners and progress bars.
12
- */
13
- export const clackLogger = defineLogger({
14
- name: 'clack',
15
- install(context, options) {
16
- const logLevel = options?.logLevel ?? logLevelMap.info
17
- const state = {
18
- ...createProgressCounters(),
19
- spinner: clack.spinner(),
20
- isSpinning: false,
21
- runningPlugins: new Set<string>(),
22
- activeProgress: new Map<string, { interval?: NodeJS.Timeout; progressBar: clack.ProgressResult }>(),
23
- activeHookLogs: new Map<string, { taskLog: ReturnType<typeof clack.taskLog>; hrStart: [number, number] }>(),
24
- }
25
-
26
- // Clear every active progress bar's interval, stop it, and drop the map.
27
- function stopActiveProgress() {
28
- for (const [, active] of state.activeProgress) {
29
- if (active.interval) {
30
- clearInterval(active.interval)
31
- }
32
- active.progressBar?.stop()
33
- }
34
- state.activeProgress.clear()
35
- }
36
-
37
- function reset() {
38
- stopActiveProgress()
39
-
40
- resetProgressCounters(state)
41
- state.spinner = clack.spinner()
42
- state.isSpinning = false
43
- state.runningPlugins.clear()
44
- state.activeHookLogs.clear()
45
- }
46
-
47
- // Label for the shared plugin bar, listing the plugins currently generating.
48
- function pluginProgressText(): string {
49
- const running = [...state.runningPlugins].map((name) => styleText('bold', name))
50
- return getMessage(running.length > 0 ? `Generating ${running.join(', ')}` : 'Generating plugins')
51
- }
52
-
53
- function showProgressStep() {
54
- if (logLevel <= logLevelMap.silent) {
55
- return
56
- }
57
-
58
- const line = buildProgressLine(state)
59
- if (line) {
60
- clack.log.step(getMessage(line))
61
- }
62
- }
63
-
64
- function getMessage(message: string): string {
65
- return formatMessage(message, logLevel)
66
- }
67
-
68
- // Registers a handler that prints a fixed step message, skipped at silent level.
69
- function onStep<E extends keyof KubbHooks>(event: E, message: string): void {
70
- context.on(event, () => {
71
- if (logLevel <= logLevelMap.silent) {
72
- return
73
- }
74
- clack.log.step(getMessage(message))
75
- })
76
- }
77
-
78
- function stopSpinner(text?: string) {
79
- if (!state.isSpinning) {
80
- return
81
- }
82
- state.spinner.stop(text)
83
- state.isSpinning = false
84
- }
85
-
86
- context.on('kubb:info', ({ message, info = '' }) => {
87
- if (logLevel <= logLevelMap.silent) {
88
- return
89
- }
90
-
91
- const text = getMessage([styleText('blue', 'ℹ'), message, info ? styleText('dim', info) : undefined].filter(Boolean).join(' '))
92
-
93
- if (state.isSpinning) {
94
- state.spinner.message(text)
95
- return
96
- }
97
- clack.log.info(text)
98
- })
99
-
100
- context.on('kubb:success', ({ message, info = '' }) => {
101
- if (logLevel <= logLevelMap.silent) {
102
- return
103
- }
104
-
105
- const text = getMessage([styleText('blue', '✓'), message, logLevel >= logLevelMap.info ? styleText('dim', info) : undefined].filter(Boolean).join(' '))
106
-
107
- if (state.isSpinning) {
108
- stopSpinner(text)
109
- return
110
- }
111
- clack.log.success(text)
112
- })
113
-
114
- context.on('kubb:warn', ({ message, info }) => {
115
- if (logLevel < logLevelMap.warn) {
116
- return
117
- }
118
-
119
- const text = getMessage(
120
- [styleText('yellow', '⚠'), message, logLevel >= logLevelMap.info && info ? styleText('dim', info) : undefined].filter(Boolean).join(' '),
121
- )
122
-
123
- clack.log.warn(text)
124
- })
125
-
126
- context.on('kubb:error', ({ error }) => {
127
- const caused = toCause(error)
128
-
129
- const text = [styleText('red', '✗'), error.message].join(' ')
130
-
131
- if (state.isSpinning) {
132
- stopSpinner(getMessage(text))
133
- return
134
- }
135
- clack.log.error(getMessage(text))
136
-
137
- // Show stack trace in verbose mode (first 3 frames)
138
- if (logLevel >= logLevelMap.verbose && error.stack) {
139
- const frames = error.stack.split('\n').slice(1, 4)
140
- for (const frame of frames) {
141
- clack.log.message(getMessage(styleText('dim', frame.trim())))
142
- }
143
-
144
- if (caused?.stack) {
145
- clack.log.message(styleText('dim', `└─ caused by ${caused.message}`))
146
-
147
- const frames = caused.stack.split('\n').slice(1, 4)
148
- for (const frame of frames) {
149
- clack.log.message(getMessage(` ${styleText('dim', frame.trim())}`))
150
- }
151
- }
152
- }
153
- })
154
-
155
- context.on('kubb:diagnostic', ({ diagnostic }) => {
156
- // Silent still surfaces errors so failures stay visible. It drops warnings and info.
157
- if (logLevel <= logLevelMap.silent && diagnostic.severity !== 'error') {
158
- return
159
- }
160
-
161
- stopSpinner()
162
-
163
- // Stop any lingering progress UI so the multi-line block renders cleanly.
164
- stopActiveProgress()
165
-
166
- // The version-update notice keeps its own framed box instead of the diagnostic gutter.
167
- if (Diagnostics.isUpdate(diagnostic)) {
168
- clack.box(
169
- `\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\`
170
- Run \`npm install -g @kubb/cli\` to update`,
171
- 'Update available for `Kubb`',
172
- {
173
- width: 'auto',
174
- formatBorder: (s: string) => styleText('yellow', s),
175
- rounded: true,
176
- withGuide: false,
177
- contentAlign: 'center',
178
- titleAlign: 'center',
179
- },
180
- )
181
-
182
- return
183
- }
184
-
185
- // Hand the severity glyph to clack as the gutter `symbol`, then let it draw the
186
- // bar on each detail line via the default `secondarySymbol`. The headline and
187
- // details carry their own colors, so clack only owns the gutter.
188
- const { symbol, headline, details } = Diagnostics.format(diagnostic)
189
- clack.log.message([headline, ...details], { symbol })
190
- })
191
-
192
- context.on('kubb:lifecycle:start', async ({ version }) => {
193
- console.log(`\n${getIntro({ title: 'The meta framework for code generation', description: 'Ready to start', version, areEyesOpen: true })}\n`)
194
-
195
- reset()
196
- })
197
-
198
- context.on('kubb:generation:start', ({ config }) => {
199
- reset()
200
-
201
- // Initialize progress tracking for this generation
202
- state.totalPlugins = config.plugins?.length ?? 0
203
-
204
- if (logLevel <= logLevelMap.silent) {
205
- return
206
- }
207
-
208
- const text = getMessage(['Generation started', config.name ? `for ${styleText('dim', config.name)}` : undefined].filter(Boolean).join(' '))
209
-
210
- clack.intro(text)
211
- })
212
-
213
- // Plugins run concurrently, so they share a single progress bar. A bar per plugin
214
- // would make clack render them side by side and pile up keypress listeners.
215
- context.on('kubb:plugin:start', ({ plugin }) => {
216
- if (logLevel <= logLevelMap.silent) {
217
- return
218
- }
219
-
220
- stopSpinner()
221
-
222
- state.runningPlugins.add(plugin.name)
223
-
224
- const active = state.activeProgress.get('plugins')
225
- if (active) {
226
- active.progressBar.advance(0, pluginProgressText())
227
- return
228
- }
229
-
230
- const progressBar = clack.progress({
231
- style: 'block',
232
- max: Math.max(state.totalPlugins, 1),
233
- size: 30,
234
- })
235
- progressBar.start(pluginProgressText())
236
- // Catch up to plugins already finished before this bar opened.
237
- progressBar.advance(state.completedPlugins + state.failedPlugins, pluginProgressText())
238
- state.activeProgress.set('plugins', { progressBar })
239
- })
240
-
241
- context.on('kubb:plugin:end', ({ plugin, success }) => {
242
- stopSpinner()
243
-
244
- const active = state.activeProgress.get('plugins')
245
-
246
- if (!active || logLevel === logLevelMap.silent) {
247
- return
248
- }
249
-
250
- state.runningPlugins.delete(plugin.name)
251
- recordPluginResult(state, success)
252
- active.progressBar.advance(1, pluginProgressText())
253
-
254
- // Close the bar once nothing is generating, then print the progress step.
255
- if (state.runningPlugins.size === 0) {
256
- active.progressBar.stop(getMessage('Plugins generated'))
257
- state.activeProgress.delete('plugins')
258
- showProgressStep()
259
- }
260
- })
261
-
262
- context.on('kubb:files:processing:start', ({ files }) => {
263
- if (logLevel <= logLevelMap.silent) {
264
- return
265
- }
266
-
267
- stopSpinner()
268
-
269
- state.totalFiles = files.length
270
- state.processedFiles = 0
271
-
272
- const text = `Writing ${files.length} files`
273
- const progressBar = clack.progress({
274
- style: 'block',
275
- max: files.length,
276
- size: 30,
277
- })
278
-
279
- context.emit('kubb:info', { message: text })
280
- progressBar.start(getMessage(text))
281
- state.activeProgress.set('files', { progressBar })
282
- })
283
-
284
- context.on('kubb:files:processing:update', ({ files }) => {
285
- if (logLevel <= logLevelMap.silent) {
286
- return
287
- }
288
-
289
- stopSpinner()
290
-
291
- const active = state.activeProgress.get('files')
292
- for (const { file, config } of files) {
293
- state.processedFiles++
294
- if (active) {
295
- active.progressBar.advance(undefined, `Writing ${relative(config.root, file.path)}`)
296
- }
297
- }
298
- })
299
- context.on('kubb:files:processing:end', () => {
300
- if (logLevel <= logLevelMap.silent) {
301
- return
302
- }
303
-
304
- stopSpinner()
305
-
306
- const text = getMessage('Files written successfully')
307
- const active = state.activeProgress.get('files')
308
-
309
- if (!active) {
310
- return
311
- }
312
-
313
- active.progressBar.stop(text)
314
- state.activeProgress.delete('files')
315
-
316
- // Show final progress step after files are written
317
- showProgressStep()
318
- })
319
-
320
- context.on('kubb:generation:end', ({ config }) => {
321
- stopSpinner()
322
-
323
- const text = getMessage(config.name ? `Generation completed for ${styleText('dim', config.name)}` : 'Generation completed')
324
-
325
- clack.outro(text)
326
- })
327
-
328
- onStep('kubb:format:start', 'Formatting')
329
- onStep('kubb:lint:start', 'Linting')
330
- onStep('kubb:hooks:start', 'Running hooks')
331
-
332
- context.on('kubb:hook:start', ({ id, command, args }) => {
333
- if (logLevel <= logLevelMap.silent || !id) {
334
- return
335
- }
336
-
337
- stopSpinner()
338
-
339
- const commandWithArgs = formatCommandWithArgs(command, args)
340
- const title = getMessage(`Running ${styleText('dim', commandWithArgs)}`)
341
- const taskLog = clack.taskLog({ title })
342
-
343
- state.activeHookLogs.set(id, { taskLog, hrStart: process.hrtime() })
344
- })
345
-
346
- // Registered only when not silent, so its presence is what tells the runner to stream
347
- // (`kubb:hook:line` listenerCount). At silent level the listener is absent, so no streaming happens.
348
- if (logLevel > logLevelMap.silent) {
349
- context.on('kubb:hook:line', ({ id, line }) => {
350
- const active = state.activeHookLogs.get(id)
351
- active?.taskLog.message(styleText('dim', line))
352
- })
353
- }
354
-
355
- context.on('kubb:hook:end', ({ id, command, args, success, error, stdout, stderr }) => {
356
- if (!id) {
357
- return
358
- }
359
-
360
- if (logLevel <= logLevelMap.silent) {
361
- // Even when silent, surface a failed hook's captured output.
362
- if (!success) {
363
- if (stdout) console.log(stdout)
364
- if (stderr) console.error(stderr)
365
- }
366
- return
367
- }
368
-
369
- const active = state.activeHookLogs.get(id)
370
- if (!active) {
371
- return
372
- }
373
- state.activeHookLogs.delete(id)
374
-
375
- const commandWithArgs = formatCommandWithArgs(command, args)
376
- const duration = formatMsWithColor(getElapsedMs(active.hrStart))
377
-
378
- if (success) {
379
- active.taskLog.success(getMessage(`${styleText('dim', commandWithArgs)} completed in ${duration}`))
380
- } else {
381
- // The hook's output already reached the taskLog live via `kubb:hook:line`, so `showLog`
382
- // replays it here. `kubb:hook:end` carries no captured output on the streaming path.
383
- const reason = error?.message ? ` (${error.message})` : ''
384
- active.taskLog.error(getMessage(`${styleText('dim', commandWithArgs)} failed${reason}`), { showLog: true })
385
- }
386
- })
387
-
388
- context.on('kubb:lifecycle:end', () => {
389
- reset()
390
- })
391
- },
392
- })
@@ -1,59 +0,0 @@
1
- import type { AsyncEventEmitter } from '@internals/utils'
2
- import type { KubbHooks } from '@kubb/core'
3
-
4
- /**
5
- * Options accepted by a logger's `install` callback.
6
- */
7
- export type LoggerOptions = {
8
- /**
9
- * Output verbosity. Use the `logLevel` constants exported from `@kubb/core`
10
- * (`silent`, `error`, `warn`, `info`, `verbose`, `debug`).
11
- */
12
- logLevel: number
13
- }
14
-
15
- /**
16
- * Event emitter handed to `Logger.install`. Use `.on('kubb:info', ...)` and
17
- * friends to subscribe to build events.
18
- */
19
- export type LoggerContext = AsyncEventEmitter<KubbHooks>
20
-
21
- /**
22
- * Logger contract. A logger receives the build's event emitter and subscribes
23
- * to whichever lifecycle events it wants to forward to its destination
24
- * (console, file, remote service).
25
- */
26
- export type Logger<TOptions extends LoggerOptions = LoggerOptions> = {
27
- /**
28
- * Display name used in diagnostics.
29
- */
30
- name: string
31
- /**
32
- * Called once per build with the shared event emitter. Subscribe to the
33
- * lifecycle events the logger wants to forward to its destination.
34
- */
35
- install: (context: LoggerContext, options?: TOptions) => void | Promise<void>
36
- }
37
-
38
- export type UserLogger<TOptions extends LoggerOptions = LoggerOptions> = Logger<TOptions>
39
-
40
- /**
41
- * Defines a typed logger. The `install` method subscribes to lifecycle events
42
- * on the shared emitter and forwards them to the logger's destination.
43
- *
44
- * @example
45
- * ```ts
46
- * import { defineLogger } from '@kubb/cli'
47
- *
48
- * export const myLogger = defineLogger({
49
- * name: 'my-logger',
50
- * install(context) {
51
- * context.on('kubb:info', ({ message }) => console.log('ℹ', message))
52
- * context.on('kubb:error', ({ error }) => console.error('✗', error.message))
53
- * },
54
- * })
55
- * ```
56
- */
57
- export function defineLogger<Options extends LoggerOptions = LoggerOptions>(logger: UserLogger<Options>): Logger<Options> {
58
- return logger
59
- }