swaggler
Version:
Swaggler helps you smuggle your existing API requests into structured, well-documented specs with ease
178 lines (177 loc) • 8.67 kB
JavaScript
;
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;