UNPKG

@kubb/cli

Version:

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

271 lines (264 loc) 8.82 kB
import path from "node:path"; import * as process$2 from "node:process"; import process$1 from "node:process"; import { safeBuild, setup } from "@kubb/core"; import { LogMapper, createLogger, randomCliColour } from "@kubb/core/logger"; import pc from "picocolors"; import { Presets, SingleBar } from "cli-progress"; import { execa } from "execa"; import { parseArgsStringToArgv } from "string-argv"; import { Writable } from "node:stream"; //#region src/utils/Writables.ts var ConsolaWritable = class extends Writable { consola; command; constructor(consola, command, opts) { super(opts); this.command = command; this.consola = consola; } _write(chunk, _encoding, callback) { process$2.stdout.write(`${pc.dim(chunk?.toString())}`); callback(); } }; //#endregion //#region src/utils/executeHooks.ts async function executeHooks({ hooks, logger }) { const commands = Array.isArray(hooks.done) ? hooks.done : [hooks.done].filter(Boolean); for (const command of commands) { const consolaWritable = new ConsolaWritable(logger.consola, command); const [cmd, ..._args] = [...parseArgsStringToArgv(command)]; if (!cmd) continue; logger?.emit("start", `Executing hook ${logger.logLevel !== LogMapper.silent ? pc.dim(command) : ""}`); await execa(cmd, _args, { detached: true, stdout: logger?.logLevel === LogMapper.silent ? void 0 : ["pipe", consolaWritable], stripFinalNewline: true }); logger?.emit("success", `Executed hook ${logger.logLevel !== LogMapper.silent ? pc.dim(command) : ""}`); } logger?.emit("success", "Executed hooks"); } //#endregion //#region src/utils/getErrorCauses.ts function getErrorCauses(errors) { return errors.reduce((prev, error) => { const causedError = error?.cause; if (causedError) { prev = [...prev, ...getErrorCauses([causedError])]; return prev; } prev = [...prev, error]; return prev; }, []).filter(Boolean); } //#endregion //#region src/utils/parseHrtimeToSeconds.ts function parseHrtimeToSeconds(hrtime) { return (hrtime[0] + hrtime[1] / 1e9).toFixed(3); } //#endregion //#region src/utils/getSummary.ts function getSummary({ pluginManager, filesCreated, status, hrStart, config }) { const logs = /* @__PURE__ */ new Set(); const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(hrStart)); const buildStartPlugins = pluginManager.executed.filter((item) => item.hookName === "buildStart" && item.plugin.name !== "core").map((item) => item.plugin.name); const buildEndPlugins = pluginManager.executed.filter((item) => item.hookName === "buildEnd" && item.plugin.name !== "core").map((item) => item.plugin.name); const failedPlugins = config.plugins?.filter((plugin) => !buildEndPlugins.includes(plugin.name))?.map((plugin) => plugin.name); const pluginsCount = config.plugins?.length || 0; const meta = { plugins: status === "success" ? `${pc.green(`${buildStartPlugins.length} successful`)}, ${pluginsCount} total` : `${pc.red(`${failedPlugins?.length ?? 1} failed`)}, ${pluginsCount} total`, pluginsFailed: status === "failed" ? failedPlugins?.map((name) => randomCliColour(name))?.join(", ") : void 0, filesCreated, time: `${pc.yellow(`${elapsedSeconds}s`)}`, output: path.isAbsolute(config.root) ? path.resolve(config.root, config.output.path) : config.root }; logs.add([ [`${pc.bold("Plugins:")} ${meta.plugins}`, true], [`${pc.dim("Failed:")} ${meta.pluginsFailed || "none"}`, !!meta.pluginsFailed], [`${pc.bold("Generated:")} ${meta.filesCreated} files in ${meta.time}`, true], [`${pc.bold("Output:")} ${meta.output}`, true] ].map((item) => { if (item.at(1)) return item.at(0); }).filter(Boolean).join("\n")); return [...logs]; } //#endregion //#region src/runners/generate.ts async function generate({ input, config, progressCache, args }) { const hrStart = process$1.hrtime(); const logger = createLogger({ logLevel: LogMapper[args.logLevel] || 3, name: config.name }); const { root = process$1.cwd(),...userConfig } = config; const inputPath = input ?? ("path" in userConfig.input ? userConfig.input.path : void 0); if (logger.logLevel !== LogMapper.debug) { logger.on("progress_start", ({ id, size, message = "" }) => { logger.consola?.pauseLogs(); const payload = { id, message }; const progressBar = new SingleBar({ format: "{percentage}% {bar} {value}/{total} | {message}", barsize: 30, clearOnComplete: true, emptyOnZero: true }, Presets.shades_grey); if (!progressCache.has(id)) { progressCache.set(id, progressBar); progressBar.start(size, 1, payload); } }); logger.on("progress_stop", ({ id }) => { progressCache.get(id)?.stop(); logger.consola?.resumeLogs(); }); logger.on("progressed", ({ id, message = "" }) => { const payload = { id, message }; progressCache.get(id)?.increment(1, payload); }); } const definedConfig = { root, ...userConfig, input: inputPath ? { ...userConfig.input, path: inputPath } : userConfig.input, output: { write: true, barrelType: "named", extension: { ".ts": ".ts" }, format: "prettier", ...userConfig.output } }; const { fabric, pluginManager } = await setup({ config: definedConfig, logger }); logger.emit("start", `Building ${logger.logLevel !== LogMapper.silent ? pc.dim(inputPath) : ""}`); const { files, error } = await safeBuild({ config: definedConfig, logger }, { pluginManager, fabric }); if (logger.logLevel === LogMapper.debug) { logger.consola?.start("Writing logs"); const logFiles = await logger.writeLogs(); logger.consola?.success(`Written logs: \n${logFiles.join("\n")}`); } const summary = getSummary({ filesCreated: files.length, pluginManager, config: definedConfig, status: error ? "failed" : "success", hrStart }); if (error && logger.consola) { logger.consola?.resumeLogs(); logger.consola.error(`Build failed ${logger.logLevel !== LogMapper.silent ? pc.dim(inputPath) : ""}`); logger.consola.box({ title: `${config.name || ""}`, message: summary.join(""), style: { padding: 2, borderColor: "red", borderStyle: "rounded" } }); const errors = getErrorCauses([error]); if (logger.consola && errors.length && logger.logLevel === LogMapper.debug) errors.forEach((err) => { logger.consola?.error(err); }); logger.consola?.error(error); process$1.exit(1); } if (config.output.format === "prettier") { logger?.emit("start", `Formatting with ${config.output.format}`); try { await execa("prettier", [ "--ignore-unknown", "--write", path.resolve(definedConfig.root, definedConfig.output.path) ]); } catch (e) { logger.consola?.warn("Prettier not found"); logger.consola?.error(e); } logger?.emit("success", `Formatted with ${config.output.format}`); } if (config.output.format === "biome") { logger?.emit("start", `Formatting with ${config.output.format}`); try { await execa("biome", [ "format", "--write", path.resolve(definedConfig.root, definedConfig.output.path) ]); } catch (e) { logger.consola?.warn("Biome not found"); logger.consola?.error(e); } logger?.emit("success", `Formatted with ${config.output.format}`); } if (config.output.lint === "eslint") { logger?.emit("start", `Linting with ${config.output.format}`); try { await execa("eslint", [path.resolve(definedConfig.root, definedConfig.output.path), "--fix"]); } catch (e) { logger.consola?.warn("Eslint not found"); logger.consola?.error(e); } logger?.emit("success", `Linted with ${config.output.format}`); } if (config.output.lint === "biome") { logger?.emit("start", `Linting with ${config.output.format}`); try { await execa("biome", [ "lint", "--fix", path.resolve(definedConfig.root, definedConfig.output.path) ]); } catch (e) { logger.consola?.warn("Biome not found"); logger.consola?.error(e); } logger?.emit("success", `Linted with ${config.output.format}`); } if (config.output.lint === "oxlint") { logger?.emit("start", `Linting with ${config.output.format}`); try { await execa("oxlint", ["--fix", path.resolve(definedConfig.root, definedConfig.output.path)]); } catch (e) { logger.consola?.warn("Oxlint not found"); logger.consola?.error(e); } logger?.emit("success", `Linted with ${config.output.format}`); } if (config.hooks) await executeHooks({ hooks: config.hooks, logger }); logger.consola?.log(`⚡Build completed ${logger.logLevel !== LogMapper.silent ? pc.dim(inputPath) : ""}`); logger.consola?.box({ title: `${config.name || ""}`, message: summary.join(""), style: { padding: 2, borderColor: "green", borderStyle: "rounded" } }); } //#endregion export { generate }; //# sourceMappingURL=generate-ChOKW4hA.js.map