@cerios/openapi-to-zod 1.0.0 → 1.1.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.
- package/README.md +238 -4
- package/dist/cli.js +173 -35
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +180 -35
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +168 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +168 -26
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.mts +53 -2
- package/dist/internal.d.ts +53 -2
- package/dist/internal.js +117 -2
- package/dist/internal.js.map +1 -1
- package/dist/internal.mjs +114 -2
- package/dist/internal.mjs.map +1 -1
- package/dist/{types-BjoP91vk.d.mts → types-CI48CjiU.d.mts} +43 -1
- package/dist/{types-BjoP91vk.d.ts → types-CI48CjiU.d.ts} +43 -1
- package/package.json +6 -2
package/dist/index.mjs
CHANGED
|
@@ -67,12 +67,24 @@ var ConfigurationError = class extends GeneratorError {
|
|
|
67
67
|
// src/openapi-generator.ts
|
|
68
68
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
69
69
|
import { dirname, normalize } from "path";
|
|
70
|
-
import { minimatch as
|
|
70
|
+
import { minimatch as minimatch3 } from "minimatch";
|
|
71
71
|
import { parse } from "yaml";
|
|
72
72
|
|
|
73
73
|
// src/utils/name-utils.ts
|
|
74
|
+
function sanitizeIdentifier(str) {
|
|
75
|
+
return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
|
|
76
|
+
}
|
|
74
77
|
function toCamelCase(str, options) {
|
|
75
|
-
|
|
78
|
+
const sanitized = sanitizeIdentifier(str);
|
|
79
|
+
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
80
|
+
let name;
|
|
81
|
+
if (words.length === 0) {
|
|
82
|
+
name = str.charAt(0).toLowerCase() + str.slice(1);
|
|
83
|
+
} else if (words.length === 1) {
|
|
84
|
+
name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
|
|
85
|
+
} else {
|
|
86
|
+
name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
87
|
+
}
|
|
76
88
|
if (options == null ? void 0 : options.prefix) {
|
|
77
89
|
const prefix = options.prefix.toLowerCase();
|
|
78
90
|
name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
|
|
@@ -85,12 +97,23 @@ function toCamelCase(str, options) {
|
|
|
85
97
|
}
|
|
86
98
|
function toPascalCase(str) {
|
|
87
99
|
const stringValue = String(str);
|
|
88
|
-
|
|
100
|
+
const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
|
|
101
|
+
if (isAlreadyValidCase) {
|
|
102
|
+
return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
|
|
103
|
+
}
|
|
104
|
+
const sanitized = sanitizeIdentifier(stringValue);
|
|
105
|
+
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
106
|
+
let result;
|
|
107
|
+
if (words.length === 0) {
|
|
108
|
+
result = "Value";
|
|
109
|
+
} else {
|
|
110
|
+
result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
111
|
+
}
|
|
89
112
|
if (/^\d/.test(result)) {
|
|
90
113
|
result = `N${result}`;
|
|
91
114
|
}
|
|
92
115
|
if (!result || /^_+$/.test(result)) {
|
|
93
|
-
|
|
116
|
+
return "Value";
|
|
94
117
|
}
|
|
95
118
|
return result;
|
|
96
119
|
}
|
|
@@ -102,9 +125,28 @@ function resolveRef(ref) {
|
|
|
102
125
|
// src/generators/enum-generator.ts
|
|
103
126
|
function generateEnum(name, values, options) {
|
|
104
127
|
const schemaName = `${toCamelCase(name, options)}Schema`;
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
|
|
128
|
+
const typeName = toPascalCase(name);
|
|
129
|
+
const allBooleans = values.every((v) => typeof v === "boolean");
|
|
130
|
+
if (allBooleans) {
|
|
131
|
+
const schemaCode2 = `export const ${schemaName} = z.boolean();`;
|
|
132
|
+
const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
133
|
+
return { schemaCode: schemaCode2, typeCode: typeCode2 };
|
|
134
|
+
}
|
|
135
|
+
const allStrings = values.every((v) => typeof v === "string");
|
|
136
|
+
if (allStrings) {
|
|
137
|
+
const enumValues = values.map((v) => `"${v}"`).join(", ");
|
|
138
|
+
const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
|
|
139
|
+
const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
140
|
+
return { schemaCode: schemaCode2, typeCode: typeCode2 };
|
|
141
|
+
}
|
|
142
|
+
const literalValues = values.map((v) => {
|
|
143
|
+
if (typeof v === "string") {
|
|
144
|
+
return `z.literal("${v}")`;
|
|
145
|
+
}
|
|
146
|
+
return `z.literal(${v})`;
|
|
147
|
+
}).join(", ");
|
|
148
|
+
const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
|
|
149
|
+
const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
108
150
|
return { schemaCode, typeCode };
|
|
109
151
|
}
|
|
110
152
|
|
|
@@ -113,7 +155,7 @@ function escapeDescription(str) {
|
|
|
113
155
|
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
114
156
|
}
|
|
115
157
|
function escapePattern(str) {
|
|
116
|
-
return str.replace(
|
|
158
|
+
return str.replace(/\//g, "\\/");
|
|
117
159
|
}
|
|
118
160
|
function escapeJSDoc(str) {
|
|
119
161
|
return str.replace(/\*\//g, "*\\/");
|
|
@@ -237,6 +279,64 @@ var LRUCache = class {
|
|
|
237
279
|
}
|
|
238
280
|
};
|
|
239
281
|
|
|
282
|
+
// src/utils/pattern-utils.ts
|
|
283
|
+
import { minimatch } from "minimatch";
|
|
284
|
+
function isValidGlobPattern(pattern) {
|
|
285
|
+
try {
|
|
286
|
+
new minimatch.Minimatch(pattern);
|
|
287
|
+
return true;
|
|
288
|
+
} catch {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function isGlobPattern(pattern) {
|
|
293
|
+
return /[*?[\]{}!]/.test(pattern);
|
|
294
|
+
}
|
|
295
|
+
function stripPrefix(input, pattern, ensureLeadingChar) {
|
|
296
|
+
if (!pattern) {
|
|
297
|
+
return input;
|
|
298
|
+
}
|
|
299
|
+
if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
|
|
300
|
+
console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
|
|
301
|
+
return input;
|
|
302
|
+
}
|
|
303
|
+
if (isGlobPattern(pattern)) {
|
|
304
|
+
let longestMatch = -1;
|
|
305
|
+
for (let i = 1; i <= input.length; i++) {
|
|
306
|
+
const testPrefix = input.substring(0, i);
|
|
307
|
+
if (minimatch(testPrefix, pattern)) {
|
|
308
|
+
longestMatch = i;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (longestMatch > 0) {
|
|
312
|
+
const stripped = input.substring(longestMatch);
|
|
313
|
+
if (ensureLeadingChar) {
|
|
314
|
+
if (stripped === "") {
|
|
315
|
+
return ensureLeadingChar;
|
|
316
|
+
}
|
|
317
|
+
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
318
|
+
return `${ensureLeadingChar}${stripped}`;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return stripped === "" && !ensureLeadingChar ? input : stripped;
|
|
322
|
+
}
|
|
323
|
+
return input;
|
|
324
|
+
}
|
|
325
|
+
if (input.startsWith(pattern)) {
|
|
326
|
+
const stripped = input.substring(pattern.length);
|
|
327
|
+
if (ensureLeadingChar) {
|
|
328
|
+
if (stripped === "") {
|
|
329
|
+
return ensureLeadingChar;
|
|
330
|
+
}
|
|
331
|
+
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
332
|
+
return `${ensureLeadingChar}${stripped}`;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return stripped;
|
|
336
|
+
}
|
|
337
|
+
return input;
|
|
338
|
+
}
|
|
339
|
+
|
|
240
340
|
// src/validators/array-validator.ts
|
|
241
341
|
function generateArrayValidation(schema, context) {
|
|
242
342
|
var _a;
|
|
@@ -1093,8 +1193,9 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1093
1193
|
}
|
|
1094
1194
|
(_a = this.context.schemaDependencies.get(currentSchema)) == null ? void 0 : _a.add(refName);
|
|
1095
1195
|
}
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1196
|
+
const strippedRefName = stripPrefix(resolvedRefName, this.context.stripSchemaPrefix);
|
|
1197
|
+
const schemaName = `${toCamelCase(strippedRefName, this.context.namingOptions)}Schema`;
|
|
1198
|
+
if (currentSchema && (refName === currentSchema || this.isCircularThroughAlias(currentSchema, refName))) {
|
|
1098
1199
|
const lazySchema = `z.lazy((): z.ZodTypeAny => ${schemaName})`;
|
|
1099
1200
|
return wrapNullable(lazySchema, nullable);
|
|
1100
1201
|
}
|
|
@@ -1106,9 +1207,25 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1106
1207
|
return wrapNullable(zodLiteral, nullable);
|
|
1107
1208
|
}
|
|
1108
1209
|
if (schema.enum) {
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
1210
|
+
const allBooleans = schema.enum.every((v) => typeof v === "boolean");
|
|
1211
|
+
if (allBooleans) {
|
|
1212
|
+
const zodBoolean = "z.boolean()";
|
|
1213
|
+
return wrapNullable(zodBoolean, nullable);
|
|
1214
|
+
}
|
|
1215
|
+
const allStrings = schema.enum.every((v) => typeof v === "string");
|
|
1216
|
+
if (allStrings) {
|
|
1217
|
+
const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
|
|
1218
|
+
const zodEnum = `z.enum([${enumValues}])`;
|
|
1219
|
+
return wrapNullable(zodEnum, nullable);
|
|
1220
|
+
}
|
|
1221
|
+
const literalValues = schema.enum.map((v) => {
|
|
1222
|
+
if (typeof v === "string") {
|
|
1223
|
+
return `z.literal("${v}")`;
|
|
1224
|
+
}
|
|
1225
|
+
return `z.literal(${v})`;
|
|
1226
|
+
}).join(", ");
|
|
1227
|
+
const zodUnion = `z.union([${literalValues}])`;
|
|
1228
|
+
return wrapNullable(zodUnion, nullable);
|
|
1112
1229
|
}
|
|
1113
1230
|
if (schema.allOf) {
|
|
1114
1231
|
let composition = generateAllOf(
|
|
@@ -1239,7 +1356,7 @@ _PropertyGenerator.INCLUSION_RULES = {
|
|
|
1239
1356
|
var PropertyGenerator = _PropertyGenerator;
|
|
1240
1357
|
|
|
1241
1358
|
// src/utils/operation-filters.ts
|
|
1242
|
-
import { minimatch } from "minimatch";
|
|
1359
|
+
import { minimatch as minimatch2 } from "minimatch";
|
|
1243
1360
|
function createFilterStatistics() {
|
|
1244
1361
|
return {
|
|
1245
1362
|
totalOperations: 0,
|
|
@@ -1258,7 +1375,7 @@ function matchesAnyPattern(value, patterns) {
|
|
|
1258
1375
|
if (!value) {
|
|
1259
1376
|
return false;
|
|
1260
1377
|
}
|
|
1261
|
-
return patterns.some((pattern) =>
|
|
1378
|
+
return patterns.some((pattern) => minimatch2(value, pattern));
|
|
1262
1379
|
}
|
|
1263
1380
|
function containsAny(arr, values) {
|
|
1264
1381
|
if (!values || values.length === 0) {
|
|
@@ -1394,6 +1511,7 @@ var OpenApiGenerator = class {
|
|
|
1394
1511
|
schemaType: options.schemaType || "all",
|
|
1395
1512
|
prefix: options.prefix,
|
|
1396
1513
|
suffix: options.suffix,
|
|
1514
|
+
stripSchemaPrefix: options.stripSchemaPrefix,
|
|
1397
1515
|
showStats: (_c = options.showStats) != null ? _c : true,
|
|
1398
1516
|
request: options.request,
|
|
1399
1517
|
response: options.response,
|
|
@@ -1470,7 +1588,8 @@ var OpenApiGenerator = class {
|
|
|
1470
1588
|
namingOptions: {
|
|
1471
1589
|
prefix: this.options.prefix,
|
|
1472
1590
|
suffix: this.options.suffix
|
|
1473
|
-
}
|
|
1591
|
+
},
|
|
1592
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix
|
|
1474
1593
|
});
|
|
1475
1594
|
}
|
|
1476
1595
|
/**
|
|
@@ -1483,6 +1602,9 @@ var OpenApiGenerator = class {
|
|
|
1483
1602
|
throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
|
|
1484
1603
|
}
|
|
1485
1604
|
for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
|
|
1605
|
+
if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1486
1608
|
this.generateComponentSchema(name, schema);
|
|
1487
1609
|
}
|
|
1488
1610
|
this.generateQueryParameterSchemas();
|
|
@@ -1504,9 +1626,11 @@ var OpenApiGenerator = class {
|
|
|
1504
1626
|
const typeCode = this.types.get(name);
|
|
1505
1627
|
if (schemaCode) {
|
|
1506
1628
|
output.push(schemaCode);
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1629
|
+
const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
1630
|
+
const typeName = toPascalCase(strippedName);
|
|
1631
|
+
if (!schemaCode.includes(`export type ${typeName}`)) {
|
|
1632
|
+
const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
1633
|
+
output.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
|
|
1510
1634
|
}
|
|
1511
1635
|
output.push("");
|
|
1512
1636
|
} else if (typeCode) {
|
|
@@ -1822,7 +1946,8 @@ var OpenApiGenerator = class {
|
|
|
1822
1946
|
const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
|
|
1823
1947
|
if (schema.enum) {
|
|
1824
1948
|
const jsdoc2 = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
|
|
1825
|
-
const
|
|
1949
|
+
const strippedName2 = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
1950
|
+
const { schemaCode, typeCode } = generateEnum(strippedName2, schema.enum, {
|
|
1826
1951
|
prefix: this.options.prefix,
|
|
1827
1952
|
suffix: this.options.suffix
|
|
1828
1953
|
});
|
|
@@ -1831,7 +1956,8 @@ ${typeCode}`;
|
|
|
1831
1956
|
this.schemas.set(name, enumSchemaCode);
|
|
1832
1957
|
return;
|
|
1833
1958
|
}
|
|
1834
|
-
const
|
|
1959
|
+
const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
1960
|
+
const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
1835
1961
|
const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
|
|
1836
1962
|
if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref) {
|
|
1837
1963
|
const refName = resolveRef(schema.allOf[0].$ref);
|
|
@@ -1847,7 +1973,8 @@ ${typeCode}`;
|
|
|
1847
1973
|
namingOptions: {
|
|
1848
1974
|
prefix: this.options.prefix,
|
|
1849
1975
|
suffix: this.options.suffix
|
|
1850
|
-
}
|
|
1976
|
+
},
|
|
1977
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix
|
|
1851
1978
|
});
|
|
1852
1979
|
const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
|
|
1853
1980
|
const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
|
|
@@ -1966,7 +2093,7 @@ ${propsCode}
|
|
|
1966
2093
|
const headerLower = headerName.toLowerCase();
|
|
1967
2094
|
return ignorePatterns.some((pattern) => {
|
|
1968
2095
|
const patternLower = pattern.toLowerCase();
|
|
1969
|
-
return
|
|
2096
|
+
return minimatch3(headerLower, patternLower);
|
|
1970
2097
|
});
|
|
1971
2098
|
}
|
|
1972
2099
|
/**
|
|
@@ -2049,12 +2176,27 @@ ${propsCode}
|
|
|
2049
2176
|
generateQueryParamType(schema, param) {
|
|
2050
2177
|
if (schema.$ref) {
|
|
2051
2178
|
const refName = resolveRef(schema.$ref);
|
|
2052
|
-
const
|
|
2179
|
+
const strippedRefName = stripPrefix(refName, this.options.stripSchemaPrefix);
|
|
2180
|
+
const schemaName = toCamelCase(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
|
|
2053
2181
|
return `${schemaName}Schema`;
|
|
2054
2182
|
}
|
|
2055
2183
|
if (schema.enum) {
|
|
2056
|
-
const
|
|
2057
|
-
|
|
2184
|
+
const allBooleans = schema.enum.every((v) => typeof v === "boolean");
|
|
2185
|
+
if (allBooleans) {
|
|
2186
|
+
return "z.boolean()";
|
|
2187
|
+
}
|
|
2188
|
+
const allStrings = schema.enum.every((v) => typeof v === "string");
|
|
2189
|
+
if (allStrings) {
|
|
2190
|
+
const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
|
|
2191
|
+
return `z.enum([${enumValues}])`;
|
|
2192
|
+
}
|
|
2193
|
+
const literalValues = schema.enum.map((v) => {
|
|
2194
|
+
if (typeof v === "string") {
|
|
2195
|
+
return `z.literal("${v}")`;
|
|
2196
|
+
}
|
|
2197
|
+
return `z.literal(${v})`;
|
|
2198
|
+
}).join(", ");
|
|
2199
|
+
return `z.union([${literalValues}])`;
|
|
2058
2200
|
}
|
|
2059
2201
|
const type = schema.type;
|
|
2060
2202
|
if (type === "string") {
|