Skip to content

Commit e397e9c

Browse files
committed
process more complex names
1 parent 05bf95b commit e397e9c

File tree

9 files changed

+93960
-15
lines changed

9 files changed

+93960
-15
lines changed

__fixtures__/openapi/swagger.json

Lines changed: 80661 additions & 0 deletions
Large diffs are not rendered by default.

__fixtures__/output/swagger.overrides.ts

Lines changed: 3285 additions & 0 deletions
Large diffs are not rendered by default.

__fixtures__/output/swagger.ts

Lines changed: 3277 additions & 0 deletions
Large diffs are not rendered by default.

__tests__/__snapshots__/openapi.overrides.test.ts.snap

Lines changed: 3289 additions & 0 deletions
Large diffs are not rendered by default.

__tests__/__snapshots__/openapi.test.ts.snap

Lines changed: 3281 additions & 0 deletions
Large diffs are not rendered by default.

__tests__/openapi.overrides.test.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { writeFileSync } from 'fs';
2+
3+
import schema from '../__fixtures__/openapi/swagger.json';
4+
import { generateTypeScript } from '../src';
5+
6+
const myschema = {
7+
title: 'Kubernetes',
8+
definitions: schema.definitions
9+
};
10+
11+
const overrides = {
12+
'io.k8s.apimachinery.pkg.util.intstr.IntOrString': {
13+
"anyOf": [
14+
{
15+
"type": "integer"
16+
},
17+
{
18+
"type": "string",
19+
}
20+
]
21+
},
22+
'io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps': {
23+
"anyOf": [
24+
{
25+
"type": "object",
26+
"properties": {
27+
// Define the properties of JSONSchemaProps here
28+
"title": {
29+
"type": "string"
30+
},
31+
"type": {
32+
"type": "string"
33+
}
34+
},
35+
"required": ["title", "type"],
36+
"additionalProperties": true
37+
},
38+
{
39+
"type": "array",
40+
"items": {
41+
"type": "object"
42+
}
43+
}
44+
]
45+
},
46+
'io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool': {
47+
"anyOf": [
48+
{
49+
"type": "object",
50+
"properties": {
51+
// Define the properties of JSONSchemaProps here
52+
"title": {
53+
"type": "string"
54+
},
55+
"type": {
56+
"type": "string"
57+
}
58+
},
59+
"required": ["title", "type"],
60+
"additionalProperties": true
61+
},
62+
{
63+
"type": "boolean"
64+
}
65+
]
66+
},
67+
'io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrStringArray': {
68+
"anyOf": [
69+
{
70+
"type": "object",
71+
"properties": {
72+
// Define the properties of JSONSchemaProps here
73+
"title": {
74+
"type": "string"
75+
},
76+
"type": {
77+
"type": "string"
78+
}
79+
},
80+
"required": ["title", "type"],
81+
"additionalProperties": true
82+
},
83+
{
84+
"type": "array",
85+
"items": {
86+
"type": "string"
87+
}
88+
}
89+
]
90+
},
91+
'io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSON': {
92+
"type": "object"
93+
},
94+
'io.k8s.apimachinery.pkg.apis.meta.v1.Time': {
95+
"type": "object"
96+
},
97+
'io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime': {
98+
"type": "object"
99+
},
100+
'io.k8s.apimachinery.pkg.api.resource.Quantity': {
101+
"type": "object"
102+
}
103+
};
104+
105+
it('swagger', () => {
106+
const code = generateTypeScript(myschema as any, {
107+
overrides
108+
});
109+
expect(code).toMatchSnapshot();
110+
writeFileSync(__dirname + '/../__fixtures__/output/swagger.overrides.ts', code);
111+
});

__tests__/openapi.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { writeFileSync } from 'fs';
2+
3+
import schema from '../__fixtures__/openapi/swagger.json';
4+
import { generateTypeScript } from '../src';
5+
6+
const myschema = {
7+
title: 'Kubernetes',
8+
definitions: schema.definitions
9+
};
10+
11+
it('swagger', () => {
12+
const code = generateTypeScript(myschema as any, {
13+
14+
});
15+
expect(code).toMatchSnapshot();
16+
writeFileSync(__dirname + '/../__fixtures__/output/swagger.ts', code);
17+
});

src/context.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import type { JSONSchema } from "./types";
21
import deepmerge from 'deepmerge';
32

3+
import type { JSONSchema } from "./types";
4+
5+
export interface SchemaDefinitionOverrides {
6+
[key: string]: JSONSchema
7+
}
48
export interface SchemaTSOptions {
59
useSingleQuotes: boolean;
610
camelCase?: boolean; // defaults to false
711
camelCaseFn?: (str: string) => string; // optional function to convert keys to camelCase
812
strictTypeSafety: boolean; // true uses { [k: string]: unknown; }, false uses any
13+
overrides?: SchemaDefinitionOverrides;
914
}
1015

1116
export interface SchemaTSContextI {

src/schema.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import { SchemaTSContext, type SchemaTSOptions } from "./context";
55
import type { JSONSchema } from "./types";
66
import { isValidIdentifier, isValidIdentifierCamelized, toCamelCase, toPascalCase } from "./utils";
77

8+
const safeProperty = (str: string): string => {
9+
if (str.match(/\./)) return str.replace(/\./g, '_');
10+
return str;
11+
};
12+
813
const identifier = (name: string, typeAnnotation: t.TSTypeAnnotation) => {
914
const i = t.identifier(name);
1015
i.typeAnnotation = typeAnnotation;
@@ -32,7 +37,7 @@ export function generateTypeScript(schema: JSONSchema, options?: Partial<SchemaT
3237
// Process both $defs and definitions
3338
const definitions = schema.$defs || schema.definitions || {};
3439
for (const key in definitions) {
35-
interfaces.push(createInterfaceDeclaration(ctx, toPascalCase(key), definitions[key]));
40+
interfaces.push(createInterfaceDeclaration(ctx, key, definitions[key]));
3641
}
3742
} catch (e) {
3843
console.error('Error processing interfaces');
@@ -45,7 +50,7 @@ export function generateTypeScript(schema: JSONSchema, options?: Partial<SchemaT
4550
console.error('schema or options require a title');
4651
return ''; // Ensure there's a return on error condition
4752
}
48-
interfaces.push(createInterfaceDeclaration(ctx, toPascalCase(title), schema));
53+
interfaces.push(createInterfaceDeclaration(ctx, title, schema));
4954
return generate(t.file(t.program(interfaces))).code;
5055
}
5156

@@ -94,14 +99,14 @@ function createInterfaceDeclaration(
9499
const combinedType = types.length > 1 ? t.tsUnionType(types) : types[0];
95100

96101
// Create a type alias instead of an interface if we're only handling these constructs
97-
const typeAlias = t.tsTypeAliasDeclaration(t.identifier(name), null, combinedType);
102+
const typeAlias = t.tsTypeAliasDeclaration(t.identifier(toPascalCase(safeProperty(name))), null, combinedType);
98103
return t.exportNamedDeclaration(typeAlias);
99104
}
100105

101106
// Finally, create the interface declaration if there are any body elements
102107
if (bodyElements.length > 0) {
103108
const interfaceDeclaration = t.tsInterfaceDeclaration(
104-
t.identifier(name),
109+
t.identifier(toPascalCase(safeProperty(name))),
105110
null,
106111
[],
107112
t.tsInterfaceBody(bodyElements)
@@ -110,12 +115,21 @@ function createInterfaceDeclaration(
110115
}
111116

112117
if (schema.type) {
113-
return t.exportNamedDeclaration(t.tsTypeAliasDeclaration(t.identifier(name), null, getTypeForProp(ctx, schema, [], schema)));
118+
return t.exportNamedDeclaration(t.tsTypeAliasDeclaration(t.identifier(toPascalCase(safeProperty(name))), null, getTypeForProp(ctx, schema, [], schema)));
119+
}
120+
121+
console.log(name);
122+
if (ctx.options.overrides && Object.prototype.hasOwnProperty.call(ctx.options.overrides, name)) {
123+
return t.exportNamedDeclaration(t.tsTypeAliasDeclaration(t.identifier(toPascalCase(safeProperty(name))), null,
124+
getTypeForProp(ctx, ctx.options.overrides[name], [], schema)
125+
));
114126
}
115127

128+
129+
116130
// Fallback to exporting a basic type if nothing else is possible
117131
console.warn(`No properties or type definitions found for ${name}, defaulting to 'any'.`);
118-
return t.exportNamedDeclaration(t.tsTypeAliasDeclaration(t.identifier(name), null, t.tsAnyKeyword()));
132+
return t.exportNamedDeclaration(t.tsTypeAliasDeclaration(t.identifier(toPascalCase(safeProperty(name))), null, t.tsAnyKeyword()));
119133
}
120134

121135

@@ -133,7 +147,7 @@ function createPropertySignature(
133147
let identifier: t.Identifier | t.StringLiteral;
134148
let isIdent: boolean;
135149
if (ctx.options.camelCase) {
136-
isIdent = isValidIdentifierCamelized(key);
150+
isIdent = isValidIdentifierCamelized(key);
137151
} else {
138152
isIdent = isValidIdentifier(key);
139153
}
@@ -162,8 +176,8 @@ function getTypeForProp(ctx: SchemaTSContext, prop: JSONSchema, required: string
162176

163177
if (prop.type) {
164178
if (Array.isArray(prop.type)) {
165-
const arrayType = prop.type.map(type => getTypeForProp(ctx, {type, items: prop.items}, [], schema));
166-
return t.tsUnionType(arrayType);
179+
const arrayType = prop.type.map(type => getTypeForProp(ctx, { type, items: prop.items }, [], schema));
180+
return t.tsUnionType(arrayType);
167181
}
168182

169183
switch (prop.type) {
@@ -219,12 +233,17 @@ function getTypeForProp(ctx: SchemaTSContext, prop: JSONSchema, required: string
219233

220234
}
221235

222-
function getTypeReferenceFromSchema(schema: JSONSchema, definitionName: string): t.TSType | null {
236+
function getTypeReferenceFromSchema(ctx: SchemaTSContext, schema: JSONSchema, definitionName: string): t.TSType | null {
223237
if (definitionName) {
238+
239+
// if (ctx.options.overrides && Object.prototype.hasOwnProperty.call(ctx.options.overrides, definitionName)) {
240+
// return getTypeForProp(ctx, ctx.options.overrides[definitionName], [], schema);
241+
// }
242+
224243
if (schema.$defs && schema.$defs[definitionName]) {
225-
return t.tsTypeReference(t.identifier(toPascalCase(definitionName)));
244+
return t.tsTypeReference(t.identifier(toPascalCase(safeProperty(definitionName))));
226245
} else if (schema.definitions && schema.definitions[definitionName]) {
227-
return t.tsTypeReference(t.identifier(toPascalCase(definitionName)));
246+
return t.tsTypeReference(t.identifier(toPascalCase(safeProperty(definitionName))));
228247
}
229248
}
230249
return null; // Return null if no type reference is found
@@ -236,13 +255,13 @@ function resolveRefType(ctx: SchemaTSContext, ref: string, schema: JSONSchema):
236255
const definitionName = path.pop();
237256

238257
// Try to resolve the type reference from the local schema
239-
const localTypeReference = getTypeReferenceFromSchema(schema, definitionName);
258+
const localTypeReference = getTypeReferenceFromSchema(ctx, schema, definitionName);
240259
if (localTypeReference) {
241260
return localTypeReference;
242261
}
243262

244263
// Try to resolve the type reference from the root schema
245-
const rootTypeReference = getTypeReferenceFromSchema(ctx.root, definitionName);
264+
const rootTypeReference = getTypeReferenceFromSchema(ctx, ctx.root, definitionName);
246265
if (rootTypeReference) {
247266
return rootTypeReference;
248267
}

0 commit comments

Comments
 (0)