@kubb/cli 5.0.0-beta.3 → 5.0.0-beta.30
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.
- package/README.md +177 -26
- package/dist/agent-CPKt8QQU.js +68 -0
- package/dist/agent-CPKt8QQU.js.map +1 -0
- package/dist/agent-lxTI4Krn.cjs +70 -0
- package/dist/agent-lxTI4Krn.cjs.map +1 -0
- package/dist/{chunk--u3MIqq1.js → chunk-BvFE5Tac.js} +1 -0
- package/dist/constants-B2JTeRBb.js +42 -0
- package/dist/constants-B2JTeRBb.js.map +1 -0
- package/dist/constants-BINTA5VZ.cjs +77 -0
- package/dist/constants-BINTA5VZ.cjs.map +1 -0
- package/dist/constants-BYGmiFs0.cjs +139 -0
- package/dist/constants-BYGmiFs0.cjs.map +1 -0
- package/dist/constants-DSJ-Xrbv.js +116 -0
- package/dist/constants-DSJ-Xrbv.js.map +1 -0
- package/dist/define-Bdn8j5VM.cjs.map +1 -1
- package/dist/{define-Ctii4bel.js → define-m_fp-Aqm.js} +2 -2
- package/dist/{define-Ctii4bel.js.map → define-m_fp-Aqm.js.map} +1 -1
- package/dist/{errors-CjPmyZHy.js → errors-CINO1EIv.js} +2 -2
- package/dist/{errors-CjPmyZHy.js.map → errors-CINO1EIv.js.map} +1 -1
- package/dist/errors-CLCjoSg0.cjs.map +1 -1
- package/dist/{generate-BzCMyyNN.cjs → generate-DgTJz23F.cjs} +10 -4
- package/dist/generate-DgTJz23F.cjs.map +1 -0
- package/dist/{generate-CTdVvIaP.js → generate-qCPyPiD6.js} +12 -6
- package/dist/generate-qCPyPiD6.js.map +1 -0
- package/dist/index.cjs +20 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +22 -13
- package/dist/index.js.map +1 -1
- package/dist/init-CM61rK5A.cjs +53 -0
- package/dist/init-CM61rK5A.cjs.map +1 -0
- package/dist/init-_-CN-ASE.js +53 -0
- package/dist/init-_-CN-ASE.js.map +1 -0
- package/dist/mcp-1ld_vD4n.js +39 -0
- package/dist/mcp-1ld_vD4n.js.map +1 -0
- package/dist/mcp-IMkVgOtF.cjs +39 -0
- package/dist/mcp-IMkVgOtF.cjs.map +1 -0
- package/dist/package-BDL80yHv.js +6 -0
- package/dist/package-BDL80yHv.js.map +1 -0
- package/dist/{package-DcmDg_mw.cjs → package-CHmy7tzG.cjs} +2 -2
- package/dist/package-CHmy7tzG.cjs.map +1 -0
- package/dist/run-B11-UaUs.cjs +33 -0
- package/dist/run-B11-UaUs.cjs.map +1 -0
- package/dist/{init-eNRlotJK.js → run-BNqMQygv.js} +107 -149
- package/dist/run-BNqMQygv.js.map +1 -0
- package/dist/{init-CZ5Xq2Hd.cjs → run-BnGfi7Cp.cjs} +105 -147
- package/dist/run-BnGfi7Cp.cjs.map +1 -0
- package/dist/{generate-BL-Kp5GY.js → run-BonExv0-.js} +561 -493
- package/dist/run-BonExv0-.js.map +1 -0
- package/dist/{agent-sdYBBgrd.js → run-BzpYYOQs.js} +46 -43
- package/dist/run-BzpYYOQs.js.map +1 -0
- package/dist/run-CCZ24VKk.js +51 -0
- package/dist/run-CCZ24VKk.js.map +1 -0
- package/dist/run-CQbj3ley.cjs +52 -0
- package/dist/run-CQbj3ley.cjs.map +1 -0
- package/dist/{agent-B4cAAab2.cjs → run-DwdAwnLG.cjs} +44 -41
- package/dist/run-DwdAwnLG.cjs.map +1 -0
- package/dist/run-PSA9X7ci.js +32 -0
- package/dist/run-PSA9X7ci.js.map +1 -0
- package/dist/{generate-DMqdAYqy.cjs → run-e1Pda1tW.cjs} +558 -490
- package/dist/run-e1Pda1tW.cjs.map +1 -0
- package/dist/shell-475fQKaX.cjs.map +1 -1
- package/dist/{shell-DLzN4fRo.js → shell-CN6DNqeC.js} +2 -2
- package/dist/{shell-DLzN4fRo.js.map → shell-CN6DNqeC.js.map} +1 -1
- package/dist/{telemetry-DN95_2pF.cjs → telemetry-B2iWkY5e.cjs} +5 -7
- package/dist/telemetry-B2iWkY5e.cjs.map +1 -0
- package/dist/{telemetry-LgT_sdPe.js → telemetry-BkektVz6.js} +6 -8
- package/dist/telemetry-BkektVz6.js.map +1 -0
- package/dist/validate-26X6uBG3.js +26 -0
- package/dist/validate-26X6uBG3.js.map +1 -0
- package/dist/validate-BAEo4Dcd.cjs +26 -0
- package/dist/validate-BAEo4Dcd.cjs.map +1 -0
- package/package.json +14 -14
- package/src/commands/agent/start.ts +10 -7
- package/src/commands/agent.ts +3 -1
- package/src/commands/generate.ts +5 -3
- package/src/commands/init.ts +34 -3
- package/src/commands/mcp.ts +28 -4
- package/src/commands/validate.ts +6 -4
- package/src/constants.ts +2 -58
- package/src/index.ts +5 -3
- package/src/loggers/clackLogger.ts +85 -118
- package/src/loggers/fileSystemLogger.ts +28 -15
- package/src/loggers/githubActionsLogger.ts +87 -96
- package/src/loggers/plainLogger.ts +48 -81
- package/src/loggers/types.ts +6 -0
- package/src/loggers/utils.ts +235 -11
- package/src/runners/agent/run.ts +113 -0
- package/src/runners/agent/utils.ts +98 -0
- package/src/runners/generate/run.ts +321 -0
- package/src/runners/generate/utils.ts +225 -0
- package/src/runners/init/run.ts +212 -0
- package/src/{utils/packageManager.ts → runners/init/utils.ts} +12 -2
- package/src/runners/mcp/run.ts +37 -0
- package/src/runners/validate/run.ts +63 -0
- package/src/{utils/telemetry.ts → telemetry.ts} +27 -20
- package/dist/agent-B4cAAab2.cjs.map +0 -1
- package/dist/agent-BFACosbG.cjs +0 -58
- package/dist/agent-BFACosbG.cjs.map +0 -1
- package/dist/agent-s7TqqoTg.js +0 -56
- package/dist/agent-s7TqqoTg.js.map +0 -1
- package/dist/agent-sdYBBgrd.js.map +0 -1
- package/dist/constants-CnDXa1R6.cjs +0 -148
- package/dist/constants-CnDXa1R6.cjs.map +0 -1
- package/dist/constants-aL3CP_Wq.js +0 -95
- package/dist/constants-aL3CP_Wq.js.map +0 -1
- package/dist/generate-BL-Kp5GY.js.map +0 -1
- package/dist/generate-BzCMyyNN.cjs.map +0 -1
- package/dist/generate-CTdVvIaP.js.map +0 -1
- package/dist/generate-DMqdAYqy.cjs.map +0 -1
- package/dist/init-BHMGbly9.cjs +0 -25
- package/dist/init-BHMGbly9.cjs.map +0 -1
- package/dist/init-CZ5Xq2Hd.cjs.map +0 -1
- package/dist/init-eNRlotJK.js.map +0 -1
- package/dist/init-qgpg-iRW.js +0 -25
- package/dist/init-qgpg-iRW.js.map +0 -1
- package/dist/mcp-BRp-2Rdc.js +0 -16
- package/dist/mcp-BRp-2Rdc.js.map +0 -1
- package/dist/mcp-CYOgxB82.cjs +0 -47
- package/dist/mcp-CYOgxB82.cjs.map +0 -1
- package/dist/mcp-DmJm3TrU.js +0 -46
- package/dist/mcp-DmJm3TrU.js.map +0 -1
- package/dist/mcp-N3mRyVuO.cjs +0 -16
- package/dist/mcp-N3mRyVuO.cjs.map +0 -1
- package/dist/package-DcmDg_mw.cjs.map +0 -1
- package/dist/package-DtuyzAVW.js +0 -6
- package/dist/package-DtuyzAVW.js.map +0 -1
- package/dist/telemetry-DN95_2pF.cjs.map +0 -1
- package/dist/telemetry-LgT_sdPe.js.map +0 -1
- package/dist/validate-CJpTOzKA.js +0 -25
- package/dist/validate-CJpTOzKA.js.map +0 -1
- package/dist/validate-DyTbv7Bc.cjs +0 -25
- package/dist/validate-DyTbv7Bc.cjs.map +0 -1
- package/dist/validate-kLJoT_hi.js +0 -33
- package/dist/validate-kLJoT_hi.js.map +0 -1
- package/dist/validate-yKKzqEZ5.cjs +0 -34
- package/dist/validate-yKKzqEZ5.cjs.map +0 -1
- package/src/runners/agent.ts +0 -155
- package/src/runners/generate.ts +0 -333
- package/src/runners/init.ts +0 -296
- package/src/runners/mcp.ts +0 -51
- package/src/runners/validate.ts +0 -39
- package/src/types.ts +0 -11
- package/src/utils/Writables.ts +0 -17
- package/src/utils/executeHooks.ts +0 -45
- package/src/utils/flags.ts +0 -9
- package/src/utils/getConfig.ts +0 -10
- package/src/utils/getCosmiConfig.ts +0 -80
- package/src/utils/getSummary.ts +0 -68
- package/src/utils/runHook.ts +0 -91
- package/src/utils/watcher.ts +0 -19
package/src/runners/generate.ts
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import process from 'node:process'
|
|
4
|
-
import { styleText } from 'node:util'
|
|
5
|
-
import * as clack from '@clack/prompts'
|
|
6
|
-
import type { AsyncEventEmitter } from '@internals/utils'
|
|
7
|
-
import { AsyncEventEmitter as AsyncEventEmitterClass, detectFormatter, detectLinter, executeIfOnline, formatters, linters, toError } from '@internals/utils'
|
|
8
|
-
import { type CLIOptions, type Config, createKubb, isInputPath, type KubbHooks, logLevel as logLevelMap } from '@kubb/core'
|
|
9
|
-
import { version } from '../../package.json'
|
|
10
|
-
import { KUBB_NPM_PACKAGE_URL } from '../constants.ts'
|
|
11
|
-
import { setupLogger } from '../loggers/utils.ts'
|
|
12
|
-
import { executeHooks } from '../utils/executeHooks.ts'
|
|
13
|
-
import { getConfigs } from '../utils/getConfig.ts'
|
|
14
|
-
import { getCosmiConfig } from '../utils/getCosmiConfig.ts'
|
|
15
|
-
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
16
|
-
import { startWatcher } from '../utils/watcher.ts'
|
|
17
|
-
|
|
18
|
-
type GenerateProps = {
|
|
19
|
-
input?: string
|
|
20
|
-
config: Config
|
|
21
|
-
hooks: AsyncEventEmitter<KubbHooks>
|
|
22
|
-
logLevel: number
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type ToolMap = typeof formatters | typeof linters
|
|
26
|
-
|
|
27
|
-
type RunToolPassOptions = {
|
|
28
|
-
toolValue: string
|
|
29
|
-
detect: () => Promise<string | null>
|
|
30
|
-
toolMap: ToolMap
|
|
31
|
-
/** Short noun used in "Auto-detected <toolLabel>:" message, e.g. "formatter" or "linter". */
|
|
32
|
-
toolLabel: string
|
|
33
|
-
/** Verb prefix for the success message, e.g. "Formatting" or "Linting". */
|
|
34
|
-
successPrefix: string
|
|
35
|
-
noToolMessage: string
|
|
36
|
-
configName: string | undefined
|
|
37
|
-
outputPath: string
|
|
38
|
-
logLevel: number
|
|
39
|
-
hooks: AsyncEventEmitter<KubbHooks>
|
|
40
|
-
onStart: () => Promise<void>
|
|
41
|
-
onEnd: () => Promise<void>
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function runToolPass({
|
|
45
|
-
toolValue,
|
|
46
|
-
detect,
|
|
47
|
-
toolMap,
|
|
48
|
-
toolLabel,
|
|
49
|
-
successPrefix,
|
|
50
|
-
noToolMessage,
|
|
51
|
-
configName,
|
|
52
|
-
outputPath,
|
|
53
|
-
logLevel,
|
|
54
|
-
hooks,
|
|
55
|
-
onStart,
|
|
56
|
-
onEnd,
|
|
57
|
-
}: RunToolPassOptions) {
|
|
58
|
-
await onStart()
|
|
59
|
-
|
|
60
|
-
let resolvedTool = toolValue
|
|
61
|
-
if (resolvedTool === 'auto') {
|
|
62
|
-
const detected = await detect()
|
|
63
|
-
if (!detected) {
|
|
64
|
-
await hooks.emit('kubb:warn', { message: noToolMessage })
|
|
65
|
-
} else {
|
|
66
|
-
resolvedTool = detected
|
|
67
|
-
await hooks.emit('kubb:info', { message: `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}` })
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
let toolError: Error | undefined
|
|
72
|
-
|
|
73
|
-
if (resolvedTool && resolvedTool !== 'auto' && resolvedTool in toolMap) {
|
|
74
|
-
const toolConfig = toolMap[resolvedTool as keyof ToolMap]
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const hookId = createHash('sha256').update([configName, resolvedTool].filter(Boolean).join('-')).digest('hex')
|
|
78
|
-
|
|
79
|
-
// Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
|
|
80
|
-
// where hook:end fires synchronously inside emit('kubb:hook:start') before the listener is registered.
|
|
81
|
-
const hookEndPromise = new Promise<void>((resolve, reject) => {
|
|
82
|
-
const handler = (ctx: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
|
|
83
|
-
if (ctx.id !== hookId) return
|
|
84
|
-
hooks.off('kubb:hook:end', handler)
|
|
85
|
-
if (!ctx.success) {
|
|
86
|
-
reject(ctx.error ?? new Error(`${toolConfig.errorMessage}`))
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
hooks
|
|
90
|
-
.emit('kubb:success', {
|
|
91
|
-
message: [
|
|
92
|
-
`${successPrefix} with ${styleText('dim', resolvedTool)}`,
|
|
93
|
-
logLevel >= logLevelMap.info ? `on ${styleText('dim', outputPath)}` : undefined,
|
|
94
|
-
'successfully',
|
|
95
|
-
]
|
|
96
|
-
.filter(Boolean)
|
|
97
|
-
.join(' '),
|
|
98
|
-
})
|
|
99
|
-
.then(resolve)
|
|
100
|
-
.catch(reject)
|
|
101
|
-
}
|
|
102
|
-
hooks.on('kubb:hook:end', handler)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
await hooks.emit('kubb:hook:start', {
|
|
106
|
-
id: hookId,
|
|
107
|
-
command: toolConfig.command,
|
|
108
|
-
args: toolConfig.args(outputPath),
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
await hookEndPromise
|
|
112
|
-
} catch (caughtError) {
|
|
113
|
-
// Use the actual error from the hook. toolConfig.errorMessage (e.g. "Oxlint not found")
|
|
114
|
-
// is misleading when the binary was found and ran but exited with a non-zero code.
|
|
115
|
-
// runHook already emitted kubb:error for binary-not-found cases; here we surface the
|
|
116
|
-
// real reason (e.g. "Hook execute failed: oxlint --fix …") for non-zero exits.
|
|
117
|
-
const err = toError(caughtError)
|
|
118
|
-
await hooks.emit('kubb:error', { error: err })
|
|
119
|
-
toolError = err
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
await onEnd()
|
|
124
|
-
|
|
125
|
-
if (toolError) {
|
|
126
|
-
throw toolError
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function generate(options: GenerateProps): Promise<void> {
|
|
131
|
-
const { input, hooks, logLevel } = options
|
|
132
|
-
|
|
133
|
-
const hrStart = process.hrtime()
|
|
134
|
-
const inputPath = input ?? ('path' in options.config.input ? options.config.input.path : undefined)
|
|
135
|
-
|
|
136
|
-
const config: Config = {
|
|
137
|
-
...options.config,
|
|
138
|
-
input: inputPath
|
|
139
|
-
? {
|
|
140
|
-
...options.config.input,
|
|
141
|
-
path: inputPath,
|
|
142
|
-
}
|
|
143
|
-
: options.config.input,
|
|
144
|
-
...options.config.output,
|
|
145
|
-
} satisfies Config
|
|
146
|
-
|
|
147
|
-
const kubb = createKubb(config, { hooks })
|
|
148
|
-
await kubb.setup()
|
|
149
|
-
|
|
150
|
-
await hooks.emit('kubb:generation:start', { config })
|
|
151
|
-
|
|
152
|
-
await hooks.emit('kubb:info', { message: config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', info: inputPath })
|
|
153
|
-
|
|
154
|
-
await hooks.emit('kubb:info', { message: config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', info: inputPath })
|
|
155
|
-
|
|
156
|
-
const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild()
|
|
157
|
-
|
|
158
|
-
await hooks.emit('kubb:info', { message: 'Load summary' })
|
|
159
|
-
|
|
160
|
-
// Handle build failures (either from failed plugins or general errors)
|
|
161
|
-
|
|
162
|
-
const hasFailures = failedPlugins.size > 0 || error
|
|
163
|
-
if (hasFailures) {
|
|
164
|
-
// Collect all errors from failed plugins and general error
|
|
165
|
-
const allErrors: Error[] = [
|
|
166
|
-
error,
|
|
167
|
-
...Array.from(failedPlugins)
|
|
168
|
-
.filter((it) => it.error)
|
|
169
|
-
.map((it) => it.error),
|
|
170
|
-
].filter(Boolean)
|
|
171
|
-
|
|
172
|
-
for (const err of allErrors) {
|
|
173
|
-
await hooks.emit('kubb:error', { error: err })
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
|
|
177
|
-
|
|
178
|
-
await hooks.emit('kubb:generation:summary', {
|
|
179
|
-
config,
|
|
180
|
-
failedPlugins,
|
|
181
|
-
filesCreated: files.length,
|
|
182
|
-
status: 'failed',
|
|
183
|
-
hrStart,
|
|
184
|
-
pluginTimings: logLevel >= logLevelMap.verbose ? pluginTimings : undefined,
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
await sendTelemetry(
|
|
188
|
-
buildTelemetryEvent({
|
|
189
|
-
command: 'generate',
|
|
190
|
-
kubbVersion: version,
|
|
191
|
-
plugins: Array.from(driver.plugins.values(), (p) => ({
|
|
192
|
-
name: p.name,
|
|
193
|
-
options: p.options as Record<string, unknown>,
|
|
194
|
-
})),
|
|
195
|
-
hrStart,
|
|
196
|
-
filesCreated: files.length,
|
|
197
|
-
status: 'failed',
|
|
198
|
-
}),
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
process.exit(1)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
await hooks.emit('kubb:success', { message: 'Generation successfully', info: inputPath })
|
|
205
|
-
await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
|
|
206
|
-
|
|
207
|
-
const outputPath = path.resolve(config.root, config.output.path)
|
|
208
|
-
|
|
209
|
-
if (config.output.format) {
|
|
210
|
-
await runToolPass({
|
|
211
|
-
toolValue: config.output.format,
|
|
212
|
-
detect: detectFormatter,
|
|
213
|
-
toolMap: formatters,
|
|
214
|
-
toolLabel: 'formatter',
|
|
215
|
-
successPrefix: 'Formatting',
|
|
216
|
-
noToolMessage: 'No formatter found (oxfmt, biome, or prettier). Skipping formatting.',
|
|
217
|
-
configName: config.name,
|
|
218
|
-
outputPath,
|
|
219
|
-
logLevel,
|
|
220
|
-
hooks,
|
|
221
|
-
onStart: () => hooks.emit('kubb:format:start'),
|
|
222
|
-
onEnd: () => hooks.emit('kubb:format:end'),
|
|
223
|
-
})
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (config.output.lint) {
|
|
227
|
-
await runToolPass({
|
|
228
|
-
toolValue: config.output.lint,
|
|
229
|
-
detect: detectLinter,
|
|
230
|
-
toolMap: linters,
|
|
231
|
-
toolLabel: 'linter',
|
|
232
|
-
successPrefix: 'Linting',
|
|
233
|
-
noToolMessage: 'No linter found (oxlint, biome, or eslint). Skipping linting.',
|
|
234
|
-
configName: config.name,
|
|
235
|
-
outputPath,
|
|
236
|
-
logLevel,
|
|
237
|
-
hooks,
|
|
238
|
-
onStart: () => hooks.emit('kubb:lint:start'),
|
|
239
|
-
onEnd: () => hooks.emit('kubb:lint:end'),
|
|
240
|
-
})
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (config.hooks) {
|
|
244
|
-
await hooks.emit('kubb:hooks:start')
|
|
245
|
-
await executeHooks({ configHooks: config.hooks, hooks })
|
|
246
|
-
|
|
247
|
-
await hooks.emit('kubb:hooks:end')
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Only reached when there are no failures (process.exit(1) is called above otherwise)
|
|
251
|
-
await hooks.emit('kubb:generation:summary', {
|
|
252
|
-
config,
|
|
253
|
-
failedPlugins,
|
|
254
|
-
filesCreated: files.length,
|
|
255
|
-
status: 'success',
|
|
256
|
-
hrStart,
|
|
257
|
-
pluginTimings,
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
const telemetryEvent = buildTelemetryEvent({
|
|
261
|
-
command: 'generate',
|
|
262
|
-
kubbVersion: version,
|
|
263
|
-
plugins: Array.from(driver.plugins.values(), (p) => ({
|
|
264
|
-
name: p.name,
|
|
265
|
-
options: p.options as Record<string, unknown>,
|
|
266
|
-
})),
|
|
267
|
-
hrStart,
|
|
268
|
-
filesCreated: files.length,
|
|
269
|
-
status: 'success',
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
await sendTelemetry(telemetryEvent)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
type GenerateCommandOptions = {
|
|
276
|
-
input?: string
|
|
277
|
-
configPath?: string
|
|
278
|
-
logLevel: string
|
|
279
|
-
watch: boolean
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }: GenerateCommandOptions): Promise<void> {
|
|
283
|
-
const logLevel = logLevelMap[logLevelKey as keyof typeof logLevelMap] ?? logLevelMap.info
|
|
284
|
-
const hooks = new AsyncEventEmitterClass<KubbHooks>()
|
|
285
|
-
|
|
286
|
-
await setupLogger(hooks, { logLevel })
|
|
287
|
-
|
|
288
|
-
await executeIfOnline(async () => {
|
|
289
|
-
try {
|
|
290
|
-
const res = await fetch(KUBB_NPM_PACKAGE_URL)
|
|
291
|
-
const data = (await res.json()) as { version: string }
|
|
292
|
-
const latestVersion = data.version
|
|
293
|
-
|
|
294
|
-
if (latestVersion && version < latestVersion) {
|
|
295
|
-
await hooks.emit('kubb:version:new', { currentVersion: version, latestVersion })
|
|
296
|
-
}
|
|
297
|
-
} catch {
|
|
298
|
-
// Ignore network errors for version check
|
|
299
|
-
}
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
try {
|
|
303
|
-
const result = await getCosmiConfig('kubb', configPath)
|
|
304
|
-
const configs = await getConfigs(result.config, { input } as CLIOptions)
|
|
305
|
-
|
|
306
|
-
await hooks.emit('kubb:config:start')
|
|
307
|
-
await hooks.emit('kubb:info', { message: 'Config loaded', info: path.relative(process.cwd(), result.filepath) })
|
|
308
|
-
await hooks.emit('kubb:success', { message: 'Config loaded successfully', info: path.relative(process.cwd(), result.filepath) })
|
|
309
|
-
await hooks.emit('kubb:config:end', { configs })
|
|
310
|
-
|
|
311
|
-
await hooks.emit('kubb:lifecycle:start', { version })
|
|
312
|
-
|
|
313
|
-
for (const config of configs) {
|
|
314
|
-
if (isInputPath(config) && watch) {
|
|
315
|
-
await startWatcher([input || config.input.path], async (paths) => {
|
|
316
|
-
// remove to avoid duplicate listeners after each change
|
|
317
|
-
hooks.removeAll()
|
|
318
|
-
|
|
319
|
-
await generate({ input, config, logLevel, hooks })
|
|
320
|
-
|
|
321
|
-
clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
|
|
322
|
-
})
|
|
323
|
-
} else {
|
|
324
|
-
await generate({ input, config, logLevel, hooks })
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
await hooks.emit('kubb:lifecycle:end')
|
|
329
|
-
} catch (error) {
|
|
330
|
-
await hooks.emit('kubb:error', { error: toError(error) })
|
|
331
|
-
process.exit(1)
|
|
332
|
-
}
|
|
333
|
-
}
|
package/src/runners/init.ts
DELETED
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import process from 'node:process'
|
|
4
|
-
import { styleText } from 'node:util'
|
|
5
|
-
import * as clack from '@clack/prompts'
|
|
6
|
-
import type { PackageManagerInfo } from '@internals/utils'
|
|
7
|
-
import { detectPackageManager } from '@internals/utils'
|
|
8
|
-
import { initDefaults, KUBB_CONFIG_FILENAME, pluginDefaultConfigs } from '../constants.ts'
|
|
9
|
-
import { hasPackageJson, initPackageJson, installPackages } from '../utils/packageManager.ts'
|
|
10
|
-
|
|
11
|
-
type PluginOption = {
|
|
12
|
-
value: string
|
|
13
|
-
label: string
|
|
14
|
-
hint?: string
|
|
15
|
-
packageName: string
|
|
16
|
-
importName: string
|
|
17
|
-
category: 'types' | 'client' | 'framework' | 'validation' | 'testing' | 'mocks' | 'documentation' | 'ai'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const availablePlugins: PluginOption[] = [
|
|
21
|
-
{
|
|
22
|
-
value: 'plugin-ts',
|
|
23
|
-
label: 'TypeScript',
|
|
24
|
-
hint: 'Recommended',
|
|
25
|
-
packageName: '@kubb/plugin-ts',
|
|
26
|
-
importName: 'pluginTs',
|
|
27
|
-
category: 'types',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
value: 'plugin-client',
|
|
31
|
-
label: 'Client (Fetch/Axios)',
|
|
32
|
-
packageName: '@kubb/plugin-client',
|
|
33
|
-
importName: 'pluginClient',
|
|
34
|
-
category: 'client',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
value: 'plugin-react-query',
|
|
38
|
-
label: 'React Query / TanStack Query',
|
|
39
|
-
packageName: '@kubb/plugin-react-query',
|
|
40
|
-
importName: 'pluginReactQuery',
|
|
41
|
-
category: 'framework',
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
value: 'plugin-vue-query',
|
|
45
|
-
label: 'Vue Query',
|
|
46
|
-
packageName: '@kubb/plugin-vue-query',
|
|
47
|
-
importName: 'pluginVueQuery',
|
|
48
|
-
category: 'framework',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
value: 'plugin-zod',
|
|
52
|
-
label: 'Zod Schemas',
|
|
53
|
-
packageName: '@kubb/plugin-zod',
|
|
54
|
-
importName: 'pluginZod',
|
|
55
|
-
category: 'validation',
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
value: 'plugin-faker',
|
|
59
|
-
label: 'Faker.js Mocks',
|
|
60
|
-
packageName: '@kubb/plugin-faker',
|
|
61
|
-
importName: 'pluginFaker',
|
|
62
|
-
category: 'mocks',
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
value: 'plugin-msw',
|
|
66
|
-
label: 'MSW Handlers',
|
|
67
|
-
packageName: '@kubb/plugin-msw',
|
|
68
|
-
importName: 'pluginMsw',
|
|
69
|
-
category: 'mocks',
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
value: 'plugin-cypress',
|
|
73
|
-
label: 'Cypress Tests',
|
|
74
|
-
packageName: '@kubb/plugin-cypress',
|
|
75
|
-
importName: 'pluginCypress',
|
|
76
|
-
category: 'testing',
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
value: 'plugin-mcp',
|
|
80
|
-
label: 'MCP Server (AI / Model Context Protocol)',
|
|
81
|
-
packageName: '@kubb/plugin-mcp',
|
|
82
|
-
importName: 'pluginMcp',
|
|
83
|
-
category: 'ai',
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
value: 'plugin-redoc',
|
|
87
|
-
label: 'ReDoc Documentation',
|
|
88
|
-
packageName: '@kubb/plugin-redoc',
|
|
89
|
-
importName: 'pluginRedoc',
|
|
90
|
-
category: 'documentation',
|
|
91
|
-
},
|
|
92
|
-
]
|
|
93
|
-
|
|
94
|
-
function generateConfigFile(selectedPlugins: PluginOption[], inputPath: string, outputPath: string): string {
|
|
95
|
-
const imports = selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join('\n')
|
|
96
|
-
|
|
97
|
-
const pluginConfigs = selectedPlugins
|
|
98
|
-
.map((plugin) => {
|
|
99
|
-
const config = (pluginDefaultConfigs as Record<string, string>)[plugin.value] ?? `${plugin.importName}()`
|
|
100
|
-
return ` ${config},`
|
|
101
|
-
})
|
|
102
|
-
.join('\n')
|
|
103
|
-
|
|
104
|
-
return `import { defineConfig } from 'kubb'
|
|
105
|
-
${imports}
|
|
106
|
-
|
|
107
|
-
export default defineConfig({
|
|
108
|
-
root: '.',
|
|
109
|
-
input: {
|
|
110
|
-
path: '${inputPath}',
|
|
111
|
-
},
|
|
112
|
-
output: {
|
|
113
|
-
path: '${outputPath}',
|
|
114
|
-
clean: true,
|
|
115
|
-
},
|
|
116
|
-
plugins: [
|
|
117
|
-
${pluginConfigs}
|
|
118
|
-
],
|
|
119
|
-
})
|
|
120
|
-
`
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function cancelAndExit(message = 'Operation cancelled.'): never {
|
|
124
|
-
clack.cancel(message)
|
|
125
|
-
process.exit(0)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
type InitOptions = {
|
|
129
|
-
yes: boolean
|
|
130
|
-
version: string
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export async function runInit({ yes, version }: InitOptions): Promise<void> {
|
|
134
|
-
const cwd = process.cwd()
|
|
135
|
-
|
|
136
|
-
clack.intro(styleText('bgCyan', styleText('black', ' Kubb Init ')))
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
// Check/create package.json
|
|
140
|
-
let packageManager: PackageManagerInfo
|
|
141
|
-
if (!hasPackageJson(cwd)) {
|
|
142
|
-
if (!yes) {
|
|
143
|
-
const shouldInit = await clack.confirm({
|
|
144
|
-
message: 'No package.json found. Would you like to create one?',
|
|
145
|
-
initialValue: true,
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
if (clack.isCancel(shouldInit) || !shouldInit) {
|
|
149
|
-
cancelAndExit()
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
packageManager = detectPackageManager(cwd)
|
|
154
|
-
|
|
155
|
-
const spinner = clack.spinner()
|
|
156
|
-
spinner.start(`Initializing package.json with ${packageManager.name}`)
|
|
157
|
-
|
|
158
|
-
await initPackageJson(cwd, packageManager)
|
|
159
|
-
|
|
160
|
-
spinner.stop(`Created package.json with ${packageManager.name}`)
|
|
161
|
-
} else {
|
|
162
|
-
packageManager = detectPackageManager(cwd)
|
|
163
|
-
clack.log.info(`Detected package manager: ${styleText('cyan', packageManager.name)}`)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Prompt for OpenAPI spec path
|
|
167
|
-
let inputPath: string
|
|
168
|
-
if (yes) {
|
|
169
|
-
inputPath = initDefaults.inputPath
|
|
170
|
-
clack.log.info(`Using input path: ${styleText('cyan', inputPath)}`)
|
|
171
|
-
} else {
|
|
172
|
-
const inputPathResult = await clack.text({
|
|
173
|
-
message: 'Where is your OpenAPI specification located?',
|
|
174
|
-
placeholder: initDefaults.inputPath,
|
|
175
|
-
defaultValue: initDefaults.inputPath,
|
|
176
|
-
validate: (value) => {
|
|
177
|
-
if (!value) return 'Input path is required'
|
|
178
|
-
},
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
if (clack.isCancel(inputPathResult)) {
|
|
182
|
-
cancelAndExit()
|
|
183
|
-
}
|
|
184
|
-
inputPath = inputPathResult as string
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Prompt for output directory
|
|
188
|
-
let outputPath: string
|
|
189
|
-
if (yes) {
|
|
190
|
-
outputPath = initDefaults.outputPath
|
|
191
|
-
clack.log.info(`Using output path: ${styleText('cyan', outputPath)}`)
|
|
192
|
-
} else {
|
|
193
|
-
const outputPathResult = await clack.text({
|
|
194
|
-
message: 'Where should the generated files be output?',
|
|
195
|
-
placeholder: initDefaults.outputPath,
|
|
196
|
-
defaultValue: initDefaults.outputPath,
|
|
197
|
-
validate: (value) => {
|
|
198
|
-
if (!value) return 'Output path is required'
|
|
199
|
-
},
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
if (clack.isCancel(outputPathResult)) {
|
|
203
|
-
cancelAndExit()
|
|
204
|
-
}
|
|
205
|
-
outputPath = outputPathResult as string
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Plugin selection
|
|
209
|
-
let selectedPlugins: PluginOption[]
|
|
210
|
-
if (yes) {
|
|
211
|
-
selectedPlugins = availablePlugins.filter((plugin) => (initDefaults.plugins as readonly string[]).includes(plugin.value))
|
|
212
|
-
clack.log.info(`Using plugins: ${styleText('cyan', selectedPlugins.map((p) => p.label).join(', '))}`)
|
|
213
|
-
} else {
|
|
214
|
-
const selectedPluginValues = await clack.multiselect({
|
|
215
|
-
message: 'Select plugins to use:',
|
|
216
|
-
options: availablePlugins.map((plugin) => ({
|
|
217
|
-
value: plugin.value,
|
|
218
|
-
label: plugin.label,
|
|
219
|
-
hint: plugin.hint,
|
|
220
|
-
})),
|
|
221
|
-
initialValues: [...initDefaults.plugins],
|
|
222
|
-
required: true,
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
if (clack.isCancel(selectedPluginValues)) {
|
|
226
|
-
cancelAndExit()
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
selectedPlugins = availablePlugins.filter((plugin) => (selectedPluginValues as string[]).includes(plugin.value))
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Install packages
|
|
233
|
-
const packagesToInstall = ['kubb', ...selectedPlugins.map((p) => p.packageName)]
|
|
234
|
-
|
|
235
|
-
const spinner = clack.spinner()
|
|
236
|
-
spinner.start(`Installing ${packagesToInstall.length} packages with ${packageManager.name}`)
|
|
237
|
-
|
|
238
|
-
try {
|
|
239
|
-
await installPackages(packagesToInstall, packageManager, cwd)
|
|
240
|
-
spinner.stop(`Installed ${packagesToInstall.length} packages`)
|
|
241
|
-
} catch (error) {
|
|
242
|
-
spinner.stop('Installation failed')
|
|
243
|
-
throw error
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Generate config file
|
|
247
|
-
const configSpinner = clack.spinner()
|
|
248
|
-
configSpinner.start(`Creating ${KUBB_CONFIG_FILENAME}`)
|
|
249
|
-
|
|
250
|
-
const configContent = generateConfigFile(selectedPlugins, inputPath, outputPath)
|
|
251
|
-
const configPath = path.join(cwd, KUBB_CONFIG_FILENAME)
|
|
252
|
-
|
|
253
|
-
if (fs.existsSync(configPath)) {
|
|
254
|
-
configSpinner.stop(`${KUBB_CONFIG_FILENAME} already exists`)
|
|
255
|
-
|
|
256
|
-
if (!yes) {
|
|
257
|
-
const shouldOverwrite = await clack.confirm({
|
|
258
|
-
message: `${KUBB_CONFIG_FILENAME} already exists. Overwrite?`,
|
|
259
|
-
initialValue: false,
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
if (clack.isCancel(shouldOverwrite) || !shouldOverwrite) {
|
|
263
|
-
cancelAndExit('Keeping existing configuration. Packages have been installed.')
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
configSpinner.start(`Overwriting ${KUBB_CONFIG_FILENAME}`)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
fs.writeFileSync(configPath, configContent, 'utf-8')
|
|
271
|
-
|
|
272
|
-
configSpinner.stop(`Created ${KUBB_CONFIG_FILENAME}`)
|
|
273
|
-
|
|
274
|
-
clack.outro(
|
|
275
|
-
styleText('green', '✓ All set!') +
|
|
276
|
-
'\n\n' +
|
|
277
|
-
styleText('dim', 'Next steps:') +
|
|
278
|
-
'\n' +
|
|
279
|
-
styleText('cyan', ` 1. Make sure your OpenAPI spec is at: ${inputPath}`) +
|
|
280
|
-
'\n' +
|
|
281
|
-
styleText('cyan', ' 2. Generate code with: npx kubb generate') +
|
|
282
|
-
'\n' +
|
|
283
|
-
styleText('cyan', ' Or start a stream server with: npx kubb start') +
|
|
284
|
-
'\n' +
|
|
285
|
-
styleText('cyan', ` 3. Find generated files in: ${outputPath}`) +
|
|
286
|
-
'\n\n' +
|
|
287
|
-
styleText('dim', `Using ${packageManager.name} • Kubb v${version}`),
|
|
288
|
-
)
|
|
289
|
-
} catch (error) {
|
|
290
|
-
clack.log.error(styleText('red', 'An error occurred during initialization'))
|
|
291
|
-
if (error instanceof Error) {
|
|
292
|
-
clack.log.error(error.message)
|
|
293
|
-
}
|
|
294
|
-
process.exit(1)
|
|
295
|
-
}
|
|
296
|
-
}
|
package/src/runners/mcp.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import process from 'node:process'
|
|
2
|
-
import { styleText } from 'node:util'
|
|
3
|
-
import { getErrorMessage } from '@internals/utils'
|
|
4
|
-
import type * as McpModule from '@kubb/mcp'
|
|
5
|
-
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
6
|
-
|
|
7
|
-
type McpOptions = {
|
|
8
|
-
version: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function runMcp({ version }: McpOptions): Promise<void> {
|
|
12
|
-
let mod: typeof McpModule
|
|
13
|
-
try {
|
|
14
|
-
mod = (await import('@kubb/mcp')) as typeof McpModule
|
|
15
|
-
} catch (_e) {
|
|
16
|
-
console.error(styleText('red', 'The @kubb/mcp package is not installed.'))
|
|
17
|
-
console.error('')
|
|
18
|
-
console.error('Install it with:')
|
|
19
|
-
console.error(styleText('cyan', ' npm install @kubb/mcp'))
|
|
20
|
-
console.error(styleText('cyan', ' # or'))
|
|
21
|
-
console.error(styleText('cyan', ' pnpm install @kubb/mcp'))
|
|
22
|
-
console.error('')
|
|
23
|
-
process.exit(1)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const { run } = mod
|
|
27
|
-
const hrStart = process.hrtime()
|
|
28
|
-
try {
|
|
29
|
-
console.log('⏳ Starting MCP server...')
|
|
30
|
-
console.warn(styleText('yellow', 'This feature is still under development — use with caution'))
|
|
31
|
-
run()
|
|
32
|
-
await sendTelemetry(
|
|
33
|
-
buildTelemetryEvent({
|
|
34
|
-
command: 'mcp',
|
|
35
|
-
kubbVersion: version,
|
|
36
|
-
hrStart,
|
|
37
|
-
status: 'success',
|
|
38
|
-
}),
|
|
39
|
-
)
|
|
40
|
-
} catch (error) {
|
|
41
|
-
await sendTelemetry(
|
|
42
|
-
buildTelemetryEvent({
|
|
43
|
-
command: 'mcp',
|
|
44
|
-
kubbVersion: version,
|
|
45
|
-
hrStart,
|
|
46
|
-
status: 'failed',
|
|
47
|
-
}),
|
|
48
|
-
)
|
|
49
|
-
console.error(getErrorMessage(error))
|
|
50
|
-
}
|
|
51
|
-
}
|