UNPKG

chanfana

Version:

OpenAPI 3 and 3.1 schema generator and validator for Hono, itty-router and more!

153 lines (149 loc) 5.05 kB
#!/usr/bin/env node "use strict"; // src/cli.ts var import_node_child_process = require("child_process"); var import_promises = require("fs/promises"); var import_node_path = require("path"); var READY_KEYWORD = "ready on"; var URL_REGEX = /ready on\s+(https?:\/\/[\w.-]+:\d+)/i; var outputFile = "schema.json"; var wranglerArgs = ["wrangler", "dev"]; var args = process.argv.slice(2); if (args.includes("--help") || args.includes("-h")) { console.log(` Usage: npx chanfana [options] Options: -o, --output <path> Specify output file path (including optional directory) -h, --help Display this help message Examples: npx chanfana -o output/schemas/public-api.json npx chanfana --output schema.json npx chanfana --help `); process.exit(0); } for (let i = 0; i < args.length; i++) { if (args[i] === "-o" || args[i] === "--output") { if (i + 1 >= args.length) { console.error("Error: -o/--output requires a file path"); process.exit(1); } const filePath = args[i + 1]; if (!filePath) { console.error("Error: -o/--output file path cannot be empty"); process.exit(1); } outputFile = filePath; i++; } else if (typeof args[i] === "string") { wranglerArgs.push(args[i]); } } var resolvedOutputFile = (0, import_node_path.join)(process.cwd(), outputFile); var outputDir = (0, import_node_path.dirname)(resolvedOutputFile); var childProcess = (0, import_node_child_process.spawn)("npx", wranglerArgs, { cwd: process.cwd(), stdio: ["inherit", "pipe", "pipe"] }); var outputBuffer = []; childProcess.stdout.on("data", (data) => { const line = data.toString().trim(); outputBuffer.push(line); }); childProcess.stderr.on("data", (data) => { const line = data.toString().trim(); outputBuffer.push(`Error: ${line}`); }); childProcess.stdout.on("data", async (data) => { const line = data.toString().trim(); if (line.toLowerCase().includes(READY_KEYWORD)) { const match = line.match(URL_REGEX); if (match?.[1]) { const url = match[1]; const request = new Request(`${url}/openapi.json`, { method: "GET", headers: { "Content-Type": "application/json" } }); try { const response = await fetch(request); if (!response.ok) { console.error(`Error fetching schema: ${response.status} ${response.statusText}`); const body = await response.text(); console.error("Response body:", body); console.error("Buffered output:", outputBuffer.join("\n")); childProcess.kill("SIGTERM"); process.exit(1); } const schema = await response.json(); if (schema.paths && Object.keys(schema.paths).length > 0) { for (const path in schema.paths) { const pathObj = schema.paths[path]; for (const method in pathObj) { if (pathObj[method]["x-ignore"] === true) { delete schema.paths[path]; break; } } } } const schemaString = JSON.stringify(schema, null, 2); try { await (0, import_promises.mkdir)(outputDir, { recursive: true }); await (0, import_promises.writeFile)(resolvedOutputFile, schemaString); console.log(`Schema written to ${resolvedOutputFile}`); } catch (err) { const error = err; console.error(`Error writing schema to ${resolvedOutputFile}: ${error.message}`); console.error("Buffered output:", outputBuffer.join("\n")); childProcess.kill("SIGTERM"); process.exit(1); } console.log("Successfully extracted schema"); childProcess.kill("SIGTERM"); process.exit(0); } catch (err) { const error = err; console.error(`Fetch error: ${error.message}`); console.error("Buffered output:", outputBuffer.join("\n")); childProcess.kill("SIGTERM"); process.exit(1); } } else { console.error(`No URL found in "ready on" line: ${line}`); console.error("Buffered output:", outputBuffer.join("\n")); } } }); var timeoutId = setTimeout(() => { childProcess.kill("SIGTERM"); console.error(`Command "npx wrangler dev" was never ready, exiting...`); console.error("Buffered output:", outputBuffer.join("\n")); process.exit(1); }, 6e4); var cleanup = () => { clearTimeout(timeoutId); if (!childProcess.killed) { childProcess.kill("SIGTERM"); console.log("Cleaning up child process on exit"); } }; process.on("exit", cleanup); process.on("SIGINT", () => { console.log("Received SIGINT (Ctrl+C), exiting..."); cleanup(); process.exit(0); }); process.on("SIGTERM", () => { console.log("Received SIGTERM, exiting..."); cleanup(); process.exit(0); }); process.on("uncaughtException", (err) => { const error = err; console.error("Uncaught Exception:", error.message); console.error("Buffered output:", outputBuffer.join("\n")); cleanup(); process.exit(1); });