vovk-cli 0.0.1-draft.266 → 0.0.1-draft.268

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.
@@ -8,7 +8,7 @@ export namespace Mixins {
8
8
  <% if(segment.meta?.components?.schemas) { %>
9
9
  export namespace <%= t._.upperFirst(t._.camelCase(segment.segmentName)) %> {
10
10
  <% for (const [componentName, componentSchema] of Object.entries(segment.meta.components.schemas)) { %>
11
- <%- await t.compileJSONSchemaToTypeScriptType(componentSchema, componentName, segment.meta.components) %>
11
+ <%- t.compileJSONSchemaToTypeScriptType(componentSchema, componentName, segment.meta.components, { dontCreateRefTypes: true }) %>
12
12
  <% } %>
13
13
  }
14
14
  <% } %>
@@ -22,11 +22,11 @@ export namespace Types {
22
22
  export namespace <%= controllerSchema.rpcModuleName %> {
23
23
  <% for (const [handlerName, handlerSchema] of Object.entries(controllerSchema.handlers)) { %>
24
24
  export namespace <%= t._.upperFirst(handlerName) %> {
25
- <%- await t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.body, 'Body') %>
26
- <%- await t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.query, 'Query') %>
27
- <%- await t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.params, 'Params') %>
28
- <%- await t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.output, 'Output') %>
29
- <%- await t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.iteration, 'Iteration') %>
25
+ <%- t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.body, 'Body') %>
26
+ <%- t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.query, 'Query') %>
27
+ <%- t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.params, 'Params') %>
28
+ <%- t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.output, 'Output') %>
29
+ <%- t.compileJSONSchemaToTypeScriptType(handlerSchema.validation?.iteration, 'Iteration') %>
30
30
  }
31
31
  <% } %>
32
32
  }
@@ -1,3 +1,5 @@
1
- import { JSONSchema } from 'json-schema-to-typescript';
2
- import { OpenAPIObject } from 'openapi3-ts/oas31';
3
- export declare function compileJSONSchemaToTypeScriptType(schema: JSONSchema, typeName: string, components?: NonNullable<OpenAPIObject['components']>): Promise<string>;
1
+ import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
+ import type { JSONSchema7 } from 'json-schema';
3
+ export declare function compileJSONSchemaToTypeScriptType(schema: JSONSchema7, typeName: string, components?: NonNullable<OpenAPIObject['components']>, options?: {
4
+ dontCreateRefTypes?: boolean;
5
+ }): string;
@@ -1,39 +1,9 @@
1
- import { compile } from 'json-schema-to-typescript';
2
- function replaceUnresolvedRefs(schema, components) {
3
- if (schema === null || typeof schema !== 'object') {
4
- return schema;
5
- }
6
- if (typeof schema.$ref === 'string') {
7
- const refPath = schema.$ref.split('/');
8
- const componentName = refPath[refPath.length - 1];
9
- if (!(componentName in (components.schemas ?? schema.$defs ?? schema.definitions ?? {}))) {
10
- return {
11
- tsType: `unknown`,
12
- };
13
- }
14
- }
15
- if (Array.isArray(schema)) {
16
- return schema.map((item) => replaceUnresolvedRefs(item, components));
17
- }
18
- else {
19
- const result = {};
20
- for (const [key, value] of Object.entries(schema)) {
21
- result[key] = replaceUnresolvedRefs(value, components);
22
- }
23
- return result;
24
- }
25
- }
26
- export async function compileJSONSchemaToTypeScriptType(schema, typeName, components = {}) {
1
+ import { compileTs } from './compileTs.mjs';
2
+ export function compileJSONSchemaToTypeScriptType(schema, typeName, components = {}, options = {}) {
27
3
  if (!schema)
28
4
  return '';
29
5
  if ('tsType' in schema && typeof schema.tsType === 'string')
30
6
  return `export type ${typeName} = ${schema.tsType};\n`;
31
- schema = replaceUnresolvedRefs(schema, components);
32
- components = replaceUnresolvedRefs(components, components);
33
- // tsType attribute isn't working with objects that use $ref, so we need to handle it separately
34
- const tsType = await compile({ ...schema, components }, typeName, {
35
- bannerComment: schema.description ? `/**\n * ${schema.description}\n */` : '',
36
- ignoreMinAndMaxItems: true,
37
- });
7
+ const tsType = compileTs({ schema: { ...schema, components }, name: typeName, ...options });
38
8
  return tsType;
39
9
  }
@@ -0,0 +1,12 @@
1
+ import { JSONSchema7 } from 'json-schema';
2
+ import { OpenAPIObject } from 'openapi3-ts/oas31';
3
+ interface CompileOptions {
4
+ name: string;
5
+ schema: JSONSchema7 & {
6
+ components?: OpenAPIObject['components'];
7
+ };
8
+ refs?: Map<string, JSONSchema7>;
9
+ dontCreateRefTypes?: boolean;
10
+ }
11
+ export declare function compileTs(options: CompileOptions): string;
12
+ export {};
@@ -0,0 +1,245 @@
1
+ export function compileTs(options) {
2
+ const context = {
3
+ refs: options.refs || new Map(),
4
+ compiledRefs: new Map(),
5
+ refsInProgress: new Set(),
6
+ };
7
+ // Collect all definitions from the schema
8
+ collectDefinitions(options.schema, context.refs);
9
+ const mainType = compileSchema(options.schema, options.name, context);
10
+ // Compile all referenced types, unless dontCreateRefTypes is set
11
+ const compiledRefs = options.dontCreateRefTypes
12
+ ? ''
13
+ : Array.from(context.compiledRefs.entries())
14
+ .map(([, typeDecl]) => typeDecl)
15
+ .join('\n\n');
16
+ return compiledRefs
17
+ ? `${compiledRefs}\n\n${options.schema.description ? `/** ${options.schema.description} */\n` : ''}export type ${options.name} = ${mainType};`
18
+ : `${options.schema.description ? `/** ${options.schema.description} */\n` : ''}export type ${options.name} = ${mainType};`;
19
+ }
20
+ function collectDefinitions(schema, refs) {
21
+ // Collect from $defs
22
+ if (schema.$defs) {
23
+ Object.entries(schema.$defs).forEach(([key, def]) => {
24
+ if (typeof def === 'object') {
25
+ refs.set(`#/$defs/${key}`, def);
26
+ }
27
+ });
28
+ }
29
+ // Collect from definitions (older spec)
30
+ if (schema.definitions) {
31
+ Object.entries(schema.definitions).forEach(([key, def]) => {
32
+ if (typeof def === 'object') {
33
+ refs.set(`#/definitions/${key}`, def);
34
+ }
35
+ });
36
+ }
37
+ // Collect from components/schemas (OpenAPI spec)
38
+ if (schema?.components?.schemas) {
39
+ Object.entries(schema?.components?.schemas ?? {}).forEach(([key, def]) => {
40
+ if (typeof def === 'object') {
41
+ refs.set(`#/components/schemas/${key}`, def);
42
+ }
43
+ });
44
+ }
45
+ // Recursively collect from nested schemas
46
+ const schemasToProcess = [];
47
+ if (schema.properties) {
48
+ schemasToProcess.push(...Object.values(schema.properties).filter(isSchema));
49
+ }
50
+ if (schema.items) {
51
+ if (Array.isArray(schema.items)) {
52
+ schemasToProcess.push(...schema.items.filter(isSchema));
53
+ }
54
+ else if (isSchema(schema.items)) {
55
+ schemasToProcess.push(schema.items);
56
+ }
57
+ }
58
+ if (schema.additionalProperties && isSchema(schema.additionalProperties)) {
59
+ schemasToProcess.push(schema.additionalProperties);
60
+ }
61
+ if (schema.allOf)
62
+ schemasToProcess.push(...schema.allOf.filter(isSchema));
63
+ if (schema.anyOf)
64
+ schemasToProcess.push(...schema.anyOf.filter(isSchema));
65
+ if (schema.oneOf)
66
+ schemasToProcess.push(...schema.oneOf.filter(isSchema));
67
+ schemasToProcess.forEach((s) => collectDefinitions(s, refs));
68
+ }
69
+ function isSchema(value) {
70
+ return typeof value === 'object' && !Array.isArray(value);
71
+ }
72
+ function compileSchema(schema, name, context) {
73
+ if (typeof schema === 'boolean') {
74
+ return schema ? 'any' : 'never';
75
+ }
76
+ // Handle x-tsType extension
77
+ if ('x-tsType' in schema && typeof schema['x-tsType'] === 'string') {
78
+ return schema['x-tsType'];
79
+ }
80
+ // Handle $ref
81
+ if (schema.$ref) {
82
+ return handleRef(schema.$ref, context);
83
+ }
84
+ // Handle combinators
85
+ if (schema.allOf) {
86
+ return handleAllOf(schema.allOf, name, context);
87
+ }
88
+ if (schema.anyOf) {
89
+ return handleAnyOf(schema.anyOf, name, context);
90
+ }
91
+ if (schema.oneOf) {
92
+ return handleOneOf(schema.oneOf, name, context);
93
+ }
94
+ // Handle type-specific compilation
95
+ if (schema.enum) {
96
+ return handleEnum(schema.enum);
97
+ }
98
+ if (schema.const !== undefined) {
99
+ return handleConst(schema.const);
100
+ }
101
+ if (!schema.type) {
102
+ // No type specified, could be object
103
+ if (schema.properties || schema.additionalProperties) {
104
+ return handleObject(schema, name, context);
105
+ }
106
+ return 'any';
107
+ }
108
+ if (Array.isArray(schema.type)) {
109
+ return schema.type.map((t) => compileSchemaWithType({ ...schema, type: t }, name, context)).join(' | ');
110
+ }
111
+ return compileSchemaWithType(schema, name, context);
112
+ }
113
+ function compileSchemaWithType(schema, name, context) {
114
+ switch (schema.type) {
115
+ case 'null':
116
+ return 'null';
117
+ case 'boolean':
118
+ return 'boolean';
119
+ case 'string':
120
+ return 'string';
121
+ case 'number':
122
+ return 'number';
123
+ case 'integer':
124
+ return 'number';
125
+ case 'array':
126
+ return handleArray(schema, name, context);
127
+ case 'object':
128
+ return handleObject(schema, name, context);
129
+ default:
130
+ return 'any';
131
+ }
132
+ }
133
+ function handleRef(ref, context) {
134
+ const typeName = refToTypeName(ref);
135
+ // Check if we're already compiling this ref (circular reference)
136
+ if (context.refsInProgress.has(ref)) {
137
+ return typeName;
138
+ }
139
+ // Check if already compiled
140
+ if (context.compiledRefs.has(ref)) {
141
+ return typeName;
142
+ }
143
+ // Find the referenced schema
144
+ const referencedSchema = context.refs.get(ref);
145
+ if (!referencedSchema) {
146
+ return 'any'; // Reference not found
147
+ }
148
+ // Mark as in progress
149
+ context.refsInProgress.add(ref);
150
+ // Compile the referenced schema
151
+ const compiledType = compileSchema(referencedSchema, typeName, context);
152
+ const description = referencedSchema.description ? `/** ${referencedSchema.description} */\n` : '';
153
+ context.compiledRefs.set(ref, `${description}export type ${typeName} = ${compiledType};`);
154
+ // Mark as completed
155
+ context.refsInProgress.delete(ref);
156
+ return typeName;
157
+ }
158
+ function handleAllOf(schemas, name, context) {
159
+ const types = schemas.map((s, i) => compileSchema(s, `${name}AllOf${i}`, context));
160
+ // For allOf, we need to intersect types
161
+ // If they're all objects, we can merge them properly
162
+ const objectTypes = types.filter((t) => t.startsWith('{') && t.endsWith('}'));
163
+ if (objectTypes.length === types.length) {
164
+ // Merge object types
165
+ const merged = objectTypes.map((t) => t.slice(1, -1).trim()).filter((t) => t.length > 0);
166
+ return merged.length > 0 ? `{ ${merged.join('; ')} }` : '{}';
167
+ }
168
+ return types.join(' & ');
169
+ }
170
+ function handleAnyOf(schemas, name, context) {
171
+ const types = schemas.map((s, i) => compileSchema(s, `${name}AnyOf${i}`, context));
172
+ return types.join(' | ');
173
+ }
174
+ function handleOneOf(schemas, name, context) {
175
+ // For TypeScript, oneOf behaves like anyOf
176
+ const types = schemas.map((s, i) => compileSchema(s, `${name}OneOf${i}`, context));
177
+ return types.join(' | ');
178
+ }
179
+ function handleEnum(enumValues) {
180
+ return enumValues.map((v) => JSON.stringify(v)).join(' | ');
181
+ }
182
+ function handleConst(value) {
183
+ return JSON.stringify(value);
184
+ }
185
+ function handleArray(schema, name, context) {
186
+ if (!schema.items) {
187
+ return 'any[]';
188
+ }
189
+ if (Array.isArray(schema.items)) {
190
+ // Tuple (ignoring min/max as requested)
191
+ const types = schema.items.map((item, i) => compileSchema(item, `${name}Item${i}`, context));
192
+ return `[${types.join(', ')}]`;
193
+ }
194
+ const itemType = compileSchema(schema.items, `${name}Item`, context);
195
+ return `${wrapUnionType(itemType)}[]`;
196
+ }
197
+ function handleObject(schema, name, context) {
198
+ const props = [];
199
+ // Handle known properties
200
+ if (schema.properties) {
201
+ const required = new Set(schema.required || []);
202
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
203
+ if (!isSchema(propSchema))
204
+ continue;
205
+ const isRequired = required.has(propName);
206
+ const propType = compileSchema(propSchema, `${name}${capitalize(propName)}`, context);
207
+ const safePropName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName) ? propName : `"${propName}"`;
208
+ // Add JSDoc comment if description is present
209
+ const comment = propSchema.description ? `\n/** ${propSchema.description} */\n` : '';
210
+ props.push(`${comment}${safePropName}${isRequired ? '' : '?'}: ${propType}`);
211
+ }
212
+ }
213
+ // Handle additional properties
214
+ if (schema.additionalProperties === true) {
215
+ props.push('[key: string]: any');
216
+ }
217
+ else if (schema.additionalProperties && isSchema(schema.additionalProperties)) {
218
+ const additionalType = compileSchema(schema.additionalProperties, `${name}Additional`, context);
219
+ props.push(`[key: string]: ${additionalType}`);
220
+ }
221
+ // Handle pattern properties
222
+ if (schema.patternProperties) {
223
+ // For simplicity, treat pattern properties as string index signature
224
+ const patternTypes = Object.values(schema.patternProperties)
225
+ .filter(isSchema)
226
+ .map((s, i) => compileSchema(s, `${name}Pattern${i}`, context));
227
+ if (patternTypes.length > 0) {
228
+ props.push(`[key: string]: ${patternTypes.join(' | ')}`);
229
+ }
230
+ }
231
+ return props.length > 0 ? `{ ${props.join('; ')} }` : '{}';
232
+ }
233
+ function refToTypeName(ref) {
234
+ // Extract the last part of the reference as the type name
235
+ const parts = ref.split('/');
236
+ const name = parts[parts.length - 1];
237
+ return capitalize(name);
238
+ }
239
+ function capitalize(str) {
240
+ return str.charAt(0).toUpperCase() + str.slice(1);
241
+ }
242
+ function wrapUnionType(type) {
243
+ // Wrap union types in parentheses for array types
244
+ return type.includes('|') ? `(${type})` : type;
245
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk-cli",
3
- "version": "0.0.1-draft.266",
3
+ "version": "0.0.1-draft.268",
4
4
  "bin": {
5
5
  "vovk": "./dist/index.mjs"
6
6
  },