@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/dist/cli.mjs CHANGED
@@ -5136,8 +5136,20 @@ var init_batch_executor = __esm({
5136
5136
  });
5137
5137
 
5138
5138
  // src/utils/name-utils.ts
5139
+ function sanitizeIdentifier(str) {
5140
+ return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
5141
+ }
5139
5142
  function toCamelCase(str, options) {
5140
- let name = str.charAt(0).toLowerCase() + str.slice(1);
5143
+ const sanitized = sanitizeIdentifier(str);
5144
+ const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
5145
+ let name;
5146
+ if (words.length === 0) {
5147
+ name = str.charAt(0).toLowerCase() + str.slice(1);
5148
+ } else if (words.length === 1) {
5149
+ name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
5150
+ } else {
5151
+ name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
5152
+ }
5141
5153
  if (options == null ? void 0 : options.prefix) {
5142
5154
  const prefix = options.prefix.toLowerCase();
5143
5155
  name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
@@ -5150,12 +5162,23 @@ function toCamelCase(str, options) {
5150
5162
  }
5151
5163
  function toPascalCase(str) {
5152
5164
  const stringValue = String(str);
5153
- let result = stringValue.replace(/[^a-zA-Z0-9_]+/g, "_").split(/[-_]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
5165
+ const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
5166
+ if (isAlreadyValidCase) {
5167
+ return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
5168
+ }
5169
+ const sanitized = sanitizeIdentifier(stringValue);
5170
+ const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
5171
+ let result;
5172
+ if (words.length === 0) {
5173
+ result = "Value";
5174
+ } else {
5175
+ result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
5176
+ }
5154
5177
  if (/^\d/.test(result)) {
5155
5178
  result = `N${result}`;
5156
5179
  }
5157
5180
  if (!result || /^_+$/.test(result)) {
5158
- result = "Value";
5181
+ return "Value";
5159
5182
  }
5160
5183
  return result;
5161
5184
  }
@@ -5173,9 +5196,28 @@ var init_name_utils = __esm({
5173
5196
  // src/generators/enum-generator.ts
5174
5197
  function generateEnum(name, values, options) {
5175
5198
  const schemaName = `${toCamelCase(name, options)}Schema`;
5176
- const enumValues = values.map((v) => `"${v}"`).join(", ");
5177
- const schemaCode = `export const ${schemaName} = z.enum([${enumValues}]);`;
5178
- const typeCode = `export type ${name} = z.infer<typeof ${schemaName}>;`;
5199
+ const typeName = toPascalCase(name);
5200
+ const allBooleans = values.every((v) => typeof v === "boolean");
5201
+ if (allBooleans) {
5202
+ const schemaCode2 = `export const ${schemaName} = z.boolean();`;
5203
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
5204
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
5205
+ }
5206
+ const allStrings = values.every((v) => typeof v === "string");
5207
+ if (allStrings) {
5208
+ const enumValues = values.map((v) => `"${v}"`).join(", ");
5209
+ const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
5210
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
5211
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
5212
+ }
5213
+ const literalValues = values.map((v) => {
5214
+ if (typeof v === "string") {
5215
+ return `z.literal("${v}")`;
5216
+ }
5217
+ return `z.literal(${v})`;
5218
+ }).join(", ");
5219
+ const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
5220
+ const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
5179
5221
  return { schemaCode, typeCode };
5180
5222
  }
5181
5223
  var init_enum_generator = __esm({
@@ -5191,7 +5233,7 @@ function escapeDescription(str) {
5191
5233
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
5192
5234
  }
5193
5235
  function escapePattern(str) {
5194
- return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
5236
+ return str.replace(/\//g, "\\/");
5195
5237
  }
5196
5238
  function escapeJSDoc(str) {
5197
5239
  return str.replace(/\*\//g, "*\\/");
@@ -5335,6 +5377,70 @@ var init_lru_cache = __esm({
5335
5377
  }
5336
5378
  });
5337
5379
 
5380
+ // src/utils/pattern-utils.ts
5381
+ import { minimatch } from "minimatch";
5382
+ function isValidGlobPattern(pattern) {
5383
+ try {
5384
+ new minimatch.Minimatch(pattern);
5385
+ return true;
5386
+ } catch {
5387
+ return false;
5388
+ }
5389
+ }
5390
+ function isGlobPattern(pattern) {
5391
+ return /[*?[\]{}!]/.test(pattern);
5392
+ }
5393
+ function stripPrefix(input, pattern, ensureLeadingChar) {
5394
+ if (!pattern) {
5395
+ return input;
5396
+ }
5397
+ if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
5398
+ console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
5399
+ return input;
5400
+ }
5401
+ if (isGlobPattern(pattern)) {
5402
+ let longestMatch = -1;
5403
+ for (let i = 1; i <= input.length; i++) {
5404
+ const testPrefix = input.substring(0, i);
5405
+ if (minimatch(testPrefix, pattern)) {
5406
+ longestMatch = i;
5407
+ }
5408
+ }
5409
+ if (longestMatch > 0) {
5410
+ const stripped = input.substring(longestMatch);
5411
+ if (ensureLeadingChar) {
5412
+ if (stripped === "") {
5413
+ return ensureLeadingChar;
5414
+ }
5415
+ if (!stripped.startsWith(ensureLeadingChar)) {
5416
+ return `${ensureLeadingChar}${stripped}`;
5417
+ }
5418
+ }
5419
+ return stripped === "" && !ensureLeadingChar ? input : stripped;
5420
+ }
5421
+ return input;
5422
+ }
5423
+ if (input.startsWith(pattern)) {
5424
+ const stripped = input.substring(pattern.length);
5425
+ if (ensureLeadingChar) {
5426
+ if (stripped === "") {
5427
+ return ensureLeadingChar;
5428
+ }
5429
+ if (!stripped.startsWith(ensureLeadingChar)) {
5430
+ return `${ensureLeadingChar}${stripped}`;
5431
+ }
5432
+ }
5433
+ return stripped;
5434
+ }
5435
+ return input;
5436
+ }
5437
+ var init_pattern_utils = __esm({
5438
+ "src/utils/pattern-utils.ts"() {
5439
+ "use strict";
5440
+ init_esm_shims();
5441
+ }
5442
+ });
5443
+
5338
5444
  // src/validators/array-validator.ts
5339
5445
  function generateArrayValidation(schema, context) {
5340
5446
  var _a;
@@ -6006,6 +6112,7 @@ var init_property_generator = __esm({
6006
6112
  init_esm_shims();
6007
6113
  init_lru_cache();
6008
6114
  init_name_utils();
6115
+ init_pattern_utils();
6009
6116
  init_string_utils();
6010
6117
  init_array_validator();
6011
6118
  init_composition_validator();
@@ -6248,8 +6355,9 @@ var init_property_generator = __esm({
6248
6355
  }
6249
6356
  (_a = this.context.schemaDependencies.get(currentSchema)) == null ? void 0 : _a.add(refName);
6250
6357
  }
6251
- const schemaName = `${toCamelCase(resolvedRefName, this.context.namingOptions)}Schema`;
6252
- if (currentSchema && this.isCircularThroughAlias(currentSchema, refName)) {
6358
+ const strippedRefName = stripPrefix(resolvedRefName, this.context.stripSchemaPrefix);
6359
+ const schemaName = `${toCamelCase(strippedRefName, this.context.namingOptions)}Schema`;
6360
+ if (currentSchema && (refName === currentSchema || this.isCircularThroughAlias(currentSchema, refName))) {
6253
6361
  const lazySchema = `z.lazy((): z.ZodTypeAny => ${schemaName})`;
6254
6362
  return wrapNullable(lazySchema, nullable);
6255
6363
  }
@@ -6261,9 +6369,25 @@ var init_property_generator = __esm({
6261
6369
  return wrapNullable(zodLiteral, nullable);
6262
6370
  }
6263
6371
  if (schema.enum) {
6264
- const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
6265
- const zodEnum = `z.enum([${enumValues}])`;
6266
- return wrapNullable(zodEnum, nullable);
6372
+ const allBooleans = schema.enum.every((v) => typeof v === "boolean");
6373
+ if (allBooleans) {
6374
+ const zodBoolean = "z.boolean()";
6375
+ return wrapNullable(zodBoolean, nullable);
6376
+ }
6377
+ const allStrings = schema.enum.every((v) => typeof v === "string");
6378
+ if (allStrings) {
6379
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
6380
+ const zodEnum = `z.enum([${enumValues}])`;
6381
+ return wrapNullable(zodEnum, nullable);
6382
+ }
6383
+ const literalValues = schema.enum.map((v) => {
6384
+ if (typeof v === "string") {
6385
+ return `z.literal("${v}")`;
6386
+ }
6387
+ return `z.literal(${v})`;
6388
+ }).join(", ");
6389
+ const zodUnion = `z.union([${literalValues}])`;
6390
+ return wrapNullable(zodUnion, nullable);
6267
6391
  }
6268
6392
  if (schema.allOf) {
6269
6393
  let composition = generateAllOf(
@@ -6396,7 +6520,7 @@ var init_property_generator = __esm({
6396
6520
  });
6397
6521
 
6398
6522
  // src/utils/operation-filters.ts
6399
- import { minimatch } from "minimatch";
6523
+ import { minimatch as minimatch2 } from "minimatch";
6400
6524
  function createFilterStatistics() {
6401
6525
  return {
6402
6526
  totalOperations: 0,
@@ -6415,7 +6539,7 @@ function matchesAnyPattern(value, patterns) {
6415
6539
  if (!value) {
6416
6540
  return false;
6417
6541
  }
6418
- return patterns.some((pattern) => minimatch(value, pattern));
6542
+ return patterns.some((pattern) => minimatch2(value, pattern));
6419
6543
  }
6420
6544
  function containsAny(arr, values) {
6421
6545
  if (!values || values.length === 0) {
@@ -6538,7 +6662,7 @@ var init_operation_filters = __esm({
6538
6662
  // src/openapi-generator.ts
6539
6663
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
6540
6664
  import { dirname, normalize } from "path";
6541
- import { minimatch as minimatch2 } from "minimatch";
6665
+ import { minimatch as minimatch3 } from "minimatch";
6542
6666
  import { parse } from "yaml";
6543
6667
  var OpenApiGenerator;
6544
6668
  var init_openapi_generator = __esm({
@@ -6551,6 +6675,7 @@ var init_openapi_generator = __esm({
6551
6675
  init_property_generator();
6552
6676
  init_name_utils();
6553
6677
  init_operation_filters();
6678
+ init_pattern_utils();
6554
6679
  init_string_validator();
6555
6680
  OpenApiGenerator = class {
6556
6681
  constructor(options) {
@@ -6573,6 +6698,7 @@ var init_openapi_generator = __esm({
6573
6698
  schemaType: options.schemaType || "all",
6574
6699
  prefix: options.prefix,
6575
6700
  suffix: options.suffix,
6701
+ stripSchemaPrefix: options.stripSchemaPrefix,
6576
6702
  showStats: (_c = options.showStats) != null ? _c : true,
6577
6703
  request: options.request,
6578
6704
  response: options.response,
@@ -6649,7 +6775,8 @@ var init_openapi_generator = __esm({
6649
6775
  namingOptions: {
6650
6776
  prefix: this.options.prefix,
6651
6777
  suffix: this.options.suffix
6652
- }
6778
+ },
6779
+ stripSchemaPrefix: this.options.stripSchemaPrefix
6653
6780
  });
6654
6781
  }
6655
6782
  /**
@@ -6662,6 +6789,9 @@ var init_openapi_generator = __esm({
6662
6789
  throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
6663
6790
  }
6664
6791
  for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
6792
+ if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
6793
+ continue;
6794
+ }
6665
6795
  this.generateComponentSchema(name, schema);
6666
6796
  }
6667
6797
  this.generateQueryParameterSchemas();
@@ -6683,9 +6813,11 @@ var init_openapi_generator = __esm({
6683
6813
  const typeCode = this.types.get(name);
6684
6814
  if (schemaCode) {
6685
6815
  output.push(schemaCode);
6686
- if (!schemaCode.includes(`export type ${name}`)) {
6687
- const schemaName = `${toCamelCase(name, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
6688
- output.push(`export type ${name} = z.infer<typeof ${schemaName}>;`);
6816
+ const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
6817
+ const typeName = toPascalCase(strippedName);
6818
+ if (!schemaCode.includes(`export type ${typeName}`)) {
6819
+ const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
6820
+ output.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
6689
6821
  }
6690
6822
  output.push("");
6691
6823
  } else if (typeCode) {
@@ -7001,7 +7133,8 @@ var init_openapi_generator = __esm({
7001
7133
  const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
7002
7134
  if (schema.enum) {
7003
7135
  const jsdoc2 = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
7004
- const { schemaCode, typeCode } = generateEnum(name, schema.enum, {
7136
+ const strippedName2 = stripPrefix(name, this.options.stripSchemaPrefix);
7137
+ const { schemaCode, typeCode } = generateEnum(strippedName2, schema.enum, {
7005
7138
  prefix: this.options.prefix,
7006
7139
  suffix: this.options.suffix
7007
7140
  });
@@ -7010,7 +7143,8 @@ ${typeCode}`;
7010
7143
  this.schemas.set(name, enumSchemaCode);
7011
7144
  return;
7012
7145
  }
7013
- const schemaName = `${toCamelCase(name, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
7146
+ const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
7147
+ const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
7014
7148
  const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
7015
7149
  if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref) {
7016
7150
  const refName = resolveRef(schema.allOf[0].$ref);
@@ -7026,7 +7160,8 @@ ${typeCode}`;
7026
7160
  namingOptions: {
7027
7161
  prefix: this.options.prefix,
7028
7162
  suffix: this.options.suffix
7029
- }
7163
+ },
7164
+ stripSchemaPrefix: this.options.stripSchemaPrefix
7030
7165
  });
7031
7166
  const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
7032
7167
  const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
@@ -7145,7 +7280,7 @@ ${propsCode}
7145
7280
  const headerLower = headerName.toLowerCase();
7146
7281
  return ignorePatterns.some((pattern) => {
7147
7282
  const patternLower = pattern.toLowerCase();
7148
- return minimatch2(headerLower, patternLower);
7283
+ return minimatch3(headerLower, patternLower);
7149
7284
  });
7150
7285
  }
7151
7286
  /**
@@ -7228,12 +7363,27 @@ ${propsCode}
7228
7363
  generateQueryParamType(schema, param) {
7229
7364
  if (schema.$ref) {
7230
7365
  const refName = resolveRef(schema.$ref);
7231
- const schemaName = toCamelCase(refName, { prefix: this.options.prefix, suffix: this.options.suffix });
7366
+ const strippedRefName = stripPrefix(refName, this.options.stripSchemaPrefix);
7367
+ const schemaName = toCamelCase(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
7232
7368
  return `${schemaName}Schema`;
7233
7369
  }
7234
7370
  if (schema.enum) {
7235
- const enumValues = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : v).join(", ");
7236
- return `z.enum([${enumValues}])`;
7371
+ const allBooleans = schema.enum.every((v) => typeof v === "boolean");
7372
+ if (allBooleans) {
7373
+ return "z.boolean()";
7374
+ }
7375
+ const allStrings = schema.enum.every((v) => typeof v === "string");
7376
+ if (allStrings) {
7377
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
7378
+ return `z.enum([${enumValues}])`;
7379
+ }
7380
+ const literalValues = schema.enum.map((v) => {
7381
+ if (typeof v === "string") {
7382
+ return `z.literal("${v}")`;
7383
+ }
7384
+ return `z.literal(${v})`;
7385
+ }).join(", ");
7386
+ return `z.union([${literalValues}])`;
7237
7387
  }
7238
7388
  const type = schema.type;
7239
7389
  if (type === "string") {
@@ -7485,7 +7635,7 @@ async function loadConfig(configPath) {
7485
7635
  }
7486
7636
  if (!result || !result.config) {
7487
7637
  throw new Error(
7488
- configPath ? `Config file not found at: ${configPath}` : "No config file found. Searched for: openapi-to-zod.config.ts, openapi-to-zod.config.json, package.json (openapi-to-zod key)"
7638
+ configPath ? `Config file not found at: ${configPath}` : "No config file found. Searched for: openapi-to-zod.config.ts, openapi-to-zod.config.json, package.json (openapi-to-zod key)\nRun 'openapi-to-zod init' to create a new config file."
7489
7639
  );
7490
7640
  }
7491
7641
  try {
@@ -7537,6 +7687,7 @@ var init_config_loader = __esm({
7537
7687
  schemaType: z2.enum(["all", "request", "response"]).optional(),
7538
7688
  prefix: z2.string().optional(),
7539
7689
  suffix: z2.string().optional(),
7690
+ stripSchemaPrefix: z2.string().optional(),
7540
7691
  showStats: z2.boolean().optional(),
7541
7692
  request: RequestResponseOptionsSchema.optional(),
7542
7693
  response: RequestResponseOptionsSchema.optional(),
@@ -7553,6 +7704,7 @@ var init_config_loader = __esm({
7553
7704
  schemaType: z2.enum(["all", "request", "response"]).optional(),
7554
7705
  prefix: z2.string().optional(),
7555
7706
  suffix: z2.string().optional(),
7707
+ stripSchemaPrefix: z2.string().optional(),
7556
7708
  showStats: z2.boolean().optional(),
7557
7709
  request: RequestResponseOptionsSchema.optional(),
7558
7710
  response: RequestResponseOptionsSchema.optional(),
@@ -7652,14 +7804,7 @@ Examples:
7652
7804
  }
7653
7805
  async function executeConfigMode(options) {
7654
7806
  var _a, _b;
7655
- let config;
7656
- try {
7657
- config = await loadConfig(options.config);
7658
- } catch {
7659
- throw new CliOptionsError("No config file found. Run 'openapi-to-zod init' to create one.", {
7660
- configPath: options.config
7661
- });
7662
- }
7807
+ const config = await loadConfig(options.config);
7663
7808
  const specs = mergeConfigWithDefaults(config);
7664
7809
  const executionMode = config.executionMode || "parallel";
7665
7810
  const batchSize = (_b = (_a = specs[0]) == null ? void 0 : _a.batchSize) != null ? _b : 10;