jszy-swagger-doc-generator 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,72 @@
1
+ import * as fs from 'fs';
2
+ import * as Handlebars from 'handlebars';
3
+
4
+ // Register helpers when module loads
5
+ (function() {
6
+ // Helper to convert to camelCase
7
+ Handlebars.registerHelper('camelCase', function(str: any) {
8
+ if (typeof str !== 'string') {
9
+ return '';
10
+ }
11
+ return str
12
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
13
+ return index === 0 ? word.toLowerCase() : word.toUpperCase();
14
+ })
15
+ .replace(/\s+/g, '');
16
+ });
17
+
18
+ // Helper to convert to PascalCase
19
+ Handlebars.registerHelper('pascalCase', function(str: any) {
20
+ if (typeof str !== 'string') {
21
+ return '';
22
+ }
23
+ return str
24
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
25
+ return index === 0 ? word.toUpperCase() : word.toUpperCase();
26
+ })
27
+ .replace(/\s+/g, '');
28
+ });
29
+
30
+ // Helper to check if array has elements
31
+ Handlebars.registerHelper('hasElements', function(arr: any[]) {
32
+ return arr && arr.length > 0;
33
+ });
34
+
35
+ // Helper for conditional rendering
36
+ Handlebars.registerHelper('ifCond', function (this: any, v1: any, operator: string, v2: any, options: any) {
37
+ switch (operator) {
38
+ case '==':
39
+ return v1 == v2 ? options.fn(this) : options.inverse(this);
40
+ case '===':
41
+ return v1 === v2 ? options.fn(this) : options.inverse(this);
42
+ case '<':
43
+ return v1 < v2 ? options.fn(this) : options.inverse(this);
44
+ case '<=':
45
+ return v1 <= v2 ? options.fn(this) : options.inverse(this);
46
+ case '>':
47
+ return v1 > v2 ? options.fn(this) : options.inverse(this);
48
+ case '>=':
49
+ return v1 >= v2 ? options.fn(this) : options.inverse(this);
50
+ case '&&':
51
+ return v1 && v2 ? options.fn(this) : options.inverse(this);
52
+ case '||':
53
+ return v1 || v2 ? options.fn(this) : options.inverse(this);
54
+ default:
55
+ return options.inverse(this);
56
+ }
57
+ });
58
+ })();
59
+
60
+ /**
61
+ * Compiles and renders a Handlebars template
62
+ */
63
+ export function compileTemplate(templatePath: string, context: any): string {
64
+ try {
65
+ const templateSource = fs.readFileSync(templatePath, 'utf8');
66
+ const template = Handlebars.compile(templateSource);
67
+ return template(context);
68
+ } catch (error) {
69
+ console.error(`Error compiling template ${templatePath}:`, error);
70
+ throw error;
71
+ }
72
+ }
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Converts OpenAPI types to TypeScript types
3
+ */
4
+ export function convertTypeToTs(typeDef: any, schemaComponents: { [key: string]: any }): string {
5
+ if (!typeDef) return 'any';
6
+
7
+ if (typeDef.$ref) {
8
+ // Extract the type name from the reference
9
+ const refTypeName = typeDef.$ref.split('/').pop();
10
+ return refTypeName || 'any';
11
+ }
12
+
13
+ // Handle allOf (used for composition/references) - combine all properties
14
+ if (typeDef.allOf && Array.isArray(typeDef.allOf)) {
15
+ const combinedProperties: any = {};
16
+ const refTypes: string[] = [];
17
+
18
+ for (const item of typeDef.allOf) {
19
+ if (item.$ref) {
20
+ // Extract the type name from the reference
21
+ const refTypeName = item.$ref.split('/').pop();
22
+ if (refTypeName) {
23
+ refTypes.push(refTypeName);
24
+ }
25
+ } else if (item.type === 'object' && item.properties) {
26
+ // Combine properties from inline object definitions
27
+ Object.assign(combinedProperties, item.properties);
28
+ }
29
+ }
30
+
31
+ if (refTypes.length > 0 && Object.keys(combinedProperties).length > 0) {
32
+ // We have both references and inline properties
33
+ const inlineDef = {
34
+ type: 'object',
35
+ properties: combinedProperties,
36
+ required: typeDef.required
37
+ };
38
+ const inlineType = convertTypeToTs(inlineDef, schemaComponents);
39
+ return `${refTypes.join(' & ')} & ${inlineType}`;
40
+ } else if (refTypes.length > 0) {
41
+ // Only references
42
+ return refTypes.join(' & ');
43
+ } else if (Object.keys(combinedProperties).length > 0) {
44
+ // Only inline properties
45
+ return convertTypeToTs({
46
+ type: 'object',
47
+ properties: combinedProperties,
48
+ required: typeDef.required
49
+ }, schemaComponents);
50
+ } else {
51
+ return 'any';
52
+ }
53
+ }
54
+
55
+ // Handle oneOf (union types)
56
+ if (typeDef.oneOf && Array.isArray(typeDef.oneOf)) {
57
+ return typeDef.oneOf.map((item: any) => {
58
+ if (item.$ref) {
59
+ return item.$ref.split('/').pop();
60
+ } else if (item.type) {
61
+ return convertTypeToTs(item, schemaComponents);
62
+ }
63
+ return 'any';
64
+ }).filter(Boolean).join(' | ') || 'any';
65
+ }
66
+
67
+ // Handle anyOf (union types)
68
+ if (typeDef.anyOf && Array.isArray(typeDef.anyOf)) {
69
+ return typeDef.anyOf.map((item: any) => {
70
+ if (item.$ref) {
71
+ return item.$ref.split('/').pop();
72
+ } else if (item.type) {
73
+ return convertTypeToTs(item, schemaComponents);
74
+ }
75
+ return 'any';
76
+ }).filter(Boolean).join(' | ') || 'any';
77
+ }
78
+
79
+ if (Array.isArray(typeDef.type)) {
80
+ // Handle union types like ["string", "null"]
81
+ if (typeDef.type.includes('null')) {
82
+ const nonNullTypes = typeDef.type.filter((t: string) => t !== 'null');
83
+ if (nonNullTypes.length === 1) {
84
+ return `${convertTypeToTs({...typeDef, type: nonNullTypes[0]}, schemaComponents)} | null`;
85
+ } else {
86
+ // Handle complex union types with null
87
+ const nonNullTypeStr = nonNullTypes
88
+ .map((t: string) => convertTypeToTs({...typeDef, type: t}, schemaComponents))
89
+ .join(' | ');
90
+ return `${nonNullTypeStr} | null`;
91
+ }
92
+ }
93
+ // Handle other array type unions
94
+ return typeDef.type
95
+ .map((t: string) => convertTypeToTs({...typeDef, type: t}, schemaComponents))
96
+ .join(' | ') || 'any';
97
+ }
98
+
99
+ switch (typeDef.type) {
100
+ case 'string':
101
+ if (typeDef.enum) {
102
+ return `"${typeDef.enum.join('" | "')}"`;
103
+ }
104
+ if (typeDef.format === 'date' || typeDef.format === 'date-time') {
105
+ return 'string';
106
+ }
107
+ return 'string';
108
+ case 'integer':
109
+ case 'number':
110
+ return 'number';
111
+ case 'boolean':
112
+ return 'boolean';
113
+ case 'array':
114
+ if (typeDef.items) {
115
+ return `${convertTypeToTs(typeDef.items, schemaComponents)}[]`;
116
+ }
117
+ return 'any[]';
118
+ case 'object':
119
+ if (typeDef.properties) {
120
+ // Inline object definition
121
+ const fields = Object.entries(typeDef.properties)
122
+ .map(([propName, propSchema]: [string, any]) => {
123
+ const required = typeDef.required && typeDef.required.includes(propName);
124
+ const optional = !required ? '?' : '';
125
+ const type = convertTypeToTs(propSchema, schemaComponents);
126
+ // Get the description for JSDoc if available
127
+ const propDescription = propSchema.description || propSchema.title;
128
+ const jsDoc = propDescription ? ` /** ${propDescription} */\n` : '';
129
+ return `${jsDoc} ${propName}${optional}: ${type};`;
130
+ })
131
+ .join('\n');
132
+ return `{\n${fields}\n }`;
133
+ }
134
+ return 'Record<string, any>';
135
+ case 'null':
136
+ return 'null';
137
+ default:
138
+ return 'any';
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Generates a single TypeScript type definition
144
+ */
145
+ export function generateSingleTypeDefinition(typeName: string, schema: any, allSchemas: { [key: string]: any }): string {
146
+ if (schema.enum) {
147
+ // Enum type
148
+ return `export type ${typeName} = ${schema.enum.map((val: any) => `'${val}'`).join(' | ')};\n`;
149
+ }
150
+
151
+ if (schema.oneOf || schema.anyOf) {
152
+ // Union type or complex type (oneOf/anyOf)
153
+ const typeOption = schema.oneOf ? 'oneOf' : 'anyOf';
154
+ const types = schema[typeOption].map((item: any) => {
155
+ if (item.$ref) {
156
+ return item.$ref.split('/').pop();
157
+ } else if (item.type) {
158
+ return convertTypeToTs(item, allSchemas);
159
+ } else {
160
+ return 'any';
161
+ }
162
+ }).filter(Boolean);
163
+ return `export type ${typeName} = ${types.join(' | ')};\n`;
164
+ }
165
+
166
+ if (schema.allOf) {
167
+ // Handle allOf - composition of multiple schemas
168
+ const allParts: string[] = [];
169
+ for (const part of schema.allOf) {
170
+ if (part.$ref) {
171
+ const refTypeName = part.$ref.split('/').pop();
172
+ if (refTypeName) {
173
+ allParts.push(refTypeName);
174
+ }
175
+ } else if (part.type === 'object' && part.properties) {
176
+ // Create a temporary interface for inline object
177
+ const inlineInterface = generateInlineObjectInterface(part, `${typeName}Inline`, allSchemas);
178
+ allParts.push(inlineInterface);
179
+ }
180
+ }
181
+ return `export type ${typeName} = ${allParts.join(' & ')};\n`;
182
+ }
183
+
184
+ if (schema.type === 'object') {
185
+ // Object type
186
+ let result = `export interface ${typeName} {\n`;
187
+
188
+ if (schema.properties) {
189
+ Object.entries(schema.properties).forEach(([propName, propSchema]: [string, any]) => {
190
+ const required = schema.required && schema.required.includes(propName);
191
+ const optional = !required ? '?' : '';
192
+
193
+ // Add JSDoc comment if available (using title or description)
194
+ const jsDoc = propSchema.title || propSchema.description;
195
+ if (jsDoc) {
196
+ result += ` /** ${jsDoc} */\n`;
197
+ }
198
+
199
+ result += ` ${propName}${optional}: ${convertTypeToTs(propSchema, allSchemas)};\n`;
200
+ });
201
+ }
202
+
203
+ result += '}\n';
204
+ return result;
205
+ }
206
+
207
+ // For other types (string, number, etc.) that might have additional properties
208
+ return `export type ${typeName} = ${convertTypeToTs(schema, allSchemas)};\n`;
209
+ }
210
+
211
+ /**
212
+ * Generates an inline object interface for allOf composition
213
+ */
214
+ export function generateInlineObjectInterface(schema: any, tempName: string, allSchemas: { [key: string]: any }): string {
215
+ if (!schema.properties) return 'any';
216
+
217
+ let result = '{\n';
218
+ Object.entries(schema.properties).forEach(([propName, propSchema]: [string, any]) => {
219
+ const required = schema.required && schema.required.includes(propName);
220
+ const optional = !required ? '?' : '';
221
+
222
+ // Add JSDoc comment if available (using title or description)
223
+ const jsDoc = propSchema.title || propSchema.description;
224
+ if (jsDoc) {
225
+ result += ` /** ${jsDoc} */\n`;
226
+ }
227
+
228
+ result += ` ${propName}${optional}: ${convertTypeToTs(propSchema, allSchemas)};\n`;
229
+ });
230
+ result += ' }';
231
+ return result;
232
+ }