@kubb/cli 5.0.0-beta.5 → 5.0.0-beta.50
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/LICENSE +17 -10
- package/README.md +171 -51
- package/dist/constants-BQ8LZB1P.js +23 -0
- package/dist/constants-BQ8LZB1P.js.map +1 -0
- package/dist/constants-MzEjK668.cjs +40 -0
- package/dist/constants-MzEjK668.cjs.map +1 -0
- package/dist/{define-Bdn8j5VM.cjs → define-C4AB3POr.cjs} +2 -2
- package/dist/{define-Bdn8j5VM.cjs.map → define-C4AB3POr.cjs.map} +1 -1
- package/dist/{define-Ctii4bel.js → define-C63T4jp6.js} +2 -2
- package/dist/{define-Ctii4bel.js.map → define-C63T4jp6.js.map} +1 -1
- package/dist/{errors-CjPmyZHy.js → errors-BsemQCMn.js} +2 -2
- package/dist/{errors-CjPmyZHy.js.map → errors-BsemQCMn.js.map} +1 -1
- package/dist/{errors-CLCjoSg0.cjs → errors-DykI11xo.cjs} +2 -2
- package/dist/{errors-CLCjoSg0.cjs.map → errors-DykI11xo.cjs.map} +1 -1
- package/dist/generate-CKnn3tQa.js +83 -0
- package/dist/generate-CKnn3tQa.js.map +1 -0
- package/dist/generate-Q6GMu4L8.cjs +82 -0
- package/dist/generate-Q6GMu4L8.cjs.map +1 -0
- package/dist/index.cjs +22 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +22 -15
- package/dist/index.js.map +1 -1
- package/dist/init-C5iCqBwA.cjs +53 -0
- package/dist/init-C5iCqBwA.cjs.map +1 -0
- package/dist/init-DyOF6CiQ.js +53 -0
- package/dist/init-DyOF6CiQ.js.map +1 -0
- package/dist/mcp-09FvxYWS.js +39 -0
- package/dist/mcp-09FvxYWS.js.map +1 -0
- package/dist/mcp-BN5HQYdK.cjs +39 -0
- package/dist/mcp-BN5HQYdK.cjs.map +1 -0
- package/dist/package-Bm6VUbtL.js +6 -0
- package/dist/package-Bm6VUbtL.js.map +1 -0
- package/dist/{package-BmYRU2hz.cjs → package-DOVOGEEY.cjs} +2 -2
- package/dist/package-DOVOGEEY.cjs.map +1 -0
- package/dist/run-BO7phoNN.js +51 -0
- package/dist/run-BO7phoNN.js.map +1 -0
- package/dist/{init-C5sZulT6.cjs → run-BtjzfTz3.cjs} +155 -85
- package/dist/run-BtjzfTz3.cjs.map +1 -0
- package/dist/run-BzaKz_nl.js +32 -0
- package/dist/run-BzaKz_nl.js.map +1 -0
- package/dist/{init-Cag3082g.js → run-C-omuksC.js} +147 -77
- package/dist/run-C-omuksC.js.map +1 -0
- package/dist/run-CkTpemme.cjs +52 -0
- package/dist/run-CkTpemme.cjs.map +1 -0
- package/dist/run-Cl4SrSob.cjs +33 -0
- package/dist/run-Cl4SrSob.cjs.map +1 -0
- package/dist/run-CrvmI4G2.cjs +1520 -0
- package/dist/run-CrvmI4G2.cjs.map +1 -0
- package/dist/run-DvZ5i2lT.js +1517 -0
- package/dist/run-DvZ5i2lT.js.map +1 -0
- package/dist/{shell-DLzN4fRo.js → shell-DsgkfUSW.js} +2 -2
- package/dist/{shell-DLzN4fRo.js.map → shell-DsgkfUSW.js.map} +1 -1
- package/dist/{shell-475fQKaX.cjs → shell-Lh-vLWwH.cjs} +2 -2
- package/dist/{shell-475fQKaX.cjs.map → shell-Lh-vLWwH.cjs.map} +1 -1
- package/dist/validate-5atkOC73.js +26 -0
- package/dist/validate-5atkOC73.js.map +1 -0
- package/dist/validate-C6KPFEex.cjs +26 -0
- package/dist/validate-C6KPFEex.cjs.map +1 -0
- package/package.json +14 -24
- package/src/commands/generate.ts +27 -13
- package/src/commands/init.ts +34 -3
- package/src/commands/mcp.ts +28 -4
- package/src/commands/validate.ts +6 -4
- package/src/constants.ts +1 -86
- package/src/index.ts +7 -6
- package/src/loggers/clackLogger.ts +137 -178
- package/src/loggers/plainLogger.ts +49 -102
- package/src/loggers/types.ts +6 -1
- package/src/loggers/utils.ts +141 -26
- package/src/runners/generate/run.ts +406 -0
- package/src/runners/generate/utils.ts +228 -0
- package/src/runners/init/run.ts +210 -0
- package/src/{utils/packageManager.ts → runners/init/utils.ts} +12 -2
- package/src/runners/mcp/run.ts +37 -0
- package/src/runners/validate/run.ts +63 -0
- package/dist/agent-BcUEl9yB.js +0 -56
- package/dist/agent-BcUEl9yB.js.map +0 -1
- package/dist/agent-CS45W0kL.cjs +0 -122
- package/dist/agent-CS45W0kL.cjs.map +0 -1
- package/dist/agent-CsMvXeqI.cjs +0 -58
- package/dist/agent-CsMvXeqI.cjs.map +0 -1
- package/dist/agent-IP0eLV3C.js +0 -118
- package/dist/agent-IP0eLV3C.js.map +0 -1
- package/dist/constants-B4iBDvCe.cjs +0 -148
- package/dist/constants-B4iBDvCe.cjs.map +0 -1
- package/dist/constants-DmPrkaz8.js +0 -95
- package/dist/constants-DmPrkaz8.js.map +0 -1
- package/dist/generate-7td_hs73.cjs +0 -65
- package/dist/generate-7td_hs73.cjs.map +0 -1
- package/dist/generate-BqeFFQGD.js +0 -66
- package/dist/generate-BqeFFQGD.js.map +0 -1
- package/dist/generate-CUd1dUY5.cjs +0 -1755
- package/dist/generate-CUd1dUY5.cjs.map +0 -1
- package/dist/generate-DVmGtwWe.js +0 -1752
- package/dist/generate-DVmGtwWe.js.map +0 -1
- package/dist/init-C5sZulT6.cjs.map +0 -1
- package/dist/init-Cag3082g.js.map +0 -1
- package/dist/init-CfAn19gC.js +0 -25
- package/dist/init-CfAn19gC.js.map +0 -1
- package/dist/init-CfsYoyGe.cjs +0 -25
- package/dist/init-CfsYoyGe.cjs.map +0 -1
- package/dist/mcp-BfORW-mY.cjs +0 -47
- package/dist/mcp-BfORW-mY.cjs.map +0 -1
- package/dist/mcp-BtOV6acy.js +0 -16
- package/dist/mcp-BtOV6acy.js.map +0 -1
- package/dist/mcp-CIbuLGMx.cjs +0 -16
- package/dist/mcp-CIbuLGMx.cjs.map +0 -1
- package/dist/mcp-DtQ5o0On.js +0 -46
- package/dist/mcp-DtQ5o0On.js.map +0 -1
- package/dist/package-BmYRU2hz.cjs.map +0 -1
- package/dist/package-DBU5ii-k.js +0 -6
- package/dist/package-DBU5ii-k.js.map +0 -1
- package/dist/telemetry-BU25EoI-.cjs +0 -282
- package/dist/telemetry-BU25EoI-.cjs.map +0 -1
- package/dist/telemetry-CaNU4-Bf.js +0 -245
- package/dist/telemetry-CaNU4-Bf.js.map +0 -1
- package/dist/validate-CQqM9siF.js +0 -25
- package/dist/validate-CQqM9siF.js.map +0 -1
- package/dist/validate-DBXLaLIn.cjs +0 -34
- package/dist/validate-DBXLaLIn.cjs.map +0 -1
- package/dist/validate-DI23zgmL.js +0 -33
- package/dist/validate-DI23zgmL.js.map +0 -1
- package/dist/validate-DwX4LzYq.cjs +0 -25
- package/dist/validate-DwX4LzYq.cjs.map +0 -1
- package/src/commands/agent/start.ts +0 -47
- package/src/commands/agent.ts +0 -8
- package/src/loggers/fileSystemLogger.ts +0 -138
- package/src/loggers/githubActionsLogger.ts +0 -379
- package/src/runners/agent.ts +0 -155
- package/src/runners/generate.ts +0 -333
- package/src/runners/init.ts +0 -296
- package/src/runners/mcp.ts +0 -51
- package/src/runners/validate.ts +0 -39
- package/src/types.ts +0 -11
- package/src/utils/Writables.ts +0 -17
- package/src/utils/executeHooks.ts +0 -45
- package/src/utils/flags.ts +0 -9
- package/src/utils/getConfig.ts +0 -10
- package/src/utils/getCosmiConfig.ts +0 -75
- package/src/utils/getSummary.ts +0 -68
- package/src/utils/runHook.ts +0 -91
- package/src/utils/telemetry.ts +0 -273
- package/src/utils/watcher.ts +0 -19
- /package/dist/{chunk-ByKO4r7w.cjs → chunk-Bx3C2hgW.cjs} +0 -0
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/src/commands/validate.ts
CHANGED
|
@@ -3,18 +3,20 @@ import { version } from '../../package.json'
|
|
|
3
3
|
|
|
4
4
|
export const command = defineCommand({
|
|
5
5
|
name: 'validate',
|
|
6
|
-
description:
|
|
6
|
+
description:
|
|
7
|
+
'Parse and validate an OpenAPI/Swagger file for structural correctness. Reports schema errors, missing required fields, and malformed references. Use this before running generate to catch spec issues early.',
|
|
8
|
+
examples: ['kubb validate --input ./openapi.yaml', 'kubb validate --input https://petstore3.swagger.io/api/v3/openapi.json'],
|
|
7
9
|
options: {
|
|
8
10
|
input: {
|
|
9
11
|
type: 'string',
|
|
10
|
-
description: 'Path to Swagger
|
|
12
|
+
description: 'Path or URL to the OpenAPI/Swagger file to validate',
|
|
11
13
|
short: 'i',
|
|
12
14
|
required: true,
|
|
13
15
|
},
|
|
14
16
|
},
|
|
15
17
|
async run({ values }) {
|
|
16
|
-
const {
|
|
18
|
+
const { run } = await import('../runners/validate/run.ts')
|
|
17
19
|
|
|
18
|
-
await
|
|
20
|
+
await run({ input: values.input, version })
|
|
19
21
|
},
|
|
20
22
|
})
|
package/src/constants.ts
CHANGED
|
@@ -1,104 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default filename for the Kubb configuration file.
|
|
3
|
-
*
|
|
4
|
-
* Used by the `init` command when scaffolding new projects and by the `agent` default config.
|
|
5
|
-
*/
|
|
6
|
-
export const KUBB_CONFIG_FILENAME = 'kubb.config.ts' as const
|
|
7
|
-
|
|
8
1
|
/**
|
|
9
2
|
* NPM registry endpoint used to check for @kubb/cli updates.
|
|
10
3
|
*/
|
|
11
4
|
export const KUBB_NPM_PACKAGE_URL = 'https://registry.npmjs.org/@kubb/cli/latest' as const
|
|
12
5
|
|
|
13
|
-
/**
|
|
14
|
-
* OpenTelemetry ingestion endpoint for anonymous usage telemetry.
|
|
15
|
-
*/
|
|
16
|
-
export const OTLP_ENDPOINT = 'https://otlp.kubb.dev' as const
|
|
17
|
-
|
|
18
6
|
/**
|
|
19
7
|
* Horizontal rule rendered above/below the plain-logger generation summary.
|
|
20
8
|
*/
|
|
21
9
|
export const SUMMARY_SEPARATOR = '─'.repeat(27)
|
|
22
10
|
|
|
23
|
-
/**
|
|
24
|
-
* Maximum number of █ characters in a plugin timing bar.
|
|
25
|
-
*/
|
|
26
|
-
export const SUMMARY_MAX_BAR_LENGTH = 10 as const
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Divides elapsed milliseconds into bar-length units (1 block per 100 ms).
|
|
30
|
-
*/
|
|
31
|
-
export const SUMMARY_TIME_SCALE_DIVISOR = 100 as const
|
|
32
|
-
|
|
33
11
|
/**
|
|
34
12
|
* Glob pattern for paths the file watcher ignores.
|
|
35
13
|
*/
|
|
36
14
|
export const WATCHER_IGNORED_PATHS = '**/{.git,node_modules}/**' as const
|
|
37
15
|
|
|
38
16
|
/**
|
|
39
|
-
* Flags that short-circuit execution (help/version)
|
|
17
|
+
* Flags that short-circuit execution (help/version), no telemetry notice is shown.
|
|
40
18
|
*/
|
|
41
19
|
export const QUIET_FLAGS = new Set(['--help', '-h', '--version', '-v'] as const)
|
|
42
|
-
|
|
43
|
-
export const agentDefaults = {
|
|
44
|
-
port: '3000',
|
|
45
|
-
host: 'localhost',
|
|
46
|
-
configFile: KUBB_CONFIG_FILENAME,
|
|
47
|
-
retryTimeout: '30000',
|
|
48
|
-
studioUrl: 'https://kubb.studio',
|
|
49
|
-
/**
|
|
50
|
-
* Relative path from the @kubb/agent package root to the server entry.
|
|
51
|
-
*/
|
|
52
|
-
serverEntryPath: '.output/server/index.mjs',
|
|
53
|
-
} as const
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Default values used during interactive `init` scaffolding.
|
|
57
|
-
*/
|
|
58
|
-
export const initDefaults = {
|
|
59
|
-
inputPath: './openapi.yaml',
|
|
60
|
-
outputPath: './src/gen',
|
|
61
|
-
plugins: ['plugin-ts'],
|
|
62
|
-
} as const
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Maps each plugin value to the default config snippet inserted by `init`.
|
|
66
|
-
* The `satisfies` constraint ensures all values remain plain strings while
|
|
67
|
-
* `as const` keeps the object deeply immutable.
|
|
68
|
-
*/
|
|
69
|
-
export const pluginDefaultConfigs = {
|
|
70
|
-
'plugin-ts': `pluginTs({
|
|
71
|
-
output: { path: 'models' },
|
|
72
|
-
})`,
|
|
73
|
-
'plugin-client': `pluginClient({
|
|
74
|
-
output: { path: 'clients' },
|
|
75
|
-
})`,
|
|
76
|
-
'plugin-react-query': `pluginReactQuery({
|
|
77
|
-
output: { path: 'hooks' },
|
|
78
|
-
})`,
|
|
79
|
-
'plugin-vue-query': `pluginVueQuery({
|
|
80
|
-
output: { path: 'hooks' },
|
|
81
|
-
})`,
|
|
82
|
-
'plugin-zod': `pluginZod({
|
|
83
|
-
output: { path: 'zod' },
|
|
84
|
-
})`,
|
|
85
|
-
'plugin-faker': `pluginFaker({
|
|
86
|
-
output: { path: 'mocks' },
|
|
87
|
-
})`,
|
|
88
|
-
'plugin-msw': `pluginMsw({
|
|
89
|
-
output: { path: 'msw' },
|
|
90
|
-
})`,
|
|
91
|
-
'plugin-cypress': `pluginCypress({
|
|
92
|
-
output: { path: 'cypress' },
|
|
93
|
-
})`,
|
|
94
|
-
'plugin-mcp': `pluginMcp({
|
|
95
|
-
output: { path: 'mcp' },
|
|
96
|
-
})`,
|
|
97
|
-
'plugin-redoc': `pluginRedoc({
|
|
98
|
-
output: { path: 'redoc' },
|
|
99
|
-
})`,
|
|
100
|
-
} as const satisfies Record<string, string>
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Color palette used by randomCliColor() for deterministic plugin name coloring.
|
|
104
|
-
*/
|
package/src/index.ts
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import { styleText } from 'node:util'
|
|
2
|
-
import { createCLI } from '@internals/utils'
|
|
2
|
+
import { createCLI, isFlag } from '@internals/utils'
|
|
3
|
+
import { Telemetry } from '@kubb/core'
|
|
3
4
|
import { version } from '../package.json'
|
|
4
5
|
import { QUIET_FLAGS } from './constants.ts'
|
|
5
|
-
import { isFlag } from './utils/flags.ts'
|
|
6
|
-
import { isTelemetryDisabled } from './utils/telemetry.ts'
|
|
7
6
|
|
|
8
7
|
const cli = createCLI()
|
|
9
8
|
|
|
10
9
|
function shouldShowTelemetryNotice(argv: Array<string>): boolean {
|
|
11
|
-
if (
|
|
10
|
+
if (Telemetry.isDisabled()) {
|
|
12
11
|
return false
|
|
13
12
|
}
|
|
13
|
+
|
|
14
14
|
// Skip when the user is just asking for help or version info
|
|
15
15
|
if (argv.some((arg) => isFlag(QUIET_FLAGS, arg))) {
|
|
16
16
|
return false
|
|
17
17
|
}
|
|
18
|
+
|
|
18
19
|
// Skip in non-interactive / scripting contexts
|
|
19
20
|
if (!process.stdout.isTTY) {
|
|
20
21
|
return false
|
|
21
22
|
}
|
|
23
|
+
|
|
22
24
|
return true
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -32,10 +34,9 @@ export async function run(argv: Array<string> = process.argv): Promise<void> {
|
|
|
32
34
|
const { command: generateCommand } = await import('./commands/generate.ts')
|
|
33
35
|
const { command: validateCommand } = await import('./commands/validate.ts')
|
|
34
36
|
const { command: mcpCommand } = await import('./commands/mcp.ts')
|
|
35
|
-
const { command: agentCommand } = await import('./commands/agent.ts')
|
|
36
37
|
const { command: initCommand } = await import('./commands/init.ts')
|
|
37
38
|
|
|
38
|
-
await cli.run([generateCommand, validateCommand, mcpCommand,
|
|
39
|
+
await cli.run([generateCommand, validateCommand, mcpCommand, initCommand], argv, {
|
|
39
40
|
programName: 'kubb',
|
|
40
41
|
defaultCommandName: 'generate',
|
|
41
42
|
version,
|
|
@@ -2,12 +2,9 @@ import { relative } from 'node:path'
|
|
|
2
2
|
import process from 'node:process'
|
|
3
3
|
import { styleText } from 'node:util'
|
|
4
4
|
import * as clack from '@clack/prompts'
|
|
5
|
-
import {
|
|
6
|
-
import { defineLogger, logLevel as logLevelMap } from '@kubb/core'
|
|
7
|
-
import {
|
|
8
|
-
import { runHook } from '../utils/runHook.ts'
|
|
9
|
-
import { ClackWritable } from '../utils/Writables.ts'
|
|
10
|
-
import { buildProgressLine, formatCommandWithArgs, formatMessage } from './utils.ts'
|
|
5
|
+
import { formatMsWithColor, getElapsedMs, getIntro, toCause } from '@internals/utils'
|
|
6
|
+
import { defineLogger, Diagnostics, type KubbHooks, logLevel as logLevelMap } from '@kubb/core'
|
|
7
|
+
import { buildProgressLine, createProgressCounters, formatCommandWithArgs, formatMessage, recordPluginResult, resetProgressCounters } from './utils.ts'
|
|
11
8
|
|
|
12
9
|
/**
|
|
13
10
|
* TTY logger with beautiful UI and progress indicators for local development.
|
|
@@ -17,34 +14,39 @@ export const clackLogger = defineLogger({
|
|
|
17
14
|
install(context, options) {
|
|
18
15
|
const logLevel = options?.logLevel ?? logLevelMap.info
|
|
19
16
|
const state = {
|
|
20
|
-
|
|
21
|
-
completedPlugins: 0,
|
|
22
|
-
failedPlugins: 0,
|
|
23
|
-
totalFiles: 0,
|
|
24
|
-
processedFiles: 0,
|
|
25
|
-
hrStart: process.hrtime(),
|
|
17
|
+
...createProgressCounters(),
|
|
26
18
|
spinner: clack.spinner(),
|
|
27
19
|
isSpinning: false,
|
|
20
|
+
runningPlugins: new Set<string>(),
|
|
28
21
|
activeProgress: new Map<string, { interval?: NodeJS.Timeout; progressBar: clack.ProgressResult }>(),
|
|
22
|
+
activeHookLogs: new Map<string, { taskLog: ReturnType<typeof clack.taskLog>; hrStart: [number, number] }>(),
|
|
29
23
|
}
|
|
30
24
|
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
// Clear every active progress bar's interval, stop it, and drop the map.
|
|
26
|
+
function stopActiveProgress() {
|
|
27
|
+
for (const [, active] of state.activeProgress) {
|
|
33
28
|
if (active.interval) {
|
|
34
29
|
clearInterval(active.interval)
|
|
35
30
|
}
|
|
36
31
|
active.progressBar?.stop()
|
|
37
32
|
}
|
|
33
|
+
state.activeProgress.clear()
|
|
34
|
+
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
state
|
|
43
|
-
state.processedFiles = 0
|
|
44
|
-
state.hrStart = process.hrtime()
|
|
36
|
+
function reset() {
|
|
37
|
+
stopActiveProgress()
|
|
38
|
+
|
|
39
|
+
resetProgressCounters(state)
|
|
45
40
|
state.spinner = clack.spinner()
|
|
46
41
|
state.isSpinning = false
|
|
47
|
-
state.
|
|
42
|
+
state.runningPlugins.clear()
|
|
43
|
+
state.activeHookLogs.clear()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Label for the shared plugin bar, listing the plugins currently generating.
|
|
47
|
+
function pluginProgressText(): string {
|
|
48
|
+
const running = [...state.runningPlugins].map((name) => styleText('bold', name))
|
|
49
|
+
return getMessage(running.length > 0 ? `Generating ${running.join(', ')}` : 'Generating plugins')
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
function showProgressStep() {
|
|
@@ -62,12 +64,25 @@ export const clackLogger = defineLogger({
|
|
|
62
64
|
return formatMessage(message, logLevel)
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
// Registers a handler that prints a fixed step message, skipped at silent level.
|
|
68
|
+
function onStep<E extends keyof KubbHooks>(event: E, message: string): void {
|
|
69
|
+
context.on(event, () => {
|
|
70
|
+
if (logLevel <= logLevelMap.silent) {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
clack.log.step(getMessage(message))
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
function startSpinner(text?: string) {
|
|
66
78
|
state.spinner.start(text)
|
|
67
79
|
state.isSpinning = true
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
function stopSpinner(text?: string) {
|
|
83
|
+
if (!state.isSpinning) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
71
86
|
state.spinner.stop(text)
|
|
72
87
|
state.isSpinning = false
|
|
73
88
|
}
|
|
@@ -77,13 +92,13 @@ export const clackLogger = defineLogger({
|
|
|
77
92
|
return
|
|
78
93
|
}
|
|
79
94
|
|
|
80
|
-
const text = getMessage([styleText('blue', 'ℹ'), message, styleText('dim', info)].join(' '))
|
|
95
|
+
const text = getMessage([styleText('blue', 'ℹ'), message, info ? styleText('dim', info) : undefined].filter(Boolean).join(' '))
|
|
81
96
|
|
|
82
97
|
if (state.isSpinning) {
|
|
83
98
|
state.spinner.message(text)
|
|
84
|
-
|
|
85
|
-
clack.log.info(text)
|
|
99
|
+
return
|
|
86
100
|
}
|
|
101
|
+
clack.log.info(text)
|
|
87
102
|
})
|
|
88
103
|
|
|
89
104
|
context.on('kubb:success', ({ message, info = '' }) => {
|
|
@@ -95,9 +110,9 @@ export const clackLogger = defineLogger({
|
|
|
95
110
|
|
|
96
111
|
if (state.isSpinning) {
|
|
97
112
|
stopSpinner(text)
|
|
98
|
-
|
|
99
|
-
clack.log.success(text)
|
|
113
|
+
return
|
|
100
114
|
}
|
|
115
|
+
clack.log.success(text)
|
|
101
116
|
})
|
|
102
117
|
|
|
103
118
|
context.on('kubb:warn', ({ message, info }) => {
|
|
@@ -119,12 +134,12 @@ export const clackLogger = defineLogger({
|
|
|
119
134
|
|
|
120
135
|
if (state.isSpinning) {
|
|
121
136
|
stopSpinner(getMessage(text))
|
|
122
|
-
|
|
123
|
-
clack.log.error(getMessage(text))
|
|
137
|
+
return
|
|
124
138
|
}
|
|
139
|
+
clack.log.error(getMessage(text))
|
|
125
140
|
|
|
126
|
-
// Show stack trace in
|
|
127
|
-
if (logLevel >= logLevelMap.
|
|
141
|
+
// Show stack trace in verbose mode (first 3 frames)
|
|
142
|
+
if (logLevel >= logLevelMap.verbose && error.stack) {
|
|
128
143
|
const frames = error.stack.split('\n').slice(1, 4)
|
|
129
144
|
for (const frame of frames) {
|
|
130
145
|
clack.log.message(getMessage(styleText('dim', frame.trim())))
|
|
@@ -141,14 +156,21 @@ export const clackLogger = defineLogger({
|
|
|
141
156
|
}
|
|
142
157
|
})
|
|
143
158
|
|
|
144
|
-
context.on('kubb:
|
|
145
|
-
|
|
159
|
+
context.on('kubb:diagnostic', ({ diagnostic }) => {
|
|
160
|
+
// Silent still surfaces errors so failures stay visible. It drops warnings and info.
|
|
161
|
+
if (logLevel <= logLevelMap.silent && diagnostic.severity !== 'error') {
|
|
146
162
|
return
|
|
147
163
|
}
|
|
148
164
|
|
|
149
|
-
|
|
165
|
+
stopSpinner()
|
|
166
|
+
|
|
167
|
+
// Stop any lingering progress UI so the multi-line block renders cleanly.
|
|
168
|
+
stopActiveProgress()
|
|
169
|
+
|
|
170
|
+
// The version-update notice keeps its own framed box instead of the diagnostic gutter.
|
|
171
|
+
if (Diagnostics.isUpdate(diagnostic)) {
|
|
150
172
|
clack.box(
|
|
151
|
-
`\`v${currentVersion}\` → \`v${latestVersion}\`
|
|
173
|
+
`\`v${diagnostic.currentVersion}\` → \`v${diagnostic.latestVersion}\`
|
|
152
174
|
Run \`npm install -g @kubb/cli\` to update`,
|
|
153
175
|
'Update available for `Kubb`',
|
|
154
176
|
{
|
|
@@ -160,14 +182,19 @@ Run \`npm install -g @kubb/cli\` to update`,
|
|
|
160
182
|
titleAlign: 'center',
|
|
161
183
|
},
|
|
162
184
|
)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
console.log('Run `npm install -g @kubb/cli` to update')
|
|
185
|
+
|
|
186
|
+
return
|
|
166
187
|
}
|
|
188
|
+
|
|
189
|
+
// Hand the severity glyph to clack as the gutter `symbol`, then let it draw the
|
|
190
|
+
// bar on each detail line via the default `secondarySymbol`. The headline and
|
|
191
|
+
// details carry their own colors, so clack only owns the gutter.
|
|
192
|
+
const { symbol, headline, details } = Diagnostics.format(diagnostic)
|
|
193
|
+
clack.log.message([headline, ...details], { symbol })
|
|
167
194
|
})
|
|
168
195
|
|
|
169
196
|
context.on('kubb:lifecycle:start', async ({ version }) => {
|
|
170
|
-
console.log(`\n${getIntro({ title: 'The
|
|
197
|
+
console.log(`\n${getIntro({ title: 'The meta framework for code generation', description: 'Ready to start', version, areEyesOpen: true })}\n`)
|
|
171
198
|
|
|
172
199
|
reset()
|
|
173
200
|
})
|
|
@@ -199,11 +226,17 @@ Run \`npm install -g @kubb/cli\` to update`,
|
|
|
199
226
|
// Initialize progress tracking for this generation
|
|
200
227
|
state.totalPlugins = config.plugins?.length ?? 0
|
|
201
228
|
|
|
229
|
+
if (logLevel <= logLevelMap.silent) {
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
|
|
202
233
|
const text = getMessage(['Generation started', config.name ? `for ${styleText('dim', config.name)}` : undefined].filter(Boolean).join(' '))
|
|
203
234
|
|
|
204
235
|
clack.intro(text)
|
|
205
236
|
})
|
|
206
237
|
|
|
238
|
+
// Plugins run concurrently, so they share a single progress bar. A bar per plugin
|
|
239
|
+
// would make clack render them side by side and pile up keypress listeners.
|
|
207
240
|
context.on('kubb:plugin:start', ({ plugin }) => {
|
|
208
241
|
if (logLevel <= logLevelMap.silent) {
|
|
209
242
|
return
|
|
@@ -211,50 +244,44 @@ Run \`npm install -g @kubb/cli\` to update`,
|
|
|
211
244
|
|
|
212
245
|
stopSpinner()
|
|
213
246
|
|
|
247
|
+
state.runningPlugins.add(plugin.name)
|
|
248
|
+
|
|
249
|
+
const active = state.activeProgress.get('plugins')
|
|
250
|
+
if (active) {
|
|
251
|
+
active.progressBar.advance(0, pluginProgressText())
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
214
255
|
const progressBar = clack.progress({
|
|
215
256
|
style: 'block',
|
|
216
|
-
max:
|
|
257
|
+
max: Math.max(state.totalPlugins, 1),
|
|
217
258
|
size: 30,
|
|
218
259
|
})
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
progressBar.advance()
|
|
224
|
-
}, 100)
|
|
225
|
-
|
|
226
|
-
state.activeProgress.set(plugin.name, { progressBar, interval })
|
|
260
|
+
progressBar.start(pluginProgressText())
|
|
261
|
+
// Catch up to plugins already finished before this bar opened.
|
|
262
|
+
progressBar.advance(state.completedPlugins + state.failedPlugins, pluginProgressText())
|
|
263
|
+
state.activeProgress.set('plugins', { progressBar })
|
|
227
264
|
})
|
|
228
265
|
|
|
229
|
-
context.on('kubb:plugin:end', ({ plugin,
|
|
266
|
+
context.on('kubb:plugin:end', ({ plugin, success }) => {
|
|
230
267
|
stopSpinner()
|
|
231
268
|
|
|
232
|
-
const active = state.activeProgress.get(
|
|
269
|
+
const active = state.activeProgress.get('plugins')
|
|
233
270
|
|
|
234
271
|
if (!active || logLevel === logLevelMap.silent) {
|
|
235
272
|
return
|
|
236
273
|
}
|
|
237
274
|
|
|
238
|
-
|
|
275
|
+
state.runningPlugins.delete(plugin.name)
|
|
276
|
+
recordPluginResult(state, success)
|
|
277
|
+
active.progressBar.advance(1, pluginProgressText())
|
|
239
278
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
state.
|
|
279
|
+
// Close the bar once nothing is generating, then print the progress step.
|
|
280
|
+
if (state.runningPlugins.size === 0) {
|
|
281
|
+
active.progressBar.stop(getMessage('Plugins generated'))
|
|
282
|
+
state.activeProgress.delete('plugins')
|
|
283
|
+
showProgressStep()
|
|
244
284
|
}
|
|
245
|
-
|
|
246
|
-
const durationStr = formatMsWithColor(duration)
|
|
247
|
-
const text = getMessage(
|
|
248
|
-
success
|
|
249
|
-
? `${styleText('bold', plugin.name)} completed in ${durationStr}`
|
|
250
|
-
: `${styleText('bold', plugin.name)} failed in ${styleText('red', formatMs(duration))}`,
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
active.progressBar.stop(text)
|
|
254
|
-
state.activeProgress.delete(plugin.name)
|
|
255
|
-
|
|
256
|
-
// Show progress step after each plugin
|
|
257
|
-
showProgressStep()
|
|
258
285
|
})
|
|
259
286
|
|
|
260
287
|
context.on('kubb:files:processing:start', ({ files }) => {
|
|
@@ -279,23 +306,20 @@ Run \`npm install -g @kubb/cli\` to update`,
|
|
|
279
306
|
state.activeProgress.set('files', { progressBar })
|
|
280
307
|
})
|
|
281
308
|
|
|
282
|
-
context.on('kubb:
|
|
309
|
+
context.on('kubb:files:processing:update', ({ files }) => {
|
|
283
310
|
if (logLevel <= logLevelMap.silent) {
|
|
284
311
|
return
|
|
285
312
|
}
|
|
286
313
|
|
|
287
314
|
stopSpinner()
|
|
288
315
|
|
|
289
|
-
state.processedFiles++
|
|
290
|
-
|
|
291
|
-
const text = `Writing ${relative(config.root, file.path)}`
|
|
292
316
|
const active = state.activeProgress.get('files')
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
317
|
+
for (const { file, config } of files) {
|
|
318
|
+
state.processedFiles++
|
|
319
|
+
if (active) {
|
|
320
|
+
active.progressBar.advance(undefined, `Writing ${relative(config.root, file.path)}`)
|
|
321
|
+
}
|
|
296
322
|
}
|
|
297
|
-
|
|
298
|
-
active.progressBar.advance(undefined, text)
|
|
299
323
|
})
|
|
300
324
|
context.on('kubb:files:processing:end', () => {
|
|
301
325
|
if (logLevel <= logLevelMap.silent) {
|
|
@@ -319,135 +343,70 @@ Run \`npm install -g @kubb/cli\` to update`,
|
|
|
319
343
|
})
|
|
320
344
|
|
|
321
345
|
context.on('kubb:generation:end', ({ config }) => {
|
|
346
|
+
stopSpinner()
|
|
347
|
+
|
|
322
348
|
const text = getMessage(config.name ? `Generation completed for ${styleText('dim', config.name)}` : 'Generation completed')
|
|
323
349
|
|
|
324
350
|
clack.outro(text)
|
|
325
351
|
})
|
|
326
352
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
353
|
+
onStep('kubb:format:start', 'Formatting')
|
|
354
|
+
onStep('kubb:lint:start', 'Linting')
|
|
355
|
+
onStep('kubb:hooks:start', 'Running hooks')
|
|
331
356
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
clack.intro(text)
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
context.on('kubb:format:end', () => {
|
|
338
|
-
if (logLevel <= logLevelMap.silent) {
|
|
357
|
+
context.on('kubb:hook:start', ({ id, command, args }) => {
|
|
358
|
+
if (logLevel <= logLevelMap.silent || !id) {
|
|
339
359
|
return
|
|
340
360
|
}
|
|
341
361
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
clack.outro(text)
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
context.on('kubb:lint:start', () => {
|
|
348
|
-
if (logLevel <= logLevelMap.silent) {
|
|
349
|
-
return
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const text = getMessage('Lint started')
|
|
353
|
-
|
|
354
|
-
clack.intro(text)
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
context.on('kubb:lint:end', () => {
|
|
358
|
-
if (logLevel <= logLevelMap.silent) {
|
|
359
|
-
return
|
|
360
|
-
}
|
|
362
|
+
stopSpinner()
|
|
361
363
|
|
|
362
|
-
const
|
|
364
|
+
const commandWithArgs = formatCommandWithArgs(command, args)
|
|
365
|
+
const title = getMessage(`Running ${styleText('dim', commandWithArgs)}`)
|
|
366
|
+
const taskLog = clack.taskLog({ title })
|
|
363
367
|
|
|
364
|
-
|
|
368
|
+
state.activeHookLogs.set(id, { taskLog, hrStart: process.hrtime() })
|
|
365
369
|
})
|
|
366
370
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
371
|
+
// Registered only when not silent, so its presence is what tells the runner to stream
|
|
372
|
+
// (`kubb:hook:line` listenerCount). At silent level the listener is absent, so no streaming happens.
|
|
373
|
+
if (logLevel > logLevelMap.silent) {
|
|
374
|
+
context.on('kubb:hook:line', ({ id, line }) => {
|
|
375
|
+
const active = state.activeHookLogs.get(id)
|
|
376
|
+
active?.taskLog.message(styleText('dim', line))
|
|
377
|
+
})
|
|
378
|
+
}
|
|
370
379
|
|
|
371
|
-
|
|
380
|
+
context.on('kubb:hook:end', ({ id, command, args, success, error, stdout, stderr }) => {
|
|
372
381
|
if (!id) {
|
|
373
382
|
return
|
|
374
383
|
}
|
|
375
384
|
|
|
376
385
|
if (logLevel <= logLevelMap.silent) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
context,
|
|
383
|
-
sink: {
|
|
384
|
-
onStderr: (s) => console.error(s),
|
|
385
|
-
onStdout: (s) => console.log(s),
|
|
386
|
-
},
|
|
387
|
-
})
|
|
386
|
+
// Even when silent, surface a failed hook's captured output.
|
|
387
|
+
if (!success) {
|
|
388
|
+
if (stdout) console.log(stdout)
|
|
389
|
+
if (stderr) console.error(stderr)
|
|
390
|
+
}
|
|
388
391
|
return
|
|
389
392
|
}
|
|
390
393
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const logger = clack.taskLog({
|
|
394
|
-
title: getMessage(['Executing hook', logLevel >= logLevelMap.info ? styleText('dim', commandWithArgs) : undefined].filter(Boolean).join(' ')),
|
|
395
|
-
})
|
|
396
|
-
|
|
397
|
-
const writable = new ClackWritable(logger)
|
|
398
|
-
|
|
399
|
-
await runHook({
|
|
400
|
-
id,
|
|
401
|
-
command,
|
|
402
|
-
args,
|
|
403
|
-
commandWithArgs,
|
|
404
|
-
context,
|
|
405
|
-
stream: true,
|
|
406
|
-
sink: {
|
|
407
|
-
onLine: (line) => writable.write(line),
|
|
408
|
-
onStderr: (s) => logger.error(s),
|
|
409
|
-
onStdout: (s) => logger.message(s),
|
|
410
|
-
},
|
|
411
|
-
})
|
|
412
|
-
})
|
|
413
|
-
|
|
414
|
-
context.on('kubb:hook:end', ({ command, args }) => {
|
|
415
|
-
if (logLevel <= logLevelMap.silent) {
|
|
394
|
+
const active = state.activeHookLogs.get(id)
|
|
395
|
+
if (!active) {
|
|
416
396
|
return
|
|
417
397
|
}
|
|
398
|
+
state.activeHookLogs.delete(id)
|
|
418
399
|
|
|
419
400
|
const commandWithArgs = formatCommandWithArgs(command, args)
|
|
420
|
-
const
|
|
401
|
+
const duration = formatMsWithColor(getElapsedMs(active.hrStart))
|
|
421
402
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
config,
|
|
430
|
-
status,
|
|
431
|
-
hrStart,
|
|
432
|
-
pluginTimings: logLevel >= logLevelMap.verbose ? pluginTimings : undefined,
|
|
433
|
-
})
|
|
434
|
-
const title = config.name || ''
|
|
435
|
-
|
|
436
|
-
summary.unshift('\n')
|
|
437
|
-
summary.push('\n')
|
|
438
|
-
|
|
439
|
-
const borderColor = status === 'success' ? 'green' : 'red'
|
|
440
|
-
try {
|
|
441
|
-
clack.box(summary.join('\n'), getMessage(title), {
|
|
442
|
-
width: 'auto',
|
|
443
|
-
formatBorder: (s: string) => styleText(borderColor, s),
|
|
444
|
-
rounded: true,
|
|
445
|
-
withGuide: false,
|
|
446
|
-
contentAlign: 'left',
|
|
447
|
-
titleAlign: 'center',
|
|
448
|
-
})
|
|
449
|
-
} catch {
|
|
450
|
-
console.log(summary.join('\n'))
|
|
403
|
+
if (success) {
|
|
404
|
+
active.taskLog.success(getMessage(`${styleText('dim', commandWithArgs)} completed in ${duration}`))
|
|
405
|
+
} else {
|
|
406
|
+
// The hook's output already reached the taskLog live via `kubb:hook:line`, so `showLog`
|
|
407
|
+
// replays it here; `kubb:hook:end` carries no captured output on the streaming path.
|
|
408
|
+
const reason = error?.message ? ` (${error.message})` : ''
|
|
409
|
+
active.taskLog.error(getMessage(`${styleText('dim', commandWithArgs)} failed${reason}`), { showLog: true })
|
|
451
410
|
}
|
|
452
411
|
})
|
|
453
412
|
|