@kubb/cli 5.0.0-alpha.6 → 5.0.0-alpha.60
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-CCb1mJmy.cjs → agent-9Xq6jMhS.cjs} +5 -5
- package/dist/agent-9Xq6jMhS.cjs.map +1 -0
- package/dist/agent-CouS-H2S.js +112 -0
- package/dist/agent-CouS-H2S.js.map +1 -0
- package/dist/{agent-Dq1OWkeN.js → agent-NodblB-8.js} +5 -5
- package/dist/agent-NodblB-8.js.map +1 -0
- package/dist/agent-Zo8CQwxZ.cjs +116 -0
- package/dist/agent-Zo8CQwxZ.cjs.map +1 -0
- package/dist/{constants-BTUap0zs.cjs → constants-BgLUQ1nx.cjs} +50 -9
- package/dist/constants-BgLUQ1nx.cjs.map +1 -0
- package/dist/{constants-CM3dJzjK.js → constants-mIdg9ls2.js} +39 -10
- package/dist/constants-mIdg9ls2.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-CH1EAyW4.cjs → generate-BbtShu-5.cjs} +3 -3
- package/dist/generate-BbtShu-5.cjs.map +1 -0
- package/dist/{generate-B9sA0jDk.js → generate-CIDU9zdG.js} +3 -3
- package/dist/generate-CIDU9zdG.js.map +1 -0
- package/dist/{generate-DM1NsxI1.cjs → generate-DDVnxPVr.cjs} +555 -262
- package/dist/generate-DDVnxPVr.cjs.map +1 -0
- package/dist/{generate-D2nVnbYA.js → generate-DvxkzxAz.js} +548 -255
- package/dist/generate-DvxkzxAz.js.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-CmrUd26x.cjs} +31 -21
- package/dist/init-CmrUd26x.cjs.map +1 -0
- package/dist/{init-Dpm2lzZO.js → init-D6yLHBUa.js} +4 -4
- package/dist/init-D6yLHBUa.js.map +1 -0
- package/dist/{init-C-InrmSY.js → init-DEC1gCsk.js} +27 -17
- package/dist/init-DEC1gCsk.js.map +1 -0
- package/dist/{init-B80nQshO.cjs → init-LU4o4mUD.cjs} +4 -4
- package/dist/init-LU4o4mUD.cjs.map +1 -0
- package/dist/{mcp-ChHFPRzD.cjs → mcp-BCLUiocM.cjs} +12 -6
- package/dist/mcp-BCLUiocM.cjs.map +1 -0
- package/dist/{mcp-BDZI-Jjk.js → mcp-BYPRrpmX.js} +4 -4
- package/dist/{mcp-BDZI-Jjk.js.map → mcp-BYPRrpmX.js.map} +1 -1
- package/dist/{mcp-D2SHEg_d.js → mcp-C-HbwyVA.js} +11 -4
- package/dist/mcp-C-HbwyVA.js.map +1 -0
- package/dist/{mcp-gFMKRdfg.cjs → mcp-f5A7Ujp8.cjs} +4 -4
- package/dist/{mcp-gFMKRdfg.cjs.map → mcp-f5A7Ujp8.cjs.map} +1 -1
- package/dist/{package-BTEeDglN.cjs → package-8UFu3zi8.cjs} +2 -2
- package/dist/package-8UFu3zi8.cjs.map +1 -0
- package/dist/package-CI2GGuZk.js +6 -0
- package/dist/package-CI2GGuZk.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-DxiR7clS.js → telemetry-CS5ZE0cN.js} +48 -6
- package/dist/telemetry-CS5ZE0cN.js.map +1 -0
- package/dist/{telemetry-Cn9X1I5B.cjs → telemetry-kmnc5OAT.cjs} +50 -8
- package/dist/telemetry-kmnc5OAT.cjs.map +1 -0
- package/dist/{validate-l8vLmwKA.js → validate-BR_0eveE.js} +5 -13
- package/dist/validate-BR_0eveE.js.map +1 -0
- package/dist/{validate-BYzj0qVB.cjs → validate-CQIfScXY.cjs} +4 -4
- package/dist/validate-CQIfScXY.cjs.map +1 -0
- package/dist/{validate-Bbrn3Q-A.cjs → validate-Db7vfaU9.cjs} +6 -14
- package/dist/validate-Db7vfaU9.cjs.map +1 -0
- package/dist/{validate-PAYtxnS6.js → validate-vlhkev2W.js} +4 -4
- package/dist/validate-vlhkev2W.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 +40 -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 +98 -116
- 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 +15 -15
- 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-CCb1mJmy.cjs.map +0 -1
- package/dist/agent-Dq1OWkeN.js.map +0 -1
- package/dist/agent-L50VNhXv.js +0 -88
- package/dist/agent-L50VNhXv.js.map +0 -1
- package/dist/constants-BTUap0zs.cjs.map +0 -1
- 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-B9sA0jDk.js.map +0 -1
- package/dist/generate-CH1EAyW4.cjs.map +0 -1
- package/dist/generate-D2nVnbYA.js.map +0 -1
- package/dist/generate-DM1NsxI1.cjs.map +0 -1
- package/dist/init-B80nQshO.cjs.map +0 -1
- package/dist/init-C-InrmSY.js.map +0 -1
- package/dist/init-Dpm2lzZO.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-BTEeDglN.cjs.map +0 -1
- package/dist/package-BZMTT_6s.js +0 -6
- package/dist/package-BZMTT_6s.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-BYzj0qVB.cjs.map +0 -1
- package/dist/validate-Bbrn3Q-A.cjs.map +0 -1
- package/dist/validate-PAYtxnS6.js.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', { message: noToolMessage })
|
|
78
65
|
} else {
|
|
79
66
|
resolvedTool = detected
|
|
80
|
-
await
|
|
67
|
+
await hooks.emit('kubb:info', { message: `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,33 +77,32 @@ 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
|
-
const handler = (
|
|
94
|
-
if (id !== hookId) return
|
|
95
|
-
|
|
96
|
-
if (!success) {
|
|
97
|
-
reject(error ?? new Error(`${toolConfig.errorMessage}`))
|
|
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}`))
|
|
98
87
|
return
|
|
99
88
|
}
|
|
100
|
-
|
|
101
|
-
.emit(
|
|
102
|
-
|
|
103
|
-
[
|
|
89
|
+
hooks
|
|
90
|
+
.emit('kubb:success', {
|
|
91
|
+
message: [
|
|
104
92
|
`${successPrefix} with ${styleText('dim', resolvedTool)}`,
|
|
105
93
|
logLevel >= logLevelMap.info ? `on ${styleText('dim', outputPath)}` : undefined,
|
|
106
94
|
'successfully',
|
|
107
95
|
]
|
|
108
96
|
.filter(Boolean)
|
|
109
97
|
.join(' '),
|
|
110
|
-
)
|
|
98
|
+
})
|
|
111
99
|
.then(resolve)
|
|
112
100
|
.catch(reject)
|
|
113
101
|
}
|
|
114
|
-
|
|
102
|
+
hooks.on('kubb:hook:end', handler)
|
|
115
103
|
})
|
|
116
104
|
|
|
117
|
-
await
|
|
105
|
+
await hooks.emit('kubb:hook:start', {
|
|
118
106
|
id: hookId,
|
|
119
107
|
command: toolConfig.command,
|
|
120
108
|
args: toolConfig.args(outputPath),
|
|
@@ -122,59 +110,52 @@ async function runToolPass({
|
|
|
122
110
|
|
|
123
111
|
await hookEndPromise
|
|
124
112
|
} catch (caughtError) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
128
120
|
}
|
|
129
121
|
}
|
|
130
122
|
|
|
131
123
|
await onEnd()
|
|
124
|
+
|
|
125
|
+
if (toolError) {
|
|
126
|
+
throw toolError
|
|
127
|
+
}
|
|
132
128
|
}
|
|
133
129
|
|
|
134
|
-
async function generate(
|
|
135
|
-
const
|
|
130
|
+
async function generate(options: GenerateProps): Promise<void> {
|
|
131
|
+
const { input, hooks, logLevel } = options
|
|
132
|
+
|
|
136
133
|
const hrStart = process.hrtime()
|
|
134
|
+
const inputPath = input ?? ('path' in options.config.input ? options.config.input.path : undefined)
|
|
137
135
|
|
|
138
136
|
const config: Config = {
|
|
139
|
-
...
|
|
140
|
-
root: userConfig.root || process.cwd(),
|
|
137
|
+
...options.config,
|
|
141
138
|
input: inputPath
|
|
142
139
|
? {
|
|
143
|
-
...
|
|
140
|
+
...options.config.input,
|
|
144
141
|
path: inputPath,
|
|
145
142
|
}
|
|
146
|
-
:
|
|
147
|
-
output
|
|
148
|
-
|
|
149
|
-
barrelType: 'named',
|
|
150
|
-
extension: {
|
|
151
|
-
'.ts': '.ts',
|
|
152
|
-
},
|
|
153
|
-
format: 'prettier',
|
|
154
|
-
...userConfig.output,
|
|
155
|
-
},
|
|
156
|
-
}
|
|
143
|
+
: options.config.input,
|
|
144
|
+
...options.config.output,
|
|
145
|
+
} satisfies Config
|
|
157
146
|
|
|
158
|
-
|
|
147
|
+
const kubb = createKubb(config, { hooks })
|
|
148
|
+
await kubb.setup()
|
|
159
149
|
|
|
160
|
-
await
|
|
150
|
+
await hooks.emit('kubb:generation:start', { config })
|
|
161
151
|
|
|
162
|
-
|
|
163
|
-
config,
|
|
164
|
-
events,
|
|
165
|
-
})
|
|
152
|
+
await hooks.emit('kubb:info', { message: config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', info: inputPath })
|
|
166
153
|
|
|
167
|
-
await
|
|
154
|
+
await hooks.emit('kubb:info', { message: config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', info: inputPath })
|
|
168
155
|
|
|
169
|
-
const { files, failedPlugins, pluginTimings, error } = await safeBuild(
|
|
170
|
-
{
|
|
171
|
-
config,
|
|
172
|
-
events,
|
|
173
|
-
},
|
|
174
|
-
{ pluginManager, fabric, events, sources },
|
|
175
|
-
)
|
|
156
|
+
const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild()
|
|
176
157
|
|
|
177
|
-
await
|
|
158
|
+
await hooks.emit('kubb:info', { message: 'Load summary' })
|
|
178
159
|
|
|
179
160
|
// Handle build failures (either from failed plugins or general errors)
|
|
180
161
|
|
|
@@ -189,12 +170,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
189
170
|
].filter(Boolean)
|
|
190
171
|
|
|
191
172
|
for (const err of allErrors) {
|
|
192
|
-
await
|
|
173
|
+
await hooks.emit('kubb:error', { error: err })
|
|
193
174
|
}
|
|
194
175
|
|
|
195
|
-
await
|
|
176
|
+
await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
|
|
196
177
|
|
|
197
|
-
await
|
|
178
|
+
await hooks.emit('kubb:generation:summary', {
|
|
179
|
+
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', { message: 'Generation successfully', info: inputPath })
|
|
205
|
+
await hooks.emit('kubb:generation:end', { config, files, sources: 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,26 @@ 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', {
|
|
252
|
+
config,
|
|
267
253
|
failedPlugins,
|
|
268
254
|
filesCreated: files.length,
|
|
269
255
|
status: 'success',
|
|
@@ -274,7 +260,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
274
260
|
const telemetryEvent = buildTelemetryEvent({
|
|
275
261
|
command: 'generate',
|
|
276
262
|
kubbVersion: version,
|
|
277
|
-
plugins:
|
|
263
|
+
plugins: Array.from(driver.plugins.values(), (p) => ({
|
|
264
|
+
name: p.name,
|
|
265
|
+
options: p.options as Record<string, unknown>,
|
|
266
|
+
})),
|
|
278
267
|
hrStart,
|
|
279
268
|
filesCreated: files.length,
|
|
280
269
|
status: 'success',
|
|
@@ -292,10 +281,9 @@ type GenerateCommandOptions = {
|
|
|
292
281
|
|
|
293
282
|
export async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }: GenerateCommandOptions): Promise<void> {
|
|
294
283
|
const logLevel = logLevelMap[logLevelKey as keyof typeof logLevelMap] ?? logLevelMap.info
|
|
295
|
-
const
|
|
296
|
-
const promiseManager = new PromiseManager()
|
|
284
|
+
const hooks = new AsyncEventEmitterClass<KubbHooks>()
|
|
297
285
|
|
|
298
|
-
await setupLogger(
|
|
286
|
+
await setupLogger(hooks, { logLevel })
|
|
299
287
|
|
|
300
288
|
await executeIfOnline(async () => {
|
|
301
289
|
try {
|
|
@@ -304,7 +292,7 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
|
|
|
304
292
|
const latestVersion = data.version
|
|
305
293
|
|
|
306
294
|
if (latestVersion && version < latestVersion) {
|
|
307
|
-
await
|
|
295
|
+
await hooks.emit('kubb:version:new', { currentVersion: version, latestVersion })
|
|
308
296
|
}
|
|
309
297
|
} catch {
|
|
310
298
|
// Ignore network errors for version check
|
|
@@ -315,37 +303,31 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
|
|
|
315
303
|
const result = await getCosmiConfig('kubb', configPath)
|
|
316
304
|
const configs = await getConfigs(result.config, { input } as CLIOptions)
|
|
317
305
|
|
|
318
|
-
await
|
|
319
|
-
await
|
|
320
|
-
await
|
|
321
|
-
await
|
|
322
|
-
|
|
323
|
-
await events.emit('lifecycle:start', version)
|
|
324
|
-
|
|
325
|
-
const promises = configs.map((config) => {
|
|
326
|
-
return async () => {
|
|
327
|
-
if (isInputPath(config) && watch) {
|
|
328
|
-
await startWatcher([input || config.input.path], async (paths) => {
|
|
329
|
-
// remove to avoid duplicate listeners after each change
|
|
330
|
-
events.removeAll()
|
|
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 })
|
|
331
310
|
|
|
332
|
-
|
|
311
|
+
await hooks.emit('kubb:lifecycle:start', { version })
|
|
333
312
|
|
|
334
|
-
|
|
335
|
-
|
|
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()
|
|
336
318
|
|
|
337
|
-
|
|
338
|
-
}
|
|
319
|
+
await generate({ input, config, logLevel, hooks })
|
|
339
320
|
|
|
340
|
-
|
|
321
|
+
clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
|
|
322
|
+
})
|
|
323
|
+
} else {
|
|
324
|
+
await generate({ input, config, logLevel, hooks })
|
|
341
325
|
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
await promiseManager.run('seq', promises)
|
|
326
|
+
}
|
|
345
327
|
|
|
346
|
-
await
|
|
328
|
+
await hooks.emit('kubb:lifecycle:end')
|
|
347
329
|
} catch (error) {
|
|
348
|
-
await
|
|
330
|
+
await hooks.emit('kubb:error', { error: toError(error) })
|
|
349
331
|
process.exit(1)
|
|
350
332
|
}
|
|
351
333
|
}
|
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, KubbHookEndContext, 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
|
-
const handler = (
|
|
28
|
-
if (id !== hookId) return
|
|
29
|
-
|
|
30
|
-
if (!success) {
|
|
31
|
-
reject(error ?? new Error(`Hook failed: ${command}`))
|
|
27
|
+
const handler = (ctx: KubbHookEndContext) => {
|
|
28
|
+
if (ctx.id !== hookId) return
|
|
29
|
+
hooks.off('kubb:hook:end', handler)
|
|
30
|
+
if (!ctx.success) {
|
|
31
|
+
reject(ctx.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', { message: `${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
|
+
}
|