UNPKG

@kubb/cli

Version:

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

343 lines (339 loc) 11.9 kB
const require_chunk = require('./chunk-CNbaEX1y.cjs'); const require_package = require('./package-oo3QhWS5.cjs'); let citty = require("citty"); let node_child_process = require("node:child_process"); let node_path = require("node:path"); node_path = require_chunk.__toESM(node_path); let node_process = require("node:process"); node_process = require_chunk.__toESM(node_process); let node_util = require("node:util"); let _clack_prompts = require("@clack/prompts"); _clack_prompts = require_chunk.__toESM(_clack_prompts); let node_fs = require("node:fs"); node_fs = require_chunk.__toESM(node_fs); //#region src/utils/packageManager.ts const packageManagers = { pnpm: { name: "pnpm", lockFile: "pnpm-lock.yaml", installCommand: ["add", "-D"] }, yarn: { name: "yarn", lockFile: "yarn.lock", installCommand: ["add", "-D"] }, bun: { name: "bun", lockFile: "bun.lockb", installCommand: ["add", "-d"] }, npm: { name: "npm", lockFile: "package-lock.json", installCommand: ["install", "--save-dev"] } }; function detectPackageManager(cwd = process.cwd()) { const packageJsonPath = node_path.default.join(cwd, "package.json"); if (node_fs.default.existsSync(packageJsonPath)) try { const packageJson = JSON.parse(node_fs.default.readFileSync(packageJsonPath, "utf-8")); if (packageJson.packageManager) { const [name] = packageJson.packageManager.split("@"); if (name in packageManagers) return packageManagers[name]; } } catch {} for (const pm of Object.values(packageManagers)) if (node_fs.default.existsSync(node_path.default.join(cwd, pm.lockFile))) return pm; return packageManagers.npm; } function hasPackageJson(cwd = process.cwd()) { return node_fs.default.existsSync(node_path.default.join(cwd, "package.json")); } async function initPackageJson(cwd, packageManager) { (0, node_child_process.spawn)(packageManager.name, { npm: ["init", "-y"], pnpm: ["init"], yarn: ["init", "-y"], bun: ["init", "-y"] }[packageManager.name], { stdio: "inherit", cwd }); } async function installPackages(packages, packageManager, cwd = process.cwd()) { (0, node_child_process.spawn)(packageManager.name, [...packageManager.installCommand, ...packages], { stdio: "inherit", cwd }); } //#endregion //#region src/commands/init.ts const plugins = [ { value: "plugin-oas", label: "OpenAPI Parser", hint: "Required", packageName: "@kubb/plugin-oas", importName: "pluginOas", category: "core" }, { value: "plugin-ts", label: "TypeScript", hint: "Recommended", packageName: "@kubb/plugin-ts", importName: "pluginTs", category: "typescript" }, { value: "plugin-client", label: "Client (Fetch/Axios)", packageName: "@kubb/plugin-client", importName: "pluginClient", category: "typescript" }, { value: "plugin-react-query", label: "React Query / TanStack Query", packageName: "@kubb/plugin-react-query", importName: "pluginReactQuery", category: "query" }, { value: "plugin-solid-query", label: "Solid Query", packageName: "@kubb/plugin-solid-query", importName: "pluginSolidQuery", category: "query" }, { value: "plugin-svelte-query", label: "Svelte Query", packageName: "@kubb/plugin-svelte-query", importName: "pluginSvelteQuery", category: "query" }, { value: "plugin-vue-query", label: "Vue Query", packageName: "@kubb/plugin-vue-query", importName: "pluginVueQuery", category: "query" }, { value: "plugin-swr", label: "SWR", packageName: "@kubb/plugin-swr", importName: "pluginSwr", category: "query" }, { value: "plugin-zod", label: "Zod Schemas", packageName: "@kubb/plugin-zod", importName: "pluginZod", category: "validation" }, { value: "plugin-faker", label: "Faker.js Mocks", packageName: "@kubb/plugin-faker", importName: "pluginFaker", category: "mocking" }, { value: "plugin-msw", label: "MSW Handlers", packageName: "@kubb/plugin-msw", importName: "pluginMsw", category: "mocking" }, { value: "plugin-cypress", label: "Cypress Tests", packageName: "@kubb/plugin-cypress", importName: "pluginCypress", category: "testing" }, { value: "plugin-redoc", label: "ReDoc Documentation", packageName: "@kubb/plugin-redoc", importName: "pluginRedoc", category: "docs" } ]; function generateConfigFile(selectedPlugins, inputPath, outputPath) { return `import { defineConfig } from '@kubb/core' ${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")} export default defineConfig({ root: '.', input: { path: '${inputPath}', }, output: { path: '${outputPath}', clean: true, }, plugins: [ ${selectedPlugins.map((plugin) => { if (plugin.value === "plugin-oas") return " pluginOas(),"; if (plugin.value === "plugin-ts") return ` pluginTs({\n output: {\n path: 'models',\n },\n }),`; if (plugin.value === "plugin-client") return ` pluginClient({\n output: {\n path: 'clients',\n },\n }),`; if (plugin.value === "plugin-react-query") return ` pluginReactQuery({\n output: {\n path: 'hooks',\n },\n }),`; if (plugin.value === "plugin-zod") return ` pluginZod({\n output: {\n path: 'zod',\n },\n }),`; if (plugin.value === "plugin-faker") return ` pluginFaker({\n output: {\n path: 'mocks',\n },\n }),`; if (plugin.value === "plugin-msw") return ` pluginMsw({\n output: {\n path: 'msw',\n },\n }),`; if (plugin.value === "plugin-swr") return ` pluginSwr({\n output: {\n path: 'hooks',\n },\n }),`; return ` ${plugin.importName}(),`; }).join("\n")} ], }) `; } const DEFAULT_INPUT_PATH = "./openapi.yaml"; const DEFAULT_OUTPUT_PATH = "./src/gen"; const DEFAULT_PLUGINS = ["plugin-oas", "plugin-ts"]; const command = (0, citty.defineCommand)({ meta: { name: "init", description: "Initialize a new Kubb project with interactive setup" }, args: { yes: { type: "boolean", alias: "y", description: "Skip prompts and use default options", default: false } }, async run({ args }) { const cwd = node_process.default.cwd(); const yes = args.yes; _clack_prompts.intro((0, node_util.styleText)("bgCyan", (0, node_util.styleText)("black", " Kubb Init "))); try { let packageManager; if (!hasPackageJson(cwd)) { if (!yes) { const shouldInit = await _clack_prompts.confirm({ message: "No package.json found. Would you like to create one?", initialValue: true }); if (_clack_prompts.isCancel(shouldInit) || !shouldInit) { _clack_prompts.cancel("Operation cancelled."); node_process.default.exit(0); } } packageManager = detectPackageManager(cwd); const spinner = _clack_prompts.spinner(); spinner.start(`Initializing package.json with ${packageManager.name}`); await initPackageJson(cwd, packageManager); spinner.stop(`Created package.json with ${packageManager.name}`); } else { packageManager = detectPackageManager(cwd); _clack_prompts.log.info(`Detected package manager: ${(0, node_util.styleText)("cyan", packageManager.name)}`); } let inputPath; if (yes) { inputPath = DEFAULT_INPUT_PATH; _clack_prompts.log.info(`Using input path: ${(0, node_util.styleText)("cyan", inputPath)}`); } else { const inputPathResult = await _clack_prompts.text({ message: "Where is your OpenAPI specification located?", placeholder: DEFAULT_INPUT_PATH, defaultValue: DEFAULT_INPUT_PATH, validate: (value) => { if (!value) return "Input path is required"; } }); if (_clack_prompts.isCancel(inputPathResult)) { _clack_prompts.cancel("Operation cancelled."); node_process.default.exit(0); } inputPath = inputPathResult; } let outputPath; if (yes) { outputPath = DEFAULT_OUTPUT_PATH; _clack_prompts.log.info(`Using output path: ${(0, node_util.styleText)("cyan", outputPath)}`); } else { const outputPathResult = await _clack_prompts.text({ message: "Where should the generated files be output?", placeholder: DEFAULT_OUTPUT_PATH, defaultValue: DEFAULT_OUTPUT_PATH, validate: (value) => { if (!value) return "Output path is required"; } }); if (_clack_prompts.isCancel(outputPathResult)) { _clack_prompts.cancel("Operation cancelled."); node_process.default.exit(0); } outputPath = outputPathResult; } let selectedPlugins; if (yes) { selectedPlugins = plugins.filter((plugin) => DEFAULT_PLUGINS.includes(plugin.value)); _clack_prompts.log.info(`Using plugins: ${(0, node_util.styleText)("cyan", selectedPlugins.map((p) => p.label).join(", "))}`); } else { const selectedPluginValues = await _clack_prompts.multiselect({ message: "Select plugins to use:", options: plugins.map((plugin) => ({ value: plugin.value, label: plugin.label, hint: plugin.hint })), initialValues: DEFAULT_PLUGINS, required: true }); if (_clack_prompts.isCancel(selectedPluginValues)) { _clack_prompts.cancel("Operation cancelled."); node_process.default.exit(0); } selectedPlugins = plugins.filter((plugin) => selectedPluginValues.includes(plugin.value)); } if (!selectedPlugins.find((p) => p.value === "plugin-oas")) selectedPlugins.unshift(plugins.find((p) => p.value === "plugin-oas")); const packagesToInstall = [ "@kubb/core", "@kubb/cli", "@kubb/agent", ...selectedPlugins.map((p) => p.packageName) ]; const spinner = _clack_prompts.spinner(); spinner.start(`Installing ${packagesToInstall.length} packages with ${packageManager.name}`); try { await installPackages(packagesToInstall, packageManager, cwd); spinner.stop(`Installed ${packagesToInstall.length} packages`); } catch (error) { spinner.stop("Installation failed"); throw error; } const configSpinner = _clack_prompts.spinner(); configSpinner.start("Creating kubb.config.ts"); const configContent = generateConfigFile(selectedPlugins, inputPath, outputPath); const configPath = node_path.default.join(cwd, "kubb.config.ts"); if (node_fs.default.existsSync(configPath)) { configSpinner.stop("kubb.config.ts already exists"); if (!yes) { const shouldOverwrite = await _clack_prompts.confirm({ message: "kubb.config.ts already exists. Overwrite?", initialValue: false }); if (_clack_prompts.isCancel(shouldOverwrite) || !shouldOverwrite) { _clack_prompts.cancel("Keeping existing configuration. Packages have been installed."); node_process.default.exit(0); } } configSpinner.start("Overwriting kubb.config.ts"); } node_fs.default.writeFileSync(configPath, configContent, "utf-8"); configSpinner.stop("Created kubb.config.ts"); _clack_prompts.outro((0, node_util.styleText)("green", "✓ All set!") + "\n\n" + (0, node_util.styleText)("dim", "Next steps:") + "\n" + (0, node_util.styleText)("cyan", ` 1. Make sure your OpenAPI spec is at: ${inputPath}`) + "\n" + (0, node_util.styleText)("cyan", " 2. Generate code with: npx kubb generate") + "\n" + (0, node_util.styleText)("cyan", " Or start a stream server with: npx kubb start") + "\n" + (0, node_util.styleText)("cyan", ` 3. Find generated files in: ${outputPath}`) + "\n\n" + (0, node_util.styleText)("dim", `Using ${packageManager.name} • Kubb v${require_package.version}`)); } catch (error) { _clack_prompts.log.error((0, node_util.styleText)("red", "An error occurred during initialization")); if (error instanceof Error) _clack_prompts.log.error(error.message); node_process.default.exit(1); } } }); //#endregion exports.default = command; //# sourceMappingURL=init-BDWQO7I8.cjs.map