@kubb/cli 5.0.0-alpha.7 → 5.0.0-alpha.71
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-CouS-H2S.js +112 -0
- package/dist/agent-CouS-H2S.js.map +1 -0
- package/dist/{agent-Cc-0-A8t.cjs → agent-D5OIwM0z.cjs} +5 -5
- package/dist/agent-D5OIwM0z.cjs.map +1 -0
- package/dist/{agent-CjXjyuPe.js → agent-DypvJBwO.js} +5 -5
- package/dist/agent-DypvJBwO.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-BP0WpC20.cjs → generate-BW0J1wvg.cjs} +3 -3
- package/dist/generate-BW0J1wvg.cjs.map +1 -0
- package/dist/{generate-yOyKmo19.cjs → generate-CLYhWYY1.cjs} +553 -251
- package/dist/generate-CLYhWYY1.cjs.map +1 -0
- package/dist/{generate-BX6LmxKo.js → generate-Cy11Ra5n.js} +3 -3
- package/dist/generate-Cy11Ra5n.js.map +1 -0
- package/dist/{generate-BYqhZfbq.js → generate-DfhKa1mG.js} +546 -244
- package/dist/generate-DfhKa1mG.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-DkwG4Gbs.cjs → init-C_EgqqO4.cjs} +4 -4
- package/dist/init-C_EgqqO4.cjs.map +1 -0
- package/dist/{init-BqkRvRTM.js → init-CaQZcAvy.js} +4 -4
- package/dist/init-CaQZcAvy.js.map +1 -0
- package/dist/{init-hmolV6B4.cjs → init-CmrUd26x.cjs} +31 -21
- package/dist/init-CmrUd26x.cjs.map +1 -0
- package/dist/{init-C-InrmSY.js → init-DEC1gCsk.js} +27 -17
- package/dist/init-DEC1gCsk.js.map +1 -0
- package/dist/{mcp-Cqn_PHUg.js → mcp-7rBVhoQz.js} +4 -4
- package/dist/{mcp-Cqn_PHUg.js.map → mcp-7rBVhoQz.js.map} +1 -1
- package/dist/{mcp-BnEafD5r.cjs → mcp-CmXrXzDF.cjs} +4 -4
- package/dist/{mcp-BnEafD5r.cjs.map → mcp-CmXrXzDF.cjs.map} +1 -1
- package/dist/{mcp-D2SHEg_d.js → mcp-nlFo36z0.js} +4 -5
- package/dist/mcp-nlFo36z0.js.map +1 -0
- package/dist/{mcp-ChHFPRzD.cjs → mcp-vi8t2Rkb.cjs} +5 -6
- package/dist/mcp-vi8t2Rkb.cjs.map +1 -0
- package/dist/package-BD90dOVc.js +6 -0
- package/dist/package-BD90dOVc.js.map +1 -0
- package/dist/{package-DZb6I8FQ.cjs → package-DsKVMEXO.cjs} +2 -2
- package/dist/package-DsKVMEXO.cjs.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-9FoHgtvi.cjs → validate-BZKRjT0Y.cjs} +4 -4
- package/dist/validate-BZKRjT0Y.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-Bgqwmhir.js → validate-RG85Fqjh.js} +4 -4
- package/dist/validate-RG85Fqjh.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 +90 -100
- package/src/runners/init.ts +9 -9
- package/src/runners/mcp.ts +17 -4
- 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 +21 -12
- package/src/utils/getSummary.ts +1 -1
- package/src/utils/runHook.ts +28 -10
- 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-Cc-0-A8t.cjs.map +0 -1
- package/dist/agent-CjXjyuPe.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-BP0WpC20.cjs.map +0 -1
- package/dist/generate-BX6LmxKo.js.map +0 -1
- package/dist/generate-BYqhZfbq.js.map +0 -1
- package/dist/generate-yOyKmo19.cjs.map +0 -1
- package/dist/init-BqkRvRTM.js.map +0 -1
- package/dist/init-C-InrmSY.js.map +0 -1
- package/dist/init-DkwG4Gbs.cjs.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-DLd3fZNd.js +0 -6
- package/dist/package-DLd3fZNd.js.map +0 -1
- package/dist/package-DZb6I8FQ.cjs.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-9FoHgtvi.cjs.map +0 -1
- package/dist/validate-Bbrn3Q-A.cjs.map +0 -1
- package/dist/validate-Bgqwmhir.js.map +0 -1
- package/dist/validate-l8vLmwKA.js.map +0 -1
- package/src/utils/getIntro.ts +0 -1
- package/src/utils/jiti.ts +0 -9
package/src/runners/generate.ts
CHANGED
|
@@ -4,25 +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
|
-
safeBuild,
|
|
20
|
-
setup,
|
|
21
|
-
} 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'
|
|
22
9
|
import { version } from '../../package.json'
|
|
23
10
|
import { KUBB_NPM_PACKAGE_URL } from '../constants.ts'
|
|
24
11
|
import { setupLogger } from '../loggers/utils.ts'
|
|
25
12
|
import { executeHooks } from '../utils/executeHooks.ts'
|
|
13
|
+
import { getConfigs } from '../utils/getConfig.ts'
|
|
26
14
|
import { getCosmiConfig } from '../utils/getCosmiConfig.ts'
|
|
27
15
|
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
28
16
|
import { startWatcher } from '../utils/watcher.ts'
|
|
@@ -30,7 +18,7 @@ import { startWatcher } from '../utils/watcher.ts'
|
|
|
30
18
|
type GenerateProps = {
|
|
31
19
|
input?: string
|
|
32
20
|
config: Config
|
|
33
|
-
|
|
21
|
+
hooks: AsyncEventEmitter<KubbHooks>
|
|
34
22
|
logLevel: number
|
|
35
23
|
}
|
|
36
24
|
|
|
@@ -38,7 +26,7 @@ type ToolMap = typeof formatters | typeof linters
|
|
|
38
26
|
|
|
39
27
|
type RunToolPassOptions = {
|
|
40
28
|
toolValue: string
|
|
41
|
-
detect: () => Promise<string |
|
|
29
|
+
detect: () => Promise<string | null>
|
|
42
30
|
toolMap: ToolMap
|
|
43
31
|
/** Short noun used in "Auto-detected <toolLabel>:" message, e.g. "formatter" or "linter". */
|
|
44
32
|
toolLabel: string
|
|
@@ -48,7 +36,7 @@ type RunToolPassOptions = {
|
|
|
48
36
|
configName: string | undefined
|
|
49
37
|
outputPath: string
|
|
50
38
|
logLevel: number
|
|
51
|
-
|
|
39
|
+
hooks: AsyncEventEmitter<KubbHooks>
|
|
52
40
|
onStart: () => Promise<void>
|
|
53
41
|
onEnd: () => Promise<void>
|
|
54
42
|
}
|
|
@@ -63,7 +51,7 @@ async function runToolPass({
|
|
|
63
51
|
configName,
|
|
64
52
|
outputPath,
|
|
65
53
|
logLevel,
|
|
66
|
-
|
|
54
|
+
hooks,
|
|
67
55
|
onStart,
|
|
68
56
|
onEnd,
|
|
69
57
|
}: RunToolPassOptions) {
|
|
@@ -73,13 +61,15 @@ async function runToolPass({
|
|
|
73
61
|
if (resolvedTool === 'auto') {
|
|
74
62
|
const detected = await detect()
|
|
75
63
|
if (!detected) {
|
|
76
|
-
await
|
|
64
|
+
await hooks.emit('kubb:warn', { message: noToolMessage })
|
|
77
65
|
} else {
|
|
78
66
|
resolvedTool = detected
|
|
79
|
-
await
|
|
67
|
+
await hooks.emit('kubb:info', { message: `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}` })
|
|
80
68
|
}
|
|
81
69
|
}
|
|
82
70
|
|
|
71
|
+
let toolError: Error | undefined
|
|
72
|
+
|
|
83
73
|
if (resolvedTool && resolvedTool !== 'auto' && resolvedTool in toolMap) {
|
|
84
74
|
const toolConfig = toolMap[resolvedTool as keyof ToolMap]
|
|
85
75
|
|
|
@@ -87,33 +77,32 @@ async function runToolPass({
|
|
|
87
77
|
const hookId = createHash('sha256').update([configName, resolvedTool].filter(Boolean).join('-')).digest('hex')
|
|
88
78
|
|
|
89
79
|
// Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
|
|
90
|
-
// 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.
|
|
91
81
|
const hookEndPromise = new Promise<void>((resolve, reject) => {
|
|
92
|
-
const handler = (
|
|
93
|
-
if (id !== hookId) return
|
|
94
|
-
|
|
95
|
-
if (!success) {
|
|
96
|
-
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}`))
|
|
97
87
|
return
|
|
98
88
|
}
|
|
99
|
-
|
|
100
|
-
.emit(
|
|
101
|
-
|
|
102
|
-
[
|
|
89
|
+
hooks
|
|
90
|
+
.emit('kubb:success', {
|
|
91
|
+
message: [
|
|
103
92
|
`${successPrefix} with ${styleText('dim', resolvedTool)}`,
|
|
104
93
|
logLevel >= logLevelMap.info ? `on ${styleText('dim', outputPath)}` : undefined,
|
|
105
94
|
'successfully',
|
|
106
95
|
]
|
|
107
96
|
.filter(Boolean)
|
|
108
97
|
.join(' '),
|
|
109
|
-
)
|
|
98
|
+
})
|
|
110
99
|
.then(resolve)
|
|
111
100
|
.catch(reject)
|
|
112
101
|
}
|
|
113
|
-
|
|
102
|
+
hooks.on('kubb:hook:end', handler)
|
|
114
103
|
})
|
|
115
104
|
|
|
116
|
-
await
|
|
105
|
+
await hooks.emit('kubb:hook:start', {
|
|
117
106
|
id: hookId,
|
|
118
107
|
command: toolConfig.command,
|
|
119
108
|
args: toolConfig.args(outputPath),
|
|
@@ -121,59 +110,52 @@ async function runToolPass({
|
|
|
121
110
|
|
|
122
111
|
await hookEndPromise
|
|
123
112
|
} catch (caughtError) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
127
120
|
}
|
|
128
121
|
}
|
|
129
122
|
|
|
130
123
|
await onEnd()
|
|
124
|
+
|
|
125
|
+
if (toolError) {
|
|
126
|
+
throw toolError
|
|
127
|
+
}
|
|
131
128
|
}
|
|
132
129
|
|
|
133
|
-
async function generate(
|
|
134
|
-
const
|
|
130
|
+
async function generate(options: GenerateProps): Promise<void> {
|
|
131
|
+
const { input, hooks, logLevel } = options
|
|
132
|
+
|
|
135
133
|
const hrStart = process.hrtime()
|
|
134
|
+
const inputPath = input ?? ('path' in options.config.input ? options.config.input.path : undefined)
|
|
136
135
|
|
|
137
136
|
const config: Config = {
|
|
138
|
-
...
|
|
139
|
-
root: userConfig.root || process.cwd(),
|
|
137
|
+
...options.config,
|
|
140
138
|
input: inputPath
|
|
141
139
|
? {
|
|
142
|
-
...
|
|
140
|
+
...options.config.input,
|
|
143
141
|
path: inputPath,
|
|
144
142
|
}
|
|
145
|
-
:
|
|
146
|
-
output
|
|
147
|
-
|
|
148
|
-
barrelType: 'named',
|
|
149
|
-
extension: {
|
|
150
|
-
'.ts': '.ts',
|
|
151
|
-
},
|
|
152
|
-
format: 'prettier',
|
|
153
|
-
...userConfig.output,
|
|
154
|
-
},
|
|
155
|
-
}
|
|
143
|
+
: options.config.input,
|
|
144
|
+
...options.config.output,
|
|
145
|
+
} satisfies Config
|
|
156
146
|
|
|
157
|
-
|
|
147
|
+
const kubb = createKubb(config, { hooks })
|
|
148
|
+
await kubb.setup()
|
|
158
149
|
|
|
159
|
-
await
|
|
150
|
+
await hooks.emit('kubb:generation:start', { config })
|
|
160
151
|
|
|
161
|
-
|
|
162
|
-
config,
|
|
163
|
-
events,
|
|
164
|
-
})
|
|
152
|
+
await hooks.emit('kubb:info', { message: config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', info: inputPath })
|
|
165
153
|
|
|
166
|
-
await
|
|
154
|
+
await hooks.emit('kubb:info', { message: config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', info: inputPath })
|
|
167
155
|
|
|
168
|
-
const { files, failedPlugins, pluginTimings, error } = await safeBuild(
|
|
169
|
-
{
|
|
170
|
-
config,
|
|
171
|
-
events,
|
|
172
|
-
},
|
|
173
|
-
{ driver, fabric, events, sources },
|
|
174
|
-
)
|
|
156
|
+
const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild()
|
|
175
157
|
|
|
176
|
-
await
|
|
158
|
+
await hooks.emit('kubb:info', { message: 'Load summary' })
|
|
177
159
|
|
|
178
160
|
// Handle build failures (either from failed plugins or general errors)
|
|
179
161
|
|
|
@@ -188,12 +170,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
188
170
|
].filter(Boolean)
|
|
189
171
|
|
|
190
172
|
for (const err of allErrors) {
|
|
191
|
-
await
|
|
173
|
+
await hooks.emit('kubb:error', { error: err })
|
|
192
174
|
}
|
|
193
175
|
|
|
194
|
-
await
|
|
176
|
+
await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
|
|
195
177
|
|
|
196
|
-
await
|
|
178
|
+
await hooks.emit('kubb:generation:summary', {
|
|
179
|
+
config,
|
|
197
180
|
failedPlugins,
|
|
198
181
|
filesCreated: files.length,
|
|
199
182
|
status: 'failed',
|
|
@@ -205,7 +188,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
205
188
|
buildTelemetryEvent({
|
|
206
189
|
command: 'generate',
|
|
207
190
|
kubbVersion: version,
|
|
208
|
-
plugins: driver.plugins.
|
|
191
|
+
plugins: Array.from(driver.plugins.values(), (p) => ({
|
|
192
|
+
name: p.name,
|
|
193
|
+
options: p.options as Record<string, unknown>,
|
|
194
|
+
})),
|
|
209
195
|
hrStart,
|
|
210
196
|
filesCreated: files.length,
|
|
211
197
|
status: 'failed',
|
|
@@ -215,8 +201,8 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
215
201
|
process.exit(1)
|
|
216
202
|
}
|
|
217
203
|
|
|
218
|
-
await
|
|
219
|
-
await
|
|
204
|
+
await hooks.emit('kubb:success', { message: 'Generation successfully', info: inputPath })
|
|
205
|
+
await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
|
|
220
206
|
|
|
221
207
|
const outputPath = path.resolve(config.root, config.output.path)
|
|
222
208
|
|
|
@@ -227,13 +213,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
227
213
|
toolMap: formatters,
|
|
228
214
|
toolLabel: 'formatter',
|
|
229
215
|
successPrefix: 'Formatting',
|
|
230
|
-
noToolMessage: 'No formatter found (
|
|
216
|
+
noToolMessage: 'No formatter found (oxfmt, biome, or prettier). Skipping formatting.',
|
|
231
217
|
configName: config.name,
|
|
232
218
|
outputPath,
|
|
233
219
|
logLevel,
|
|
234
|
-
|
|
235
|
-
onStart: () =>
|
|
236
|
-
onEnd: () =>
|
|
220
|
+
hooks,
|
|
221
|
+
onStart: () => hooks.emit('kubb:format:start'),
|
|
222
|
+
onEnd: () => hooks.emit('kubb:format:end'),
|
|
237
223
|
})
|
|
238
224
|
}
|
|
239
225
|
|
|
@@ -244,25 +230,26 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
244
230
|
toolMap: linters,
|
|
245
231
|
toolLabel: 'linter',
|
|
246
232
|
successPrefix: 'Linting',
|
|
247
|
-
noToolMessage: 'No linter found (
|
|
233
|
+
noToolMessage: 'No linter found (oxlint, biome, or eslint). Skipping linting.',
|
|
248
234
|
configName: config.name,
|
|
249
235
|
outputPath,
|
|
250
236
|
logLevel,
|
|
251
|
-
|
|
252
|
-
onStart: () =>
|
|
253
|
-
onEnd: () =>
|
|
237
|
+
hooks,
|
|
238
|
+
onStart: () => hooks.emit('kubb:lint:start'),
|
|
239
|
+
onEnd: () => hooks.emit('kubb:lint:end'),
|
|
254
240
|
})
|
|
255
241
|
}
|
|
256
242
|
|
|
257
243
|
if (config.hooks) {
|
|
258
|
-
await
|
|
259
|
-
await executeHooks({
|
|
244
|
+
await hooks.emit('kubb:hooks:start')
|
|
245
|
+
await executeHooks({ configHooks: config.hooks, hooks })
|
|
260
246
|
|
|
261
|
-
await
|
|
247
|
+
await hooks.emit('kubb:hooks:end')
|
|
262
248
|
}
|
|
263
249
|
|
|
264
250
|
// Only reached when there are no failures (process.exit(1) is called above otherwise)
|
|
265
|
-
await
|
|
251
|
+
await hooks.emit('kubb:generation:summary', {
|
|
252
|
+
config,
|
|
266
253
|
failedPlugins,
|
|
267
254
|
filesCreated: files.length,
|
|
268
255
|
status: 'success',
|
|
@@ -273,7 +260,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
|
|
|
273
260
|
const telemetryEvent = buildTelemetryEvent({
|
|
274
261
|
command: 'generate',
|
|
275
262
|
kubbVersion: version,
|
|
276
|
-
plugins: driver.plugins.
|
|
263
|
+
plugins: Array.from(driver.plugins.values(), (p) => ({
|
|
264
|
+
name: p.name,
|
|
265
|
+
options: p.options as Record<string, unknown>,
|
|
266
|
+
})),
|
|
277
267
|
hrStart,
|
|
278
268
|
filesCreated: files.length,
|
|
279
269
|
status: 'success',
|
|
@@ -291,9 +281,9 @@ type GenerateCommandOptions = {
|
|
|
291
281
|
|
|
292
282
|
export async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }: GenerateCommandOptions): Promise<void> {
|
|
293
283
|
const logLevel = logLevelMap[logLevelKey as keyof typeof logLevelMap] ?? logLevelMap.info
|
|
294
|
-
const
|
|
284
|
+
const hooks = new AsyncEventEmitterClass<KubbHooks>()
|
|
295
285
|
|
|
296
|
-
await setupLogger(
|
|
286
|
+
await setupLogger(hooks, { logLevel })
|
|
297
287
|
|
|
298
288
|
await executeIfOnline(async () => {
|
|
299
289
|
try {
|
|
@@ -302,7 +292,7 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
|
|
|
302
292
|
const latestVersion = data.version
|
|
303
293
|
|
|
304
294
|
if (latestVersion && version < latestVersion) {
|
|
305
|
-
await
|
|
295
|
+
await hooks.emit('kubb:version:new', { currentVersion: version, latestVersion })
|
|
306
296
|
}
|
|
307
297
|
} catch {
|
|
308
298
|
// Ignore network errors for version check
|
|
@@ -313,31 +303,31 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
|
|
|
313
303
|
const result = await getCosmiConfig('kubb', configPath)
|
|
314
304
|
const configs = await getConfigs(result.config, { input } as CLIOptions)
|
|
315
305
|
|
|
316
|
-
await
|
|
317
|
-
await
|
|
318
|
-
await
|
|
319
|
-
await
|
|
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 })
|
|
320
310
|
|
|
321
|
-
await
|
|
311
|
+
await hooks.emit('kubb:lifecycle:start', { version })
|
|
322
312
|
|
|
323
313
|
for (const config of configs) {
|
|
324
314
|
if (isInputPath(config) && watch) {
|
|
325
315
|
await startWatcher([input || config.input.path], async (paths) => {
|
|
326
316
|
// remove to avoid duplicate listeners after each change
|
|
327
|
-
|
|
317
|
+
hooks.removeAll()
|
|
328
318
|
|
|
329
|
-
await generate({ input, config, logLevel,
|
|
319
|
+
await generate({ input, config, logLevel, hooks })
|
|
330
320
|
|
|
331
321
|
clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
|
|
332
322
|
})
|
|
333
323
|
} else {
|
|
334
|
-
await generate({ input, config, logLevel,
|
|
324
|
+
await generate({ input, config, logLevel, hooks })
|
|
335
325
|
}
|
|
336
326
|
}
|
|
337
327
|
|
|
338
|
-
await
|
|
328
|
+
await hooks.emit('kubb:lifecycle:end')
|
|
339
329
|
} catch (error) {
|
|
340
|
-
await
|
|
330
|
+
await hooks.emit('kubb:error', { error: toError(error) })
|
|
341
331
|
process.exit(1)
|
|
342
332
|
}
|
|
343
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
|
@@ -2,7 +2,6 @@ import process from 'node:process'
|
|
|
2
2
|
import { styleText } from 'node:util'
|
|
3
3
|
import { getErrorMessage } from '@internals/utils'
|
|
4
4
|
import type * as McpModule from '@kubb/mcp'
|
|
5
|
-
import { jiti } from '../utils/jiti.ts'
|
|
6
5
|
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
7
6
|
|
|
8
7
|
type McpOptions = {
|
|
@@ -12,7 +11,7 @@ type McpOptions = {
|
|
|
12
11
|
export async function runMcp({ version }: McpOptions): Promise<void> {
|
|
13
12
|
let mod: typeof McpModule
|
|
14
13
|
try {
|
|
15
|
-
mod = (await
|
|
14
|
+
mod = (await import('@kubb/mcp')) as typeof McpModule
|
|
16
15
|
} catch (_e) {
|
|
17
16
|
console.error(`Import of '@kubb/mcp' is required to start the MCP server`)
|
|
18
17
|
process.exit(1)
|
|
@@ -24,9 +23,23 @@ export async function runMcp({ version }: McpOptions): Promise<void> {
|
|
|
24
23
|
console.log('⏳ Starting MCP server...')
|
|
25
24
|
console.warn(styleText('yellow', 'This feature is still under development — use with caution'))
|
|
26
25
|
run()
|
|
27
|
-
await sendTelemetry(
|
|
26
|
+
await sendTelemetry(
|
|
27
|
+
buildTelemetryEvent({
|
|
28
|
+
command: 'mcp',
|
|
29
|
+
kubbVersion: version,
|
|
30
|
+
hrStart,
|
|
31
|
+
status: 'success',
|
|
32
|
+
}),
|
|
33
|
+
)
|
|
28
34
|
} catch (error) {
|
|
29
|
-
await sendTelemetry(
|
|
35
|
+
await sendTelemetry(
|
|
36
|
+
buildTelemetryEvent({
|
|
37
|
+
command: 'mcp',
|
|
38
|
+
kubbVersion: version,
|
|
39
|
+
hrStart,
|
|
40
|
+
status: 'failed',
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
30
43
|
console.error(getErrorMessage(error))
|
|
31
44
|
}
|
|
32
45
|
}
|
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
|
+
}
|