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