fork of hey-api/openapi-ts because I need some additional things
1import colors from 'ansi-colors';
2
3import type { IR } from '../ir/types';
4import { httpMethods } from '../openApi/shared/utils/operation';
5
6export interface PrintOptions {
7 /**
8 * Indentation depth for `JSON.stringify()` when printing objects.
9 *
10 * @default 2
11 */
12 depth?: number;
13 /**
14 * Which section of the IR to print. Use 'all' to print every section.
15 *
16 * @default 'all'
17 */
18 section?: keyof IR.Model | 'all';
19 /**
20 * How much detail to print.
21 *
22 * - `summary` → only keys, names, operationIds, status codes
23 * - `full` → dump full JSON structures
24 *
25 * @default 'summary'
26 */
27 verbosity?: 'full' | 'summary';
28}
29
30const indent = (level: number) => ' '.repeat(level);
31
32const log = (message: string, level?: number) => console.log(`${indent(level ?? 0)}${message}`);
33
34const print = (ir: IR.Model, options: PrintOptions = {}) => {
35 const { depth = 2, section = 'all', verbosity = 'summary' } = options;
36
37 const printObject = (
38 obj: unknown,
39 level: number,
40 kind: 'responses' | 'requestBody' | 'schema' | 'generic' = 'generic',
41 ) => {
42 if (verbosity === 'summary' && obj && typeof obj === 'object') {
43 if (kind === 'responses') {
44 const count = Object.keys(obj).length;
45 const noun = count === 1 ? 'code' : 'codes';
46 log(`responses: ${colors.yellow(`${count} ${noun}`)}`, level);
47 } else if (kind === 'requestBody') {
48 log(`requestBody: ${Object.keys(obj).join(', ')}`, level);
49 } else if (kind === 'schema') {
50 log(`schema keys: ${Object.keys(obj).join(', ')}`, level);
51 } else {
52 log(`keys: ${Object.keys(obj).join(', ')}`, level);
53 }
54 } else {
55 log(JSON.stringify(obj, null, depth), level);
56 }
57 };
58
59 const printPathItem = (
60 key: string,
61 item: IR.PathItemObject | IR.ReferenceObject,
62 base: number = 1,
63 ) => {
64 if ('$ref' in item) {
65 log(`${colors.cyan(key)} is a $ref → ${colors.yellow(item.$ref)}`, base);
66 return;
67 }
68
69 for (const method of Object.keys(item) as Array<keyof IR.PathItemObject>) {
70 if (!httpMethods.includes(method)) continue;
71
72 const operation = item[method]!;
73 log(
74 `${colors.green(method.toUpperCase())} ${colors.cyan(key)} (${colors.magenta(operation.operationId ?? '')})`,
75 base,
76 );
77
78 if (operation.body) printObject(operation.body, base + 1, 'requestBody');
79 if (operation.responses) printObject(operation.responses, base + 1, 'responses');
80 }
81 };
82
83 const sections =
84 section === 'all' ? (Object.keys(ir) as unknown as ReadonlyArray<keyof IR.Model>) : [section];
85
86 for (const section of sections) {
87 switch (section) {
88 case 'components':
89 if (ir.components?.schemas) {
90 log(`Components: ${Object.keys(ir.components.schemas).length} schemas`);
91 for (const [, schema] of Object.entries(ir.components.schemas)) {
92 printObject(schema, 1, 'schema');
93 }
94 }
95 break;
96 case 'paths': {
97 const paths = ir.paths || {};
98 log(`paths (${Object.keys(paths).length} items):`);
99 for (const [path, item] of Object.entries(paths)) {
100 printPathItem(path, item);
101 }
102 break;
103 }
104 case 'servers':
105 break;
106 case 'webhooks': {
107 const webhooks = ir.webhooks || {};
108 log(`webhooks (${Object.keys(webhooks).length} items):`);
109 for (const [path, item] of Object.entries(webhooks)) {
110 printPathItem(path, item);
111 }
112 break;
113 }
114 }
115 }
116};
117
118export const ir = {
119 print,
120} as const;