@kubb/cli 5.0.0-alpha.5 → 5.0.0-alpha.51
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 +4 -2
- package/bin/kubb.js +6 -0
- package/dist/agent-Bt-Z24BD.cjs +116 -0
- package/dist/agent-Bt-Z24BD.cjs.map +1 -0
- package/dist/{agent-CEHZSBVT.cjs → agent-CEY6yeqU.cjs} +5 -5
- package/dist/agent-CEY6yeqU.cjs.map +1 -0
- package/dist/agent-LgBdb1gx.js +112 -0
- package/dist/agent-LgBdb1gx.js.map +1 -0
- package/dist/{agent-SWUT_sgq.js → agent-NAz3U695.js} +5 -5
- package/dist/agent-NAz3U695.js.map +1 -0
- package/dist/{constants-BTUap0zs.cjs → constants-B2q7VPbi.cjs} +91 -9
- package/dist/constants-B2q7VPbi.cjs.map +1 -0
- package/dist/constants-T_2rsi7T.js +137 -0
- package/dist/constants-T_2rsi7T.js.map +1 -0
- package/dist/define-Bdn8j5VM.cjs +54 -0
- package/dist/define-Bdn8j5VM.cjs.map +1 -0
- package/dist/define-Ctii4bel.js +43 -0
- package/dist/define-Ctii4bel.js.map +1 -0
- package/dist/{errors-DBW0N9w4.cjs → errors-CLCjoSg0.cjs} +22 -6
- package/dist/errors-CLCjoSg0.cjs.map +1 -0
- package/dist/errors-CjPmyZHy.js +43 -0
- package/dist/errors-CjPmyZHy.js.map +1 -0
- package/dist/{generate-CfoLoEd0.js → generate-BspZWGKE.js} +3 -3
- package/dist/generate-BspZWGKE.js.map +1 -0
- package/dist/{generate-CNkdrX6u.cjs → generate-DaqmCSQU.cjs} +3 -3
- package/dist/generate-DaqmCSQU.cjs.map +1 -0
- package/dist/{generate-CtFkZeTU.js → generate-L7N_8n1y.js} +493 -248
- package/dist/generate-L7N_8n1y.js.map +1 -0
- package/dist/{generate-nJaqPdGL.cjs → generate-uiU5r6nt.cjs} +500 -255
- package/dist/generate-uiU5r6nt.cjs.map +1 -0
- package/dist/index.cjs +49 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +49 -21
- package/dist/index.js.map +1 -1
- package/dist/{init-hmolV6B4.cjs → init-AVT-2Uxi.cjs} +31 -21
- package/dist/init-AVT-2Uxi.cjs.map +1 -0
- package/dist/{init-C-InrmSY.js → init-BecEYFRb.js} +27 -17
- package/dist/init-BecEYFRb.js.map +1 -0
- package/dist/{init-D_oERuMn.js → init-DyKEzT4F.js} +4 -4
- package/dist/init-DyKEzT4F.js.map +1 -0
- package/dist/{init-BAs1d5rK.cjs → init-SBJcdXR6.cjs} +4 -4
- package/dist/init-SBJcdXR6.cjs.map +1 -0
- package/dist/{mcp-DEvDB6FY.cjs → mcp-1vHQuAyo.cjs} +4 -4
- package/dist/{mcp-DEvDB6FY.cjs.map → mcp-1vHQuAyo.cjs.map} +1 -1
- package/dist/{mcp-D2SHEg_d.js → mcp-BDHEbuy3.js} +11 -4
- package/dist/mcp-BDHEbuy3.js.map +1 -0
- package/dist/{mcp-ChHFPRzD.cjs → mcp-CqpVw3PO.cjs} +12 -6
- package/dist/mcp-CqpVw3PO.cjs.map +1 -0
- package/dist/{mcp-jKYiomZT.js → mcp-k4i3z_Ug.js} +4 -4
- package/dist/{mcp-jKYiomZT.js.map → mcp-k4i3z_Ug.js.map} +1 -1
- package/dist/{package-C-fC2Eoy.cjs → package-BwQBf-kE.cjs} +2 -2
- package/dist/package-BwQBf-kE.cjs.map +1 -0
- package/dist/package-CXcGIpWz.js +6 -0
- package/dist/package-CXcGIpWz.js.map +1 -0
- package/dist/{shell-7HPrTCJ5.cjs → shell-475fQKaX.cjs} +8 -3
- package/dist/shell-475fQKaX.cjs.map +1 -0
- package/dist/{shell-DqqWsHCD.js → shell-DLzN4fRo.js} +8 -3
- package/dist/shell-DLzN4fRo.js.map +1 -0
- package/dist/{telemetry-Cn9X1I5B.cjs → telemetry-B6ZbZOrb.cjs} +50 -8
- package/dist/telemetry-B6ZbZOrb.cjs.map +1 -0
- package/dist/{telemetry-DxiR7clS.js → telemetry-Dis5nWR_.js} +48 -6
- package/dist/telemetry-Dis5nWR_.js.map +1 -0
- package/dist/{validate--IXRJEhp.js → validate-C2n4-h7x.js} +4 -4
- package/dist/validate-C2n4-h7x.js.map +1 -0
- package/dist/{validate-Bbrn3Q-A.cjs → validate-D-8_wAFV.cjs} +6 -14
- package/dist/validate-D-8_wAFV.cjs.map +1 -0
- package/dist/{validate-BK3pc8zv.cjs → validate-Dx1S1gng.cjs} +4 -4
- package/dist/validate-Dx1S1gng.cjs.map +1 -0
- package/dist/{validate-l8vLmwKA.js → validate-GfErHsnI.js} +5 -13
- package/dist/validate-GfErHsnI.js.map +1 -0
- package/package.json +47 -46
- package/src/commands/agent/start.ts +20 -4
- package/src/commands/generate.ts +35 -6
- package/src/commands/init.ts +6 -1
- package/src/commands/validate.ts +6 -1
- package/src/constants.ts +65 -11
- package/src/index.ts +10 -12
- package/src/loggers/clackLogger.ts +54 -46
- package/src/loggers/fileSystemLogger.ts +13 -11
- package/src/loggers/githubActionsLogger.ts +22 -22
- package/src/loggers/plainLogger.ts +21 -21
- package/src/runners/agent.ts +81 -34
- package/src/runners/generate.ts +90 -109
- package/src/runners/init.ts +9 -9
- package/src/runners/mcp.ts +19 -3
- package/src/runners/validate.ts +19 -15
- package/src/types.ts +11 -0
- package/src/utils/executeHooks.ts +11 -11
- package/src/utils/flags.ts +10 -0
- package/src/utils/getConfig.ts +10 -0
- package/src/utils/getCosmiConfig.ts +9 -3
- package/src/utils/getSummary.ts +1 -1
- package/src/utils/runHook.ts +27 -9
- package/src/utils/telemetry.ts +16 -3
- package/bin/kubb.cjs +0 -18
- package/dist/agent-C6o_6GSJ.cjs +0 -92
- package/dist/agent-C6o_6GSJ.cjs.map +0 -1
- package/dist/agent-CEHZSBVT.cjs.map +0 -1
- package/dist/agent-L50VNhXv.js +0 -88
- package/dist/agent-L50VNhXv.js.map +0 -1
- package/dist/agent-SWUT_sgq.js.map +0 -1
- package/dist/constants-BTUap0zs.cjs.map +0 -1
- package/dist/constants-CM3dJzjK.js +0 -67
- package/dist/constants-CM3dJzjK.js.map +0 -1
- package/dist/define--M_JMcDC.js +0 -25
- package/dist/define--M_JMcDC.js.map +0 -1
- package/dist/define-D6Kfm7-Z.cjs +0 -36
- package/dist/define-D6Kfm7-Z.cjs.map +0 -1
- package/dist/errors-6mF_WKxg.js +0 -27
- package/dist/errors-6mF_WKxg.js.map +0 -1
- package/dist/errors-DBW0N9w4.cjs.map +0 -1
- package/dist/generate-CNkdrX6u.cjs.map +0 -1
- package/dist/generate-CfoLoEd0.js.map +0 -1
- package/dist/generate-CtFkZeTU.js.map +0 -1
- package/dist/generate-nJaqPdGL.cjs.map +0 -1
- package/dist/init-BAs1d5rK.cjs.map +0 -1
- package/dist/init-C-InrmSY.js.map +0 -1
- package/dist/init-D_oERuMn.js.map +0 -1
- package/dist/init-hmolV6B4.cjs.map +0 -1
- package/dist/jiti-Cd3S0xwr.cjs +0 -16
- package/dist/jiti-Cd3S0xwr.cjs.map +0 -1
- package/dist/jiti-e08mD2Ph.js +0 -11
- package/dist/jiti-e08mD2Ph.js.map +0 -1
- package/dist/mcp-ChHFPRzD.cjs.map +0 -1
- package/dist/mcp-D2SHEg_d.js.map +0 -1
- package/dist/package-C-fC2Eoy.cjs.map +0 -1
- package/dist/package-DxH_MEG4.js +0 -6
- package/dist/package-DxH_MEG4.js.map +0 -1
- package/dist/shell-7HPrTCJ5.cjs.map +0 -1
- package/dist/shell-DqqWsHCD.js.map +0 -1
- package/dist/telemetry-Cn9X1I5B.cjs.map +0 -1
- package/dist/telemetry-DxiR7clS.js.map +0 -1
- package/dist/validate--IXRJEhp.js.map +0 -1
- package/dist/validate-BK3pc8zv.cjs.map +0 -1
- package/dist/validate-Bbrn3Q-A.cjs.map +0 -1
- package/dist/validate-l8vLmwKA.js.map +0 -1
- package/src/utils/getIntro.ts +0 -1
package/src/runners/generate.ts
CHANGED
|
@@ -4,26 +4,13 @@ import process from 'node:process'
|
|
|
4
4
|
import { styleText } from 'node:util'
|
|
5
5
|
import * as clack from '@clack/prompts'
|
|
6
6
|
import type { AsyncEventEmitter } from '@internals/utils'
|
|
7
|
-
import { AsyncEventEmitter as AsyncEventEmitterClass, executeIfOnline, toError } from '@internals/utils'
|
|
8
|
-
import {
|
|
9
|
-
type CLIOptions,
|
|
10
|
-
type Config,
|
|
11
|
-
detectFormatter,
|
|
12
|
-
detectLinter,
|
|
13
|
-
formatters,
|
|
14
|
-
getConfigs,
|
|
15
|
-
isInputPath,
|
|
16
|
-
type KubbEvents,
|
|
17
|
-
linters,
|
|
18
|
-
logLevel as logLevelMap,
|
|
19
|
-
PromiseManager,
|
|
20
|
-
safeBuild,
|
|
21
|
-
setup,
|
|
22
|
-
} from '@kubb/core'
|
|
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'
|
|
23
9
|
import { version } from '../../package.json'
|
|
24
10
|
import { KUBB_NPM_PACKAGE_URL } from '../constants.ts'
|
|
25
11
|
import { setupLogger } from '../loggers/utils.ts'
|
|
26
12
|
import { executeHooks } from '../utils/executeHooks.ts'
|
|
13
|
+
import { getConfigs } from '../utils/getConfig.ts'
|
|
27
14
|
import { getCosmiConfig } from '../utils/getCosmiConfig.ts'
|
|
28
15
|
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
29
16
|
import { startWatcher } from '../utils/watcher.ts'
|
|
@@ -31,7 +18,7 @@ import { startWatcher } from '../utils/watcher.ts'
|
|
|
31
18
|
type GenerateProps = {
|
|
32
19
|
input?: string
|
|
33
20
|
config: Config
|
|
34
|
-
|
|
21
|
+
hooks: AsyncEventEmitter<KubbHooks>
|
|
35
22
|
logLevel: number
|
|
36
23
|
}
|
|
37
24
|
|
|
@@ -39,7 +26,7 @@ type ToolMap = typeof formatters | typeof linters
|
|
|
39
26
|
|
|
40
27
|
type RunToolPassOptions = {
|
|
41
28
|
toolValue: string
|
|
42
|
-
detect: () => Promise<string |
|
|
29
|
+
detect: () => Promise<string | null>
|
|
43
30
|
toolMap: ToolMap
|
|
44
31
|
/** Short noun used in "Auto-detected <toolLabel>:" message, e.g. "formatter" or "linter". */
|
|
45
32
|
toolLabel: string
|
|
@@ -49,7 +36,7 @@ type RunToolPassOptions = {
|
|
|
49
36
|
configName: string | undefined
|
|
50
37
|
outputPath: string
|
|
51
38
|
logLevel: number
|
|
52
|
-
|
|
39
|
+
hooks: AsyncEventEmitter<KubbHooks>
|
|
53
40
|
onStart: () => Promise<void>
|
|
54
41
|
onEnd: () => Promise<void>
|
|
55
42
|
}
|
|
@@ -64,7 +51,7 @@ async function runToolPass({
|
|
|
64
51
|
configName,
|
|
65
52
|
outputPath,
|
|
66
53
|
logLevel,
|
|
67
|
-
|
|
54
|
+
hooks,
|
|
68
55
|
onStart,
|
|
69
56
|
onEnd,
|
|
70
57
|
}: RunToolPassOptions) {
|
|
@@ -74,13 +61,15 @@ async function runToolPass({
|
|
|
74
61
|
if (resolvedTool === 'auto') {
|
|
75
62
|
const detected = await detect()
|
|
76
63
|
if (!detected) {
|
|
77
|
-
await
|
|
64
|
+
await hooks.emit('kubb:warn', noToolMessage)
|
|
78
65
|
} else {
|
|
79
66
|
resolvedTool = detected
|
|
80
|
-
await
|
|
67
|
+
await hooks.emit('kubb:info', `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}`)
|
|
81
68
|
}
|
|
82
69
|
}
|
|
83
70
|
|
|
71
|
+
let toolError: Error | undefined
|
|
72
|
+
|
|
84
73
|
if (resolvedTool && resolvedTool !== 'auto' && resolvedTool in toolMap) {
|
|
85
74
|
const toolConfig = toolMap[resolvedTool as keyof ToolMap]
|
|
86
75
|
|
|
@@ -88,18 +77,18 @@ async function runToolPass({
|
|
|
88
77
|
const hookId = createHash('sha256').update([configName, resolvedTool].filter(Boolean).join('-')).digest('hex')
|
|
89
78
|
|
|
90
79
|
// Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
|
|
91
|
-
// where hook:end fires synchronously inside emit('hook:start') before the listener is registered.
|
|
80
|
+
// where hook:end fires synchronously inside emit('kubb:hook:start') before the listener is registered.
|
|
92
81
|
const hookEndPromise = new Promise<void>((resolve, reject) => {
|
|
93
82
|
const handler = ({ id, success, error }: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
|
|
94
83
|
if (id !== hookId) return
|
|
95
|
-
|
|
84
|
+
hooks.off('kubb:hook:end', handler)
|
|
96
85
|
if (!success) {
|
|
97
86
|
reject(error ?? new Error(`${toolConfig.errorMessage}`))
|
|
98
87
|
return
|
|
99
88
|
}
|
|
100
|
-
|
|
89
|
+
hooks
|
|
101
90
|
.emit(
|
|
102
|
-
'success',
|
|
91
|
+
'kubb:success',
|
|
103
92
|
[
|
|
104
93
|
`${successPrefix} with ${styleText('dim', resolvedTool)}`,
|
|
105
94
|
logLevel >= logLevelMap.info ? `on ${styleText('dim', outputPath)}` : undefined,
|
|
@@ -111,10 +100,10 @@ async function runToolPass({
|
|
|
111
100
|
.then(resolve)
|
|
112
101
|
.catch(reject)
|
|
113
102
|
}
|
|
114
|
-
|
|
103
|
+
hooks.on('kubb:hook:end', handler)
|
|
115
104
|
})
|
|
116
105
|
|
|
117
|
-
await
|
|
106
|
+
await hooks.emit('kubb:hook:start', {
|
|
118
107
|
id: hookId,
|
|
119
108
|
command: toolConfig.command,
|
|
120
109
|
args: toolConfig.args(outputPath),
|
|
@@ -122,59 +111,52 @@ async function runToolPass({
|
|
|
122
111
|
|
|
123
112
|
await hookEndPromise
|
|
124
113
|
} catch (caughtError) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
114
|
+
// Use the actual error from the hook. toolConfig.errorMessage (e.g. "Oxlint not found")
|
|
115
|
+
// is misleading when the binary was found and ran but exited with a non-zero code.
|
|
116
|
+
// runHook already emitted kubb:error for binary-not-found cases; here we surface the
|
|
117
|
+
// real reason (e.g. "Hook execute failed: oxlint --fix …") for non-zero exits.
|
|
118
|
+
const err = toError(caughtError)
|
|
119
|
+
await hooks.emit('kubb:error', err)
|
|
120
|
+
toolError = err
|
|
128
121
|
}
|
|
129
122
|
}
|
|
130
123
|
|
|
131
124
|
await onEnd()
|
|
125
|
+
|
|
126
|
+
if (toolError) {
|
|
127
|
+
throw toolError
|
|
128
|
+
}
|
|
132
129
|
}
|
|
133
130
|
|
|
134
|
-
async function generate(
|
|
135
|
-
const
|
|
131
|
+
async function generate(options: GenerateProps): Promise<void> {
|
|
132
|
+
const { input, hooks, logLevel } = options
|
|
133
|
+
|
|
136
134
|
const hrStart = process.hrtime()
|
|
135
|
+
const inputPath = input ?? ('path' in options.config.input ? options.config.input.path : undefined)
|
|
137
136
|
|
|
138
137
|
const config: Config = {
|
|
139
|
-
...
|
|
140
|
-
root: userConfig.root || process.cwd(),
|
|
138
|
+
...options.config,
|
|
141
139
|
input: inputPath
|
|
142
140
|
? {
|
|
143
|
-
...
|
|
141
|
+
...options.config.input,
|
|
144
142
|
path: inputPath,
|
|
145
143
|
}
|
|
146
|
-
:
|
|
147
|
-
output
|
|
148
|
-
|
|
149
|
-
barrelType: 'named',
|
|
150
|
-
extension: {
|
|
151
|
-
'.ts': '.ts',
|
|
152
|
-
},
|
|
153
|
-
format: 'prettier',
|
|
154
|
-
...userConfig.output,
|
|
155
|
-
},
|
|
156
|
-
}
|
|
144
|
+
: options.config.input,
|
|
145
|
+
...options.config.output,
|
|
146
|
+
} satisfies Config
|
|
157
147
|
|
|
158
|
-
|
|
148
|
+
const kubb = createKubb(config, { hooks })
|
|
149
|
+
await kubb.setup()
|
|
159
150
|
|
|
160
|
-
await
|
|
151
|
+
await hooks.emit('kubb:generation:start', config)
|
|
161
152
|
|
|
162
|
-
|
|
163
|
-
config,
|
|
164
|
-
events,
|
|
165
|
-
})
|
|
153
|
+
await hooks.emit('kubb:info', config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', inputPath)
|
|
166
154
|
|
|
167
|
-
await
|
|
155
|
+
await hooks.emit('kubb:info', config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', inputPath)
|
|
168
156
|
|
|
169
|
-
const { files, failedPlugins, pluginTimings, error } = await safeBuild(
|
|
170
|
-
{
|
|
171
|
-
config,
|
|
172
|
-
events,
|
|
173
|
-
},
|
|
174
|
-
{ pluginManager, fabric, events, sources },
|
|
175
|
-
)
|
|
157
|
+
const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild()
|
|
176
158
|
|
|
177
|
-
await
|
|
159
|
+
await hooks.emit('kubb:info', 'Load summary')
|
|
178
160
|
|
|
179
161
|
// Handle build failures (either from failed plugins or general errors)
|
|
180
162
|
|
|
@@ -189,12 +171,12 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
189
171
|
].filter(Boolean)
|
|
190
172
|
|
|
191
173
|
for (const err of allErrors) {
|
|
192
|
-
await
|
|
174
|
+
await hooks.emit('kubb:error', err)
|
|
193
175
|
}
|
|
194
176
|
|
|
195
|
-
await
|
|
177
|
+
await hooks.emit('kubb:generation:end', config, files, kubb.sources)
|
|
196
178
|
|
|
197
|
-
await
|
|
179
|
+
await hooks.emit('kubb:generation:summary', config, {
|
|
198
180
|
failedPlugins,
|
|
199
181
|
filesCreated: files.length,
|
|
200
182
|
status: 'failed',
|
|
@@ -206,7 +188,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
206
188
|
buildTelemetryEvent({
|
|
207
189
|
command: 'generate',
|
|
208
190
|
kubbVersion: version,
|
|
209
|
-
plugins:
|
|
191
|
+
plugins: Array.from(driver.plugins.values(), (p) => ({
|
|
192
|
+
name: p.name,
|
|
193
|
+
options: p.options as Record<string, unknown>,
|
|
194
|
+
})),
|
|
210
195
|
hrStart,
|
|
211
196
|
filesCreated: files.length,
|
|
212
197
|
status: 'failed',
|
|
@@ -216,8 +201,8 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
216
201
|
process.exit(1)
|
|
217
202
|
}
|
|
218
203
|
|
|
219
|
-
await
|
|
220
|
-
await
|
|
204
|
+
await hooks.emit('kubb:success', 'Generation successfully', inputPath)
|
|
205
|
+
await hooks.emit('kubb:generation:end', config, files, kubb.sources)
|
|
221
206
|
|
|
222
207
|
const outputPath = path.resolve(config.root, config.output.path)
|
|
223
208
|
|
|
@@ -228,13 +213,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
228
213
|
toolMap: formatters,
|
|
229
214
|
toolLabel: 'formatter',
|
|
230
215
|
successPrefix: 'Formatting',
|
|
231
|
-
noToolMessage: 'No formatter found (
|
|
216
|
+
noToolMessage: 'No formatter found (oxfmt, biome, or prettier). Skipping formatting.',
|
|
232
217
|
configName: config.name,
|
|
233
218
|
outputPath,
|
|
234
219
|
logLevel,
|
|
235
|
-
|
|
236
|
-
onStart: () =>
|
|
237
|
-
onEnd: () =>
|
|
220
|
+
hooks,
|
|
221
|
+
onStart: () => hooks.emit('kubb:format:start'),
|
|
222
|
+
onEnd: () => hooks.emit('kubb:format:end'),
|
|
238
223
|
})
|
|
239
224
|
}
|
|
240
225
|
|
|
@@ -245,25 +230,25 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
245
230
|
toolMap: linters,
|
|
246
231
|
toolLabel: 'linter',
|
|
247
232
|
successPrefix: 'Linting',
|
|
248
|
-
noToolMessage: 'No linter found (
|
|
233
|
+
noToolMessage: 'No linter found (oxlint, biome, or eslint). Skipping linting.',
|
|
249
234
|
configName: config.name,
|
|
250
235
|
outputPath,
|
|
251
236
|
logLevel,
|
|
252
|
-
|
|
253
|
-
onStart: () =>
|
|
254
|
-
onEnd: () =>
|
|
237
|
+
hooks,
|
|
238
|
+
onStart: () => hooks.emit('kubb:lint:start'),
|
|
239
|
+
onEnd: () => hooks.emit('kubb:lint:end'),
|
|
255
240
|
})
|
|
256
241
|
}
|
|
257
242
|
|
|
258
243
|
if (config.hooks) {
|
|
259
|
-
await
|
|
260
|
-
await executeHooks({
|
|
244
|
+
await hooks.emit('kubb:hooks:start')
|
|
245
|
+
await executeHooks({ configHooks: config.hooks, hooks })
|
|
261
246
|
|
|
262
|
-
await
|
|
247
|
+
await hooks.emit('kubb:hooks:end')
|
|
263
248
|
}
|
|
264
249
|
|
|
265
250
|
// Only reached when there are no failures (process.exit(1) is called above otherwise)
|
|
266
|
-
await
|
|
251
|
+
await hooks.emit('kubb:generation:summary', config, {
|
|
267
252
|
failedPlugins,
|
|
268
253
|
filesCreated: files.length,
|
|
269
254
|
status: 'success',
|
|
@@ -274,7 +259,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
274
259
|
const telemetryEvent = buildTelemetryEvent({
|
|
275
260
|
command: 'generate',
|
|
276
261
|
kubbVersion: version,
|
|
277
|
-
plugins:
|
|
262
|
+
plugins: Array.from(driver.plugins.values(), (p) => ({
|
|
263
|
+
name: p.name,
|
|
264
|
+
options: p.options as Record<string, unknown>,
|
|
265
|
+
})),
|
|
278
266
|
hrStart,
|
|
279
267
|
filesCreated: files.length,
|
|
280
268
|
status: 'success',
|
|
@@ -292,10 +280,9 @@ type GenerateCommandOptions = {
|
|
|
292
280
|
|
|
293
281
|
export async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }: GenerateCommandOptions): Promise<void> {
|
|
294
282
|
const logLevel = logLevelMap[logLevelKey as keyof typeof logLevelMap] ?? logLevelMap.info
|
|
295
|
-
const
|
|
296
|
-
const promiseManager = new PromiseManager()
|
|
283
|
+
const hooks = new AsyncEventEmitterClass<KubbHooks>()
|
|
297
284
|
|
|
298
|
-
await setupLogger(
|
|
285
|
+
await setupLogger(hooks, { logLevel })
|
|
299
286
|
|
|
300
287
|
await executeIfOnline(async () => {
|
|
301
288
|
try {
|
|
@@ -304,7 +291,7 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
|
|
|
304
291
|
const latestVersion = data.version
|
|
305
292
|
|
|
306
293
|
if (latestVersion && version < latestVersion) {
|
|
307
|
-
await
|
|
294
|
+
await hooks.emit('kubb:version:new', version, latestVersion)
|
|
308
295
|
}
|
|
309
296
|
} catch {
|
|
310
297
|
// Ignore network errors for version check
|
|
@@ -315,37 +302,31 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
|
|
|
315
302
|
const result = await getCosmiConfig('kubb', configPath)
|
|
316
303
|
const configs = await getConfigs(result.config, { input } as CLIOptions)
|
|
317
304
|
|
|
318
|
-
await
|
|
319
|
-
await
|
|
320
|
-
await
|
|
321
|
-
await
|
|
305
|
+
await hooks.emit('kubb:config:start')
|
|
306
|
+
await hooks.emit('kubb:info', 'Config loaded', path.relative(process.cwd(), result.filepath))
|
|
307
|
+
await hooks.emit('kubb:success', 'Config loaded successfully', path.relative(process.cwd(), result.filepath))
|
|
308
|
+
await hooks.emit('kubb:config:end', configs)
|
|
322
309
|
|
|
323
|
-
await
|
|
310
|
+
await hooks.emit('kubb:lifecycle:start', version)
|
|
324
311
|
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
events.removeAll()
|
|
312
|
+
for (const config of configs) {
|
|
313
|
+
if (isInputPath(config) && watch) {
|
|
314
|
+
await startWatcher([input || config.input.path], async (paths) => {
|
|
315
|
+
// remove to avoid duplicate listeners after each change
|
|
316
|
+
hooks.removeAll()
|
|
331
317
|
|
|
332
|
-
|
|
318
|
+
await generate({ input, config, logLevel, hooks })
|
|
333
319
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
await generate({ input, config, logLevel, events })
|
|
320
|
+
clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
|
|
321
|
+
})
|
|
322
|
+
} else {
|
|
323
|
+
await generate({ input, config, logLevel, hooks })
|
|
341
324
|
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
await promiseManager.run('seq', promises)
|
|
325
|
+
}
|
|
345
326
|
|
|
346
|
-
await
|
|
327
|
+
await hooks.emit('kubb:lifecycle:end')
|
|
347
328
|
} catch (error) {
|
|
348
|
-
await
|
|
329
|
+
await hooks.emit('kubb:error', toError(error))
|
|
349
330
|
process.exit(1)
|
|
350
331
|
}
|
|
351
332
|
}
|
package/src/runners/init.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { styleText } from 'node:util'
|
|
|
5
5
|
import * as clack from '@clack/prompts'
|
|
6
6
|
import type { PackageManagerInfo } from '@internals/utils'
|
|
7
7
|
import { detectPackageManager } from '@internals/utils'
|
|
8
|
-
import { initDefaults, pluginDefaultConfigs } from '../constants.ts'
|
|
8
|
+
import { initDefaults, KUBB_CONFIG_FILENAME, pluginDefaultConfigs } from '../constants.ts'
|
|
9
9
|
import { hasPackageJson, initPackageJson, installPackages } from '../utils/packageManager.ts'
|
|
10
10
|
|
|
11
11
|
type PluginOption = {
|
|
@@ -123,7 +123,7 @@ function generateConfigFile(selectedPlugins: PluginOption[], inputPath: string,
|
|
|
123
123
|
})
|
|
124
124
|
.join('\n')
|
|
125
125
|
|
|
126
|
-
return `import { defineConfig } from '
|
|
126
|
+
return `import { defineConfig } from 'kubb'
|
|
127
127
|
${imports}
|
|
128
128
|
|
|
129
129
|
export default defineConfig({
|
|
@@ -257,7 +257,7 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
// Install packages
|
|
260
|
-
const packagesToInstall = ['
|
|
260
|
+
const packagesToInstall = ['kubb', ...selectedPlugins.map((p) => p.packageName)]
|
|
261
261
|
|
|
262
262
|
const spinner = clack.spinner()
|
|
263
263
|
spinner.start(`Installing ${packagesToInstall.length} packages with ${packageManager.name}`)
|
|
@@ -272,17 +272,17 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
|
|
|
272
272
|
|
|
273
273
|
// Generate config file
|
|
274
274
|
const configSpinner = clack.spinner()
|
|
275
|
-
configSpinner.start(
|
|
275
|
+
configSpinner.start(`Creating ${KUBB_CONFIG_FILENAME}`)
|
|
276
276
|
|
|
277
277
|
const configContent = generateConfigFile(selectedPlugins, inputPath, outputPath)
|
|
278
|
-
const configPath = path.join(cwd,
|
|
278
|
+
const configPath = path.join(cwd, KUBB_CONFIG_FILENAME)
|
|
279
279
|
|
|
280
280
|
if (fs.existsSync(configPath)) {
|
|
281
|
-
configSpinner.stop(
|
|
281
|
+
configSpinner.stop(`${KUBB_CONFIG_FILENAME} already exists`)
|
|
282
282
|
|
|
283
283
|
if (!yes) {
|
|
284
284
|
const shouldOverwrite = await clack.confirm({
|
|
285
|
-
message:
|
|
285
|
+
message: `${KUBB_CONFIG_FILENAME} already exists. Overwrite?`,
|
|
286
286
|
initialValue: false,
|
|
287
287
|
})
|
|
288
288
|
|
|
@@ -291,12 +291,12 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
configSpinner.start(
|
|
294
|
+
configSpinner.start(`Overwriting ${KUBB_CONFIG_FILENAME}`)
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
fs.writeFileSync(configPath, configContent, 'utf-8')
|
|
298
298
|
|
|
299
|
-
configSpinner.stop(
|
|
299
|
+
configSpinner.stop(`Created ${KUBB_CONFIG_FILENAME}`)
|
|
300
300
|
|
|
301
301
|
clack.outro(
|
|
302
302
|
styleText('green', '✓ All set!') +
|
package/src/runners/mcp.ts
CHANGED
|
@@ -12,7 +12,9 @@ type McpOptions = {
|
|
|
12
12
|
export async function runMcp({ version }: McpOptions): Promise<void> {
|
|
13
13
|
let mod: typeof McpModule
|
|
14
14
|
try {
|
|
15
|
-
mod = (await jiti.import('@kubb/mcp', {
|
|
15
|
+
mod = (await jiti.import('@kubb/mcp', {
|
|
16
|
+
default: true,
|
|
17
|
+
})) as typeof McpModule
|
|
16
18
|
} catch (_e) {
|
|
17
19
|
console.error(`Import of '@kubb/mcp' is required to start the MCP server`)
|
|
18
20
|
process.exit(1)
|
|
@@ -24,9 +26,23 @@ export async function runMcp({ version }: McpOptions): Promise<void> {
|
|
|
24
26
|
console.log('⏳ Starting MCP server...')
|
|
25
27
|
console.warn(styleText('yellow', 'This feature is still under development — use with caution'))
|
|
26
28
|
run()
|
|
27
|
-
await sendTelemetry(
|
|
29
|
+
await sendTelemetry(
|
|
30
|
+
buildTelemetryEvent({
|
|
31
|
+
command: 'mcp',
|
|
32
|
+
kubbVersion: version,
|
|
33
|
+
hrStart,
|
|
34
|
+
status: 'success',
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
28
37
|
} catch (error) {
|
|
29
|
-
await sendTelemetry(
|
|
38
|
+
await sendTelemetry(
|
|
39
|
+
buildTelemetryEvent({
|
|
40
|
+
command: 'mcp',
|
|
41
|
+
kubbVersion: version,
|
|
42
|
+
hrStart,
|
|
43
|
+
status: 'failed',
|
|
44
|
+
}),
|
|
45
|
+
)
|
|
30
46
|
console.error(getErrorMessage(error))
|
|
31
47
|
}
|
|
32
48
|
}
|
package/src/runners/validate.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import process from 'node:process'
|
|
2
2
|
import { getErrorMessage } from '@internals/utils'
|
|
3
|
-
import
|
|
4
|
-
import { jiti } from '../utils/jiti.ts'
|
|
3
|
+
import { parseDocument, validateDocument } from '@kubb/adapter-oas'
|
|
5
4
|
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
6
5
|
|
|
7
6
|
type ValidateOptions = {
|
|
@@ -10,24 +9,29 @@ type ValidateOptions = {
|
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
export async function runValidate({ input, version }: ValidateOptions): Promise<void> {
|
|
13
|
-
let mod: typeof OasModule
|
|
14
|
-
try {
|
|
15
|
-
mod = (await jiti.import('@kubb/oas', { default: true })) as typeof OasModule
|
|
16
|
-
} catch (_e) {
|
|
17
|
-
console.error(`Import of '@kubb/oas' is required to do validation`)
|
|
18
|
-
process.exit(1)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const { parse } = mod
|
|
22
12
|
const hrStart = process.hrtime()
|
|
23
13
|
try {
|
|
24
|
-
const
|
|
25
|
-
await
|
|
14
|
+
const document = await parseDocument(input)
|
|
15
|
+
await validateDocument(document, { throwOnError: true })
|
|
26
16
|
|
|
27
|
-
await sendTelemetry(
|
|
17
|
+
await sendTelemetry(
|
|
18
|
+
buildTelemetryEvent({
|
|
19
|
+
command: 'validate',
|
|
20
|
+
kubbVersion: version,
|
|
21
|
+
hrStart,
|
|
22
|
+
status: 'success',
|
|
23
|
+
}),
|
|
24
|
+
)
|
|
28
25
|
console.log('✅ Validation success')
|
|
29
26
|
} catch (error) {
|
|
30
|
-
await sendTelemetry(
|
|
27
|
+
await sendTelemetry(
|
|
28
|
+
buildTelemetryEvent({
|
|
29
|
+
command: 'validate',
|
|
30
|
+
kubbVersion: version,
|
|
31
|
+
hrStart,
|
|
32
|
+
status: 'failed',
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
31
35
|
console.error('❌ Validation failed')
|
|
32
36
|
console.error(getErrorMessage(error))
|
|
33
37
|
process.exit(1)
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type QuiteFlag = '--help' | '-h' | '--version' | '-v'
|
|
2
|
+
|
|
3
|
+
export type GenerateFlag = '--config' | '-c' | '--log-level' | '-l' | '--watch' | '-w' | '--debug' | '-d' | '--verbose' | '-v' | '--silent' | '-s'
|
|
4
|
+
|
|
5
|
+
export type ValidateFlag = '--input' | '-i'
|
|
6
|
+
|
|
7
|
+
export type InitFlag = '--yes' | '-y'
|
|
8
|
+
|
|
9
|
+
export type AgentStartFlag = '--config' | '-c' | '--port' | '-p' | '--host' | '--allow-write' | '--allow-all'
|
|
10
|
+
|
|
11
|
+
export type Arg = QuiteFlag | GenerateFlag | ValidateFlag | InitFlag | AgentStartFlag
|
|
@@ -2,15 +2,15 @@ import { createHash } from 'node:crypto'
|
|
|
2
2
|
import { styleText } from 'node:util'
|
|
3
3
|
import type { AsyncEventEmitter } from '@internals/utils'
|
|
4
4
|
import { tokenize } from '@internals/utils'
|
|
5
|
-
import type { Config,
|
|
5
|
+
import type { Config, KubbHooks } from '@kubb/core'
|
|
6
6
|
|
|
7
7
|
type ExecutingHooksProps = {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
configHooks: NonNullable<Config['hooks']>
|
|
9
|
+
hooks: AsyncEventEmitter<KubbHooks>
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export async function executeHooks({
|
|
13
|
-
const commands = Array.isArray(
|
|
12
|
+
export async function executeHooks({ configHooks, hooks }: ExecutingHooksProps): Promise<void> {
|
|
13
|
+
const commands = Array.isArray(configHooks.done) ? configHooks.done : [configHooks.done].filter(Boolean)
|
|
14
14
|
|
|
15
15
|
for (const command of commands) {
|
|
16
16
|
const [cmd, ...args] = tokenize(command)
|
|
@@ -22,24 +22,24 @@ export async function executeHooks({ hooks, events }: ExecutingHooksProps): Prom
|
|
|
22
22
|
const hookId = createHash('sha256').update(command).digest('hex')
|
|
23
23
|
|
|
24
24
|
// Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
|
|
25
|
-
// where hook:end fires synchronously inside emit('hook:start') before the listener is registered.
|
|
25
|
+
// where hook:end fires synchronously inside emit('kubb:hook:start') before the listener is registered.
|
|
26
26
|
const hookEndPromise = new Promise<void>((resolve, reject) => {
|
|
27
27
|
const handler = ({ id, success, error }: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
|
|
28
28
|
if (id !== hookId) return
|
|
29
|
-
|
|
29
|
+
hooks.off('kubb:hook:end', handler)
|
|
30
30
|
if (!success) {
|
|
31
31
|
reject(error ?? new Error(`Hook failed: ${command}`))
|
|
32
32
|
return
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
.emit('success', `${styleText('dim', command)} successfully executed`)
|
|
34
|
+
hooks
|
|
35
|
+
.emit('kubb:success', `${styleText('dim', command)} successfully executed`)
|
|
36
36
|
.then(resolve)
|
|
37
37
|
.catch(reject)
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
hooks.on('kubb:hook:end', handler)
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
await
|
|
42
|
+
await hooks.emit('kubb:hook:start', { id: hookId, command: cmd, args })
|
|
43
43
|
await hookEndPromise
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard that checks whether a raw string is a member of a typed flag set.
|
|
3
|
+
* Avoids the need for type assertions when working with `Set<T extends string>`.
|
|
4
|
+
*/
|
|
5
|
+
export function isFlag<T extends string>(set: ReadonlySet<T>, value: string): value is T {
|
|
6
|
+
for (const flag of set) {
|
|
7
|
+
if (flag === value) return true
|
|
8
|
+
}
|
|
9
|
+
return false
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CLIOptions, Config, PossibleConfig } from '@kubb/core'
|
|
2
|
+
|
|
3
|
+
type ConfigInput = PossibleConfig<CLIOptions>
|
|
4
|
+
|
|
5
|
+
export async function getConfigs(config: ConfigInput, args: CLIOptions): Promise<Array<Config>> {
|
|
6
|
+
const resolved = await (typeof config === 'function' ? config(args as CLIOptions) : config)
|
|
7
|
+
const userConfigs = Array.isArray(resolved) ? resolved : [resolved]
|
|
8
|
+
|
|
9
|
+
return userConfigs.map((item) => ({ ...item, plugins: item.plugins ?? [] }) as Config)
|
|
10
|
+
}
|