diff --git a/.changeset/flat-comics-learn.md b/.changeset/flat-comics-learn.md new file mode 100644 index 00000000..1374a851 --- /dev/null +++ b/.changeset/flat-comics-learn.md @@ -0,0 +1,5 @@ +--- +"@scalar/cli": patch +--- + +feat: add bundle command diff --git a/README.md b/README.md index 010b142d..45bb267b 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Command-line interface to work with OpenAPI files * Upload your OpenAPI files to Scalar * Get a fully mocked API for testing purposes * Preview your API reference +* Bundle multiple OpenAPI files ## Quickstart @@ -34,8 +35,9 @@ scalar --version scalar init scalar format openapi.json scalar validate openapi.json -scalar reference openapi.json -scalar mock openapi.json +scalar bundle openapi.json --output bundle.json +scalar reference openapi.json --watch +scalar mock openapi.json --watch scalar share openapi.json ``` diff --git a/packages/cli/README.md b/packages/cli/README.md index dcc2c310..579b97f6 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -62,6 +62,16 @@ You can also change the port like this: scalar mock openapi.json --watch --port 8080 ``` +### bundle + +Some OpenAPI files reference other files from the file system or an URL. You can bundle those files and make them a single file: + +```bash +scalar bundle openapi.json --output bundle.json +``` + +If you don’t provide an `output` file name, the input file will be overwritten. + ### init If you’re tired of passing the file name again and again, just configure it once: diff --git a/packages/cli/src/commands/bundle/BundleCommand.ts b/packages/cli/src/commands/bundle/BundleCommand.ts new file mode 100644 index 00000000..60e747f0 --- /dev/null +++ b/packages/cli/src/commands/bundle/BundleCommand.ts @@ -0,0 +1,57 @@ +import fs from 'node:fs' +import { Command } from 'commander' +import kleur from 'kleur' +import type { OpenAPI } from 'openapi-types' +import { loadOpenApiFile, useGivenFileOrConfiguration } from '../../utils' + +export function BundleCommand() { + const cmd = new Command('bundle') + + cmd.description('Resolve all references in an OpenAPI file') + cmd.argument('[file]', 'file to bundle') + cmd.option('-o, --output ', 'output file') + cmd.action(async (fileArgument: string) => { + const { output } = cmd.opts() + + const startTime = performance.now() + + const file = useGivenFileOrConfiguration(fileArgument) + + const newContent = (await loadOpenApiFile(file)) + .specification as OpenAPI.Document + + // Replace file content with newContent + const cache = [] + const json = JSON.stringify( + newContent, + (key, value) => { + if (typeof value === 'object' && value !== null) { + if (cache.indexOf(value) !== -1) { + // Circular reference found, discard key + return + } + // Store value in our collection + cache.push(value) + } + return value + }, + 2, + ) + + fs.writeFileSync(output ?? file, json, 'utf8') + + const endTime = performance.now() + + console.log( + kleur.green('OpenAPI Schema bundled'), + kleur.grey( + `in ${kleur.white( + `${kleur.bold(`${Math.round(endTime - startTime)}`)} ms`, + )}`, + ), + ) + console.log() + }) + + return cmd +} diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 85e19026..f14b05f9 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -1,3 +1,4 @@ +export * from './bundle/BundleCommand' export * from './format/FormatCommand' export * from './init/InitCommand' export * from './mock/MockCommand' diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 374fc590..c50025b3 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -3,6 +3,7 @@ import { Command } from 'commander' import { version } from '../package.json' import { + BundleCommand, FormatCommand, InitCommand, MockCommand, @@ -23,6 +24,7 @@ program.addCommand(InitCommand(), { }) program.addCommand(FormatCommand()) program.addCommand(ValidateCommand()) +program.addCommand(BundleCommand()) program.addCommand(ReferenceCommand()) program.addCommand(MockCommand()) program.addCommand(ShareCommand())