@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
JavaScript
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