@zimic/http
Version:
Next-gen TypeScript-first HTTP utilities
115 lines (104 loc) • 4.74 kB
text/typescript
import color from 'picocolors';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { version } from '@@/package.json';
import { generateTypesFromOpenAPI } from '@/typegen';
import { logger } from '@/utils/logging';
import { usingElapsedTime, formatElapsedTime } from '@/utils/time';
async function runCLI() {
await yargs(hideBin(process.argv))
.scriptName('zimic-http')
.version(version)
.showHelpOnFail(false)
.strict()
.command('typegen', 'Generate types from schema sources.', (yargs) =>
yargs.demandCommand().command(
'openapi <input>',
'Generate types from an OpenAPI schema.',
(yargs) =>
yargs
.positional('input', {
type: 'string',
description:
'The path to a local OpenAPI schema file or an URL to fetch it. ' +
'Version 3 is supported as YAML or JSON.',
demandOption: true,
})
.option('output', {
type: 'string',
description:
'The path to write the generated types to. If not provided, the types will be written to stdout.',
alias: 'o',
})
.option('service-name', {
type: 'string',
description: 'The name of the service to use in the generated types.',
alias: 's',
demandOption: true,
})
.option('comments', {
type: 'boolean',
description: 'Whether to include comments in the generated types.',
alias: 'c',
default: true,
})
.option('prune', {
type: 'boolean',
description:
'Whether to remove unused operations and components from the generated types. This is useful for ' +
'reducing the size of the output file.',
alias: 'p',
default: true,
})
.option('filter', {
type: 'string',
array: true,
description: [
'One or more expressions filtering which endpoints to include. Filters must follow the format `<method> ' +
'<path>`, where:',
'- `<method>`: one HTTP method, a list of HTTP methods separated by commas, or `*` to match any HTTP ' +
'method;',
'- `<path>`: a literal path or a glob. `*` matches zero or more characters in a segment (except `/`), ' +
'while `**` matches zero or more characters across segments (may include `/`). For example, ' +
'`GET /users` matches a single method and path, while `* /users` matches any method to the `/users` ' +
'path; `GET /users*` matches any `GET` request whose path starts with `/users`, and `GET /users/**/*` ' +
'matches any `GET` request to any sub-path of `/users`.',
'Negative filters can be created by prefixing the expression with `!`. For example, `!GET /users` will ' +
'exclude paths matching `GET /users`.',
].join('\n'),
alias: 'f',
})
.option('filter-file', {
type: 'string',
description:
'A path to a file containing filter expressions. One expression is expected per line and the format ' +
'is the same as used in a `--filter` option. Comments are prefixed with `#`. A filter file can be ' +
'used alongside additional `--filter` expressions.',
alias: 'F',
}),
async (cliArguments) => {
const executionSummary = await usingElapsedTime(async () => {
await generateTypesFromOpenAPI({
input: cliArguments.input,
output: cliArguments.output,
serviceName: cliArguments.serviceName,
includeComments: cliArguments.comments,
prune: cliArguments.prune,
filters: cliArguments.filter,
filterFile: cliArguments.filterFile,
});
});
const outputFilePath = cliArguments.output;
const successMessage =
`${color.green(color.bold('✔'))} Generated ` +
`${outputFilePath ? color.green(outputFilePath) : `to ${color.yellow('stdout')}`} ${color.dim(
`(${formatElapsedTime(executionSummary.elapsedTime)})`,
)}`;
const hasWrittenToStdout = outputFilePath === undefined;
logger[hasWrittenToStdout ? 'warn' : 'info'](successMessage);
},
),
)
.parse();
}
export default runCLI;