UNPKG

openapi-generate-html

Version:

Generate standalone HTML from OpenAPI Specification

144 lines (130 loc) 3.73 kB
#!/usr/bin/env node import { program } from 'commander' import inquirer from 'inquirer' import fs from 'fs' import yaml from 'js-yaml' import ejs from 'ejs' import path from 'path' import { fileURLToPath } from 'url' import $RefParser from '@apidevtools/json-schema-ref-parser' import { UI } from './constants/index.js' import { validateCommandInput, validateCommandOutput, validateCommandUi, validateCommandTheme, validateInquirerInput, validateInquirerOutput, isValidUrl, isJSON, isYAML, } from './utils/validate.js' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) async function main() { // 1. get CLI options from user const options = getCliOptions() const answers = await askQuestions(options) const result = { ...options, ...answers } // 2. bundle HTML file based on the UI const htmlContent = await renderOpenApiHtml(result) // 3. write the HTML file const output = result.output fs.writeFileSync(output, htmlContent, 'utf-8') console.log(`✅ HTML file generated: ${output}`) } function getCliOptions() { program .option( '-i, --input <input>', 'Input OpenAPI file or URL', validateCommandInput, ) .option( '-o, --output <output>', 'Output HTML file name', validateCommandOutput, ) .option('--ui <ui>', `Choose UI (${UI.join(', ')})`, validateCommandUi) .option('--title <title>', 'Title of the HTML page', 'OpenAPI Docs') .option('--description <description>', 'Description of the HTML page', '') .option( '--theme <theme>', 'Theme of the HTML page. Choose from light or dark.', validateCommandTheme, ) .parse(process.argv) return program.opts() } async function askQuestions(options) { return await inquirer.prompt([ { type: 'list', name: 'ui', message: 'Which UI would you like to use?', default: UI[0], choices: UI, when: !options.ui, }, { type: 'input', name: 'input', message: 'Please provide the path to your OpenAPI file or URL:', default: './openapi.json', validate: validateInquirerInput, when: !options.input, }, { type: 'input', name: 'output', message: 'Output HTML file name:', default: 'openapi.html', validate: validateInquirerOutput, when: !options.output, }, ]) } async function renderOpenApiHtml(result) { const ui = result.ui const template = fs.readFileSync( path.resolve(__dirname, `../resources/${ui}/template.ejs`), 'utf-8', ) const cssContent = fs.readFileSync( path.resolve(__dirname, `../resources/${ui}/index.css`), 'utf-8', ) const jsContent = fs.readFileSync( path.resolve(__dirname, `../resources/${ui}/index.js`), 'utf-8', ) const input = result.input let rawApiDocsText if (isValidUrl(input)) { rawApiDocsText = await (await fetch(input)).text() } else { rawApiDocsText = fs.readFileSync(input, 'utf-8') } let rawApiDocs if (isJSON(rawApiDocsText)) { rawApiDocs = JSON.parse(rawApiDocsText) } else if (isYAML(rawApiDocsText)) { rawApiDocs = yaml.load(rawApiDocsText) } else { throw new Error( 'Unsupported file format. Please provide a .json or .yaml/.yml file.', ) } // resolve $ref pointers // https://github.com/APIDevTools/json-schema-ref-parser/blob/main/docs/ref-parser.md#bundleschema-options-callback const apiDocs = await $RefParser.bundle(rawApiDocs) return ejs.render(template, { theme: result.theme, title: result.title, description: result.description, jsContent, cssContent, apiDocs: JSON.stringify(apiDocs), }) } main()