UNPKG

swaggler

Version:

Swaggler helps you smuggle your existing API requests into structured, well-documented specs with ease

178 lines (177 loc) 8.67 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.CLIService = void 0; const commander_1 = require("commander"); const child_process_1 = require("child_process"); const util_1 = require("util"); const CurlParser_1 = require("./CurlParser"); const OpenAPIGenerator_1 = require("./OpenAPIGenerator"); const fs = __importStar(require("fs")); const SwagglerException_1 = require("../errors/SwagglerException"); const path = __importStar(require("path")); // Read version from package.json const packageJsonPath = path.resolve(__dirname, "..", "..", "package.json"); let pkgVersion = "0.0.0"; try { const pkgContents = fs.readFileSync(packageJsonPath, "utf-8"); const pkg = JSON.parse(pkgContents); pkgVersion = pkg.version; } catch (err) { console.warn(`Could not read version from package.json: ${err}`); } const execAsync = (0, util_1.promisify)(child_process_1.exec); class CLIService { static setupCLI() { const program = new commander_1.Command(); program .name("Swaggler") .description("Swaggler helps you smuggle your existing API requests into structured, well-documented specs with ease") .version(pkgVersion); program .command("generate") .description("Generate OpenAPI documentation from curl command or response") .option("-c, --curl <curl>", "Curl command to convert or path to a file containing a curl command") .option("-i, --input <input>", "Path to a file containing a curl command") .option("-o, --output <output>", "Output file name (defaults to swagger.yaml)", "swagger.yaml") .option("-p, --output-path <path>", "Explicit output path (overrides --output if provided)") .option("-n, --name <name>", "Operation name", "") .option("-s, --schema <schema>", "URL template with parameters (e.g. /users/:id/edit)") .option("-t, --tag <tag>", "Tag for the operation") .option("-a, --append <file>", "Append to existing swagger file") .option("-S, --summary <summary>", "Custom summary for the operation") .option("-x, --skip-execution", "Skip executing the curl command and use provided response", false) .option("-r, --response <response>", "JSON response to use when skip-execution is true") .option("-R, --response-file <file>", "Path to a JSON file containing the response to use when skip-execution is true") .action(async (options) => { try { // Must have at least one source of curl text if (!options.curl && !options.input) { throw new Error("Either --curl or --input must be provided"); } // Load the curl command (from option or file) let curlCommand = options.curl || ""; if (options.input) { if (!fs.existsSync(options.input)) { throw new Error(`Input file not found: ${options.input}`); } curlCommand = fs.readFileSync(options.input, "utf-8").trim(); } else if (fs.existsSync(curlCommand)) { // if --curl points at an existing file, read from that curlCommand = fs.readFileSync(curlCommand, "utf-8").trim(); } // Sanity check if (!curlCommand.startsWith("curl ")) { throw new Error('Curl command must start with "curl"'); } // Fetch or accept the response JSON let responseData; if (!options.skipExecution) { console.log("→ Executing curl …"); const { stdout, stderr } = await execAsync(curlCommand); if (stderr) console.warn("⚠ warning from curl:", stderr); try { responseData = JSON.parse(stdout); } catch { throw new Error("Failed to parse curl output as JSON"); } } else { if (!options.response && !options.responseFile) { throw new Error("Either --response or --response-file is required when --skip-execution is set"); } try { if (options.responseFile) { if (!fs.existsSync(options.responseFile)) { throw new Error(`Response file not found: ${options.responseFile}`); } responseData = JSON.parse(fs.readFileSync(options.responseFile, "utf-8")); } else { console.log("------------- response ----------------"); console.log(options.response); console.log("--------------------------------"); responseData = JSON.parse(options.response); } } catch (err) { throw new Error(`Invalid JSON: ${err instanceof Error ? err.message : "Unknown error"}`); } } // Sanitize & parse const sanitized = CurlParser_1.CurlParser.sanitize(curlCommand); const parsed = CurlParser_1.CurlParser.parse(sanitized); // Decide on final output path (fallback to --output) const outputFile = options.outputPath ? options.outputPath : options.output; // Build OpenAPI options const openAPIOptions = { operationName: options.name, urlTemplate: options.schema, tags: options.tag ? [options.tag] : undefined, outputPath: outputFile, appendPath: options.append, summary: options.summary, }; // Generate + (optionally) merge let openapi = OpenAPIGenerator_1.OpenAPIGenerator.generate(parsed, responseData, openAPIOptions); if (options.append) { openapi = OpenAPIGenerator_1.OpenAPIGenerator.mergeWithExisting(openapi, options.append); } // Write it out OpenAPIGenerator_1.OpenAPIGenerator.saveToFile(openapi, openAPIOptions); console.log(`----- ✅🥳🚀 OpenAPI spec written to ${outputFile} -----`); } catch (err) { if (err instanceof SwagglerException_1.SwagglerException) { console.error(`Error: ${err.message}`); if (err.details) console.error("Details:", err.details); } else { console.error("Error:", err.message || err); } process.exit(1); } }); return program; } } exports.CLIService = CLIService;