UNPKG

openapi-minifier

Version:

A CLI tool by Treblle tp minify OpenAPI V3 Specs by removing redundant information not relevant to AI Agents and LLMs.

130 lines • 6.87 kB
#!/usr/bin/env node import { Command } from 'commander'; import { readFile, writeFile } from 'fs/promises'; import { extname, resolve } from 'path'; import { minifyOpenAPI } from './minifier.js'; import { validateOpenAPI } from './validator.js'; import { colors, getTreblleAsciiArt } from './utils.js'; const program = new Command(); program .name('openapi-minify') .description('Minify OpenAPI v3 specifications by removing redundant information not relevant to AI Agents and LLMs') .version('1.0.0'); program .argument('<input>', 'Input OpenAPI file (JSON or YAML)') .option('-o, --output <file>', 'Output file path') .option('--preset <preset>', 'Minification preset: max, balanced, min', 'balanced') .option('--keep-examples', 'Keep example values', false) .option('--keep-descriptions <mode>', 'Description handling: all, schema-only, none', 'schema-only') .option('--keep-summaries', 'Keep summary fields', false) .option('--keep-tags', 'Keep tag descriptions', false) .option('--remove-deprecated', 'Remove deprecated paths and operations', false) .option('--extract-common-responses', 'Extract common responses to components/responses', false) .option('--extract-common-schemas', 'Extract common schemas to components/schemas', false) .option('--validate', 'Enable OpenAPI validation', false) .option('--format <format>', 'Output format: json, yaml', 'json') .action(async (input, options) => { try { console.log(getTreblleAsciiArt()); console.log(colors.blue('================================================================\n')); const inputPath = resolve(input); console.log(colors.gray(`šŸ“ Input: ${inputPath}`)); const outputPath = options.output || getDefaultOutputPath(inputPath, options.format); console.log(colors.gray(`šŸ“ Output: ${outputPath}\n`)); console.log(colors.yellow('šŸ“– Reading input file...')); const inputContent = await readFile(inputPath, 'utf-8'); const originalSize = Buffer.byteLength(inputContent, 'utf-8'); if (options.validate) { console.log(colors.yellow('āœ… Validating OpenAPI specification...')); await validateOpenAPI(inputContent, getFileFormat(inputPath)); console.log(colors.green('āœ… OpenAPI specification is valid')); } const minificationOptions = { keepExamples: options.keepExamples, keepDescriptions: options.keepDescriptions, keepSummaries: options.keepSummaries, keepTags: options.keepTags, removeDeprecated: options.removeDeprecated, extractCommonResponses: options.extractCommonResponses, extractCommonSchemas: options.extractCommonSchemas, validate: options.validate, preset: options.preset, }; applyPreset(minificationOptions); console.log(colors.yellow('šŸ”§ Minifying OpenAPI specification...')); console.log(colors.gray(` Preset: ${minificationOptions.preset}`)); console.log(colors.gray(` Keep examples: ${minificationOptions.keepExamples}`)); console.log(colors.gray(` Keep descriptions: ${minificationOptions.keepDescriptions}`)); const outputFormat = options.format === 'yaml' ? 'yaml' : 'json'; const result = await minifyOpenAPI(inputContent, getFileFormat(inputPath), minificationOptions, outputFormat); console.log(colors.yellow('šŸ’¾ Writing minified specification...')); await writeFile(outputPath, result.minifiedContent); console.log(colors.green('\nšŸŽ‰ Minification completed successfully!\n')); console.log(colors.bold('šŸ“Š Results:')); console.log(` Original size: ${colors.cyan(formatBytes(originalSize))}`); console.log(` Minified size: ${colors.cyan(formatBytes(result.stats.minifiedSize))}`); console.log(` Size reduction: ${colors.green(result.stats.reductionPercentage.toFixed(1))}%`); console.log(`\n Removed elements:`); console.log(` • Examples: ${colors.yellow(result.stats.removedElements.examples.toString())}`); console.log(` • Descriptions: ${colors.yellow(result.stats.removedElements.descriptions.toString())}`); console.log(` • Summaries: ${colors.yellow(result.stats.removedElements.summaries.toString())}`); console.log(` • Tags: ${colors.yellow(result.stats.removedElements.tags.toString())}`); console.log(` • Deprecated paths: ${colors.yellow(result.stats.removedElements.deprecatedPaths.toString())}`); console.log(` • Extracted responses: ${colors.yellow(result.stats.removedElements.extractedResponses.toString())}`); console.log(` • Extracted schemas: ${colors.yellow(result.stats.removedElements.extractedSchemas.toString())}`); } catch (error) { console.error(colors.red('āŒ Error:'), error instanceof Error ? error.message : String(error)); process.exit(1); } }); function getDefaultOutputPath(inputPath, format) { const ext = format === 'yaml' ? '.yaml' : '.json'; const baseName = inputPath.replace(/\.(json|yaml|yml)$/i, ''); return `${baseName}.minified${ext}`; } function getFileFormat(filePath) { const ext = extname(filePath).toLowerCase(); return ext === '.yaml' || ext === '.yml' ? 'yaml' : 'json'; } function applyPreset(options) { switch (options.preset) { case 'balanced': options.keepExamples = false; options.keepDescriptions = 'schema-only'; options.keepSummaries = false; options.keepTags = false; options.removeDeprecated = true; options.extractCommonResponses = true; options.extractCommonSchemas = true; break; case 'max': options.keepExamples = false; options.keepDescriptions = 'none'; options.keepSummaries = false; options.keepTags = false; options.removeDeprecated = true; options.extractCommonResponses = true; options.extractCommonSchemas = true; break; case 'min': options.keepExamples = true; options.keepDescriptions = 'all'; options.keepSummaries = true; options.keepTags = true; options.removeDeprecated = false; options.extractCommonResponses = false; options.extractCommonSchemas = false; break; } } function formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`; } program.parse(); //# sourceMappingURL=cli.js.map