jszy-swagger-doc-generator 1.4.0 → 1.5.0
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.
- package/README.md +327 -113
- package/dist/cli.js +34 -1
- package/dist/cli.js.map +1 -1
- package/dist/generators/swagger.generator.d.ts +90 -0
- package/dist/generators/swagger.generator.js +626 -0
- package/dist/generators/swagger.generator.js.map +1 -0
- package/dist/helpers/handlebars.helpers.d.ts +4 -0
- package/dist/helpers/handlebars.helpers.js +92 -0
- package/dist/helpers/handlebars.helpers.js.map +1 -0
- package/dist/helpers/string.helpers.d.ts +8 -0
- package/dist/helpers/string.helpers.js +25 -0
- package/dist/helpers/string.helpers.js.map +1 -0
- package/dist/helpers/template.helpers.d.ts +4 -0
- package/dist/helpers/template.helpers.js +105 -0
- package/dist/helpers/template.helpers.js.map +1 -0
- package/dist/helpers/type.helpers.d.ts +18 -0
- package/dist/helpers/type.helpers.js +229 -0
- package/dist/helpers/type.helpers.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +432 -159
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +97 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +1 -1
- package/src/cli.ts +35 -1
- package/src/generators/swagger.generator.ts +664 -0
- package/src/helpers/template.helpers.ts +72 -0
- package/src/helpers/type.helpers.ts +232 -0
- package/src/index.ts +465 -162
- package/src/types.ts +98 -0
- package/templates/hooks/individual-hook.hbs +55 -0
- package/templates/hooks/react-hook.hbs +14 -0
- package/templates/types/type-definition.hbs +5 -0
- package/test-openapi-swagger.json +454 -0
|
@@ -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
|
+
}
|