UNPKG

@kubb/cli

Version:

Command-line interface for Kubb, enabling easy generation of TypeScript, React-Query, Zod, and other code from OpenAPI specifications.

304 lines (231 loc) 7.51 kB
import { relative } from 'node:path' import { defineLogger, LogLevel } from '@kubb/core' import { formatMs } from '@kubb/core/utils' import { type NonZeroExitError, x } from 'tinyexec' import { getSummary } from '../utils/getSummary.ts' /** * Plain console adapter for non-TTY environments * Simple console.log output with indentation */ export const plainLogger = defineLogger({ name: 'plain', install(context, options) { const logLevel = options?.logLevel || 3 function getMessage(message: string): string { if (logLevel >= LogLevel.verbose) { const timestamp = new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', }) return [`[${timestamp}]`, message].join(' ') } return message } context.on('info', (message, info) => { if (logLevel <= LogLevel.silent) { return } const text = getMessage(['ℹ', message, info].join(' ')) console.log(text) }) context.on('success', (message, info = '') => { if (logLevel <= LogLevel.silent) { return } const text = getMessage(['✓', message, logLevel >= LogLevel.info ? info : undefined].filter(Boolean).join(' ')) console.log(text) }) context.on('warn', (message, info) => { if (logLevel < LogLevel.warn) { return } const text = getMessage(['⚠', message, logLevel >= LogLevel.info ? info : undefined].filter(Boolean).join(' ')) console.log(text) }) context.on('error', (error) => { const caused = error.cause as Error | undefined const text = getMessage(['✗', error.message].join(' ')) console.log(text) // Show stack trace in debug mode (first 3 frames) if (logLevel >= LogLevel.debug && error.stack) { const frames = error.stack.split('\n').slice(1, 4) for (const frame of frames) { console.log(getMessage(frame.trim())) } if (caused?.stack) { console.log(`└─ caused by ${caused.message}`) const frames = caused.stack.split('\n').slice(1, 4) for (const frame of frames) { console.log(getMessage(` ${frame.trim()}`)) } } } }) context.on('lifecycle:start', () => { console.log('Kubb CLI 🧩') }) context.on('config:start', () => { if (logLevel <= LogLevel.silent) { return } const text = getMessage('Configuration started') console.log(text) }) context.on('config:end', () => { if (logLevel <= LogLevel.silent) { return } const text = getMessage('Configuration completed') console.log(text) }) context.on('generation:start', () => { const text = getMessage('Configuration started') console.log(text) }) context.on('plugin:start', (plugin) => { if (logLevel <= LogLevel.silent) { return } const text = getMessage(`Generating ${plugin.name}`) console.log(text) }) context.on('plugin:end', (plugin, { duration, success }) => { if (logLevel <= LogLevel.silent) { return } const durationStr = formatMs(duration) const text = getMessage(success ? `${plugin.name} completed in ${durationStr}` : `${plugin.name} failed in ${durationStr}`) console.log(text) }) context.on('files:processing:start', (files) => { if (logLevel <= LogLevel.silent) { return } const text = getMessage(`Writing ${files.length} files`) console.log(text) }) context.on('file:processing:update', ({ file, config }) => { if (logLevel <= LogLevel.silent) { return } const text = getMessage(`Writing ${relative(config.root, file.path)}`) console.log(text) }) context.on('files:processing:end', () => { if (logLevel <= LogLevel.silent) { return } const text = getMessage('Files written successfully') console.log(text) }) context.on('generation:end', (config) => { const text = getMessage(config.name ? `Generation completed for ${config.name}` : 'Generation completed') console.log(text) }) context.on('format:start', () => { if (logLevel <= LogLevel.silent) { return } const text = getMessage('Format started') console.log(text) }) context.on('format:end', () => { if (logLevel <= LogLevel.silent) { return } const text = getMessage('Format completed') console.log(text) }) context.on('lint:start', () => { if (logLevel <= LogLevel.silent) { return } const text = getMessage('Lint started') console.log(text) }) context.on('lint:end', () => { if (logLevel <= LogLevel.silent) { return } const text = getMessage('Lint completed') console.log(text) }) context.on('hook:start', async ({ id, command, args }) => { const commandWithArgs = args?.length ? `${command} ${args.join(' ')}` : command const text = getMessage(`Hook ${commandWithArgs} started`) if (logLevel > LogLevel.silent) { console.log(text) } // Skip hook execution if no id is provided (e.g., during benchmarks or tests) if (!id) { return } try { const result = await x(command, [...(args ?? [])], { nodeOptions: { detached: true }, throwOnError: true, }) await context.emit('debug', { date: new Date(), logs: [result.stdout.trimEnd()], }) if (logLevel > LogLevel.silent) { console.log(result.stdout.trimEnd()) } await context.emit('hook:end', { command, args, id, success: true, error: null, }) } catch (err) { const error = err as NonZeroExitError const stderr = error.output?.stderr ?? '' const stdout = error.output?.stdout ?? '' await context.emit('debug', { date: new Date(), logs: [stdout, stderr].filter(Boolean), }) if (stderr) { console.error(stderr) } if (stdout) { console.log(stdout) } const errorMessage = new Error(`Hook execute failed: ${commandWithArgs}`) await context.emit('hook:end', { command, args, id, success: false, error: errorMessage, }) await context.emit('error', errorMessage) } }) context.on('hook:end', ({ command, args }) => { if (logLevel <= LogLevel.silent) { return } const commandWithArgs = args?.length ? `${command} ${args.join(' ')}` : command const text = getMessage(`Hook ${commandWithArgs} completed`) console.log(text) }) context.on('generation:summary', (config, { pluginTimings, status, hrStart, failedPlugins, filesCreated }) => { const summary = getSummary({ failedPlugins, filesCreated, config, status, hrStart, pluginTimings: logLevel >= LogLevel.verbose ? pluginTimings : undefined, }) console.log('---------------------------') console.log(summary.join('\n')) console.log('---------------------------') }) }, })