orval 8.17.0 → 8.19.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.
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SupportedFormatter, asyncReduce, buildDynamicScope, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateMutator, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getImportExtension, getMockFileExtensionByTypeName, getRefInfo, getRoute, isBoolean, isComponentRef, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, resolveValue, splitSchemasByType, upath, writeGeneratedFile, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
2
+ import { DefaultTag, FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SupportedFormatter, asyncReduce, buildDynamicScope, buildSchemaTagMap, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateMutator, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getImportExtension, getMockFileExtensionByTypeName, getRefInfo, getRoute, isBoolean, isComponentRef, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, kebab, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, resolveValue, splitSchemasByType, upath, writeGeneratedFile, writeSchemas, writeSchemasTagsSplit, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
3
3
  import { bundle } from "@scalar/json-magic/bundle";
4
4
  import { fetchUrls, parseJson, parseYaml, readFiles } from "@scalar/json-magic/bundle/plugins/node";
5
5
  import { upgrade, validate } from "@scalar/openapi-parser";
@@ -15,7 +15,7 @@ import mcp from "@orval/mcp";
15
15
  import query from "@orval/query";
16
16
  import solidStart from "@orval/solid-start";
17
17
  import swr from "@orval/swr";
18
- import zod, { dereference, generateFormDataZodSchema, generateZodValidationSchemaDefinition, isZodVersionV4, parseZodValidationSchemaDefinition } from "@orval/zod";
18
+ import zod, { dereference, generateFormDataZodSchema, generateZodValidationSchemaDefinition, parseZodValidationSchemaDefinition, resolveIsZodV4 } from "@orval/zod";
19
19
  import { ExecaError, execa } from "execa";
20
20
  import fs from "fs-extra";
21
21
  import fs$1, { access } from "node:fs/promises";
@@ -28,7 +28,7 @@ import fs$2 from "node:fs";
28
28
  import { createJiti } from "jiti";
29
29
  //#region package.json
30
30
  var name = "orval";
31
- var version = "8.17.0";
31
+ var version = "8.19.0";
32
32
  var description = "A swagger client generator for typescript";
33
33
  //#endregion
34
34
  //#region src/client.ts
@@ -77,21 +77,24 @@ const generateClientImports = ({ client, implementation, imports, projectName, h
77
77
  };
78
78
  const generateClientHeader = ({ outputClient = DEFAULT_CLIENT, isRequestOptions, isGlobalMutator, isMutator, provideIn, hasAwaitedType, titles, output, verbOptions, tag, isDefaultTagBucket, clientImplementation }) => {
79
79
  const { header } = getGeneratorClient(outputClient, output);
80
+ const rawHeader = header ? header({
81
+ title: titles.implementation,
82
+ isRequestOptions,
83
+ isGlobalMutator,
84
+ isMutator,
85
+ provideIn,
86
+ hasAwaitedType,
87
+ output,
88
+ verbOptions,
89
+ tag,
90
+ isDefaultTagBucket,
91
+ clientImplementation
92
+ }) : "";
93
+ const normalizedHeader = typeof rawHeader === "string" ? { implementation: rawHeader } : rawHeader;
80
94
  return {
81
- implementation: header ? header({
82
- title: titles.implementation,
83
- isRequestOptions,
84
- isGlobalMutator,
85
- isMutator,
86
- provideIn,
87
- hasAwaitedType,
88
- output,
89
- verbOptions,
90
- tag,
91
- isDefaultTagBucket,
92
- clientImplementation
93
- }) : "",
94
- implementationMock: `export const ${titles.implementationMock} = () => [\n`
95
+ implementation: normalizedHeader.implementation,
96
+ implementationMock: `export const ${titles.implementationMock} = () => [\n`,
97
+ sharedTypes: normalizedHeader.sharedTypes
95
98
  };
96
99
  };
97
100
  const generateClientFooter = ({ outputClient, operationNames, hasMutator, hasAwaitedType, titles, output }) => {
@@ -173,7 +176,8 @@ const generateOperations = (outputClient = DEFAULT_CLIENT, verbsOptions, options
173
176
  type: isFunction(entry) ? OutputMockType.MSW : entry.type,
174
177
  implementation: generated.implementation,
175
178
  imports: generated.imports,
176
- strictMockSchemaTypeNames: generated.strictMockSchemaTypeNames
179
+ strictMockSchemaTypeNames: generated.strictMockSchemaTypeNames,
180
+ strictMockSchemaKinds: generated.strictMockSchemaKinds
177
181
  };
178
182
  });
179
183
  const hasImplementation = client.implementation.trim().length > 0;
@@ -883,17 +887,28 @@ function createFormData(workspace, formData) {
883
887
  function normalizeSchemasOption(schemas, workspace) {
884
888
  if (!schemas) return;
885
889
  if (isString(schemas)) return normalizePath(schemas, workspace);
886
- if (schemas.importPath !== void 0 && !schemas.importPath) throw new Error(`schemas.importPath must be a non-empty package specifier (e.g. '@acme/models'). Received an empty string.`);
887
- if (schemas.importPath?.trim() === "") throw new Error(`schemas.importPath must be a non-empty package specifier (e.g. '@acme/models'). Received a whitespace-only string.`);
888
- if (schemas.importPath && schemas.importPath.trim() !== schemas.importPath) throw new Error(`schemas.importPath must be a non-empty package specifier (e.g. '@acme/models'). Received a value with leading or trailing whitespace: "${schemas.importPath}"`);
889
- if (schemas.importPath?.startsWith(".")) throw new Error(`schemas.importPath must be a package specifier (e.g. '@acme/models'), not a relative path. Received: "${schemas.importPath}"`);
890
- if (schemas.importPath && (path.isAbsolute(schemas.importPath) || /^[A-Za-z]:[\\/]/.test(schemas.importPath) || schemas.importPath.startsWith("\\\\"))) throw new Error(`schemas.importPath must be a package specifier (e.g. '@acme/models'), not an absolute path. Received: "${schemas.importPath}"`);
890
+ validatePackageSpecifier(schemas.importPath, "schemas.importPath");
891
891
  return {
892
892
  path: normalizePath(schemas.path, workspace),
893
- type: schemas.type,
894
- importPath: schemas.importPath
893
+ type: schemas.type ?? "typescript",
894
+ importPath: schemas.importPath,
895
+ splitByTags: schemas.splitByTags ?? false
895
896
  };
896
897
  }
898
+ /**
899
+ * Validates that a config value is a valid package specifier (bare specifier
900
+ * or sub-path import like `@acme/models` / `@acme/models/fakers`). Rejects
901
+ * empty, whitespace-only, relative (`./`, `../`), and absolute paths with a
902
+ * clear, actionable error message. No-op when the value is `undefined`.
903
+ */
904
+ function validatePackageSpecifier(value, fieldName) {
905
+ if (value === void 0) return;
906
+ if (!value) throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received an empty string.`);
907
+ if (value.trim() === "") throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received a whitespace-only string.`);
908
+ if (value.trim() !== value) throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received a value with leading or trailing whitespace: "${value}"`);
909
+ if (value.startsWith(".")) throw new Error(`\`${fieldName}\` must be a package specifier (e.g. '@acme/models'), not a relative path. Received: "${value}"`);
910
+ if (path.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\")) throw new Error(`\`${fieldName}\` must be a package specifier (e.g. '@acme/models'), not an absolute path. Received: "${value}"`);
911
+ }
897
912
  function normalizeEffectOptions(effect) {
898
913
  return {
899
914
  strict: {
@@ -955,6 +970,7 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
955
970
  if (isFunction(entry)) continue;
956
971
  if (seenMockTypes.has(entry.type)) throw new Error(`Duplicate mock generator type "${entry.type}". Each type can only appear once in mock.generators.`);
957
972
  seenMockTypes.add(entry.type);
973
+ if (entry.type === OutputMockType.FAKER) validatePackageSpecifier(entry.schemasImportPath, "mock.generators[faker].schemasImportPath");
958
974
  }
959
975
  const defaultFileExtension = ".ts";
960
976
  const defaultSchemaFileExtension = !!outputOptions.schemas && (!isString(outputOptions.schemas) && outputOptions.schemas.type === "zod" || isString(outputOptions.schemas) && (outputOptions.client ?? client) === "zod" && outputOptions.override?.zod?.generateReusableSchemas === true) ? ".zod.ts" : defaultFileExtension;
@@ -973,7 +989,7 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
973
989
  shouldExportQueryKey: true,
974
990
  shouldFilterQueryKey: false,
975
991
  shouldSplitQueryKey: false,
976
- ...normalizeQueryOptions(outputOptions.override?.query, workspace)
992
+ ...normalizeQueryOptions(outputOptions.override?.query, outputWorkspace)
977
993
  };
978
994
  const normalizedOptions = {
979
995
  input: {
@@ -1005,6 +1021,8 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
1005
1021
  baseUrl: outputOptions.baseUrl,
1006
1022
  unionAddMissingProperties: outputOptions.unionAddMissingProperties ?? false,
1007
1023
  factoryMethods,
1024
+ tagsSplitDeduplication: outputOptions.tagsSplitDeduplication ?? false,
1025
+ commonTypesFileName: outputOptions.commonTypesFileName ?? "common-types",
1008
1026
  override: {
1009
1027
  ...outputOptions.override,
1010
1028
  mock: {
@@ -1015,8 +1033,8 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
1015
1033
  fractionDigits: outputOptions.override?.mock?.fractionDigits ?? 2,
1016
1034
  ...outputOptions.override?.mock
1017
1035
  },
1018
- operations: normalizeOperationsAndTags(outputOptions.override?.operations ?? {}, outputWorkspace, { query: globalQueryOptions }),
1019
- tags: normalizeOperationsAndTags(outputOptions.override?.tags ?? {}, outputWorkspace, { query: globalQueryOptions }),
1036
+ operations: normalizeOperationsAndTags(outputOptions.override?.operations ?? {}, outputWorkspace, { query: globalQueryOptions }, "operations"),
1037
+ tags: normalizeOperationsAndTags(outputOptions.override?.tags ?? {}, outputWorkspace, { query: globalQueryOptions }, "tags"),
1020
1038
  mutator: normalizeMutator(outputWorkspace, outputOptions.override?.mutator),
1021
1039
  formData: createFormData(outputWorkspace, outputOptions.override?.formData),
1022
1040
  formUrlEncoded: (isBoolean(outputOptions.override?.formUrlEncoded) ? outputOptions.override.formUrlEncoded : normalizeMutator(outputWorkspace, outputOptions.override?.formUrlEncoded)) ?? true,
@@ -1071,13 +1089,14 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
1071
1089
  response: outputOptions.override?.zod?.coerce?.response ?? false
1072
1090
  },
1073
1091
  preprocess: {
1074
- ...outputOptions.override?.zod?.preprocess?.param ? { param: normalizeMutator(workspace, outputOptions.override.zod.preprocess.param) } : {},
1075
- ...outputOptions.override?.zod?.preprocess?.query ? { query: normalizeMutator(workspace, outputOptions.override.zod.preprocess.query) } : {},
1076
- ...outputOptions.override?.zod?.preprocess?.header ? { header: normalizeMutator(workspace, outputOptions.override.zod.preprocess.header) } : {},
1077
- ...outputOptions.override?.zod?.preprocess?.body ? { body: normalizeMutator(workspace, outputOptions.override.zod.preprocess.body) } : {},
1078
- ...outputOptions.override?.zod?.preprocess?.response ? { response: normalizeMutator(workspace, outputOptions.override.zod.preprocess.response) } : {}
1092
+ ...outputOptions.override?.zod?.preprocess?.param ? { param: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.param) } : {},
1093
+ ...outputOptions.override?.zod?.preprocess?.query ? { query: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.query) } : {},
1094
+ ...outputOptions.override?.zod?.preprocess?.header ? { header: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.header) } : {},
1095
+ ...outputOptions.override?.zod?.preprocess?.body ? { body: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.body) } : {},
1096
+ ...outputOptions.override?.zod?.preprocess?.response ? { response: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.response) } : {}
1079
1097
  },
1080
- ...outputOptions.override?.zod?.params ? { params: normalizeMutator(workspace, outputOptions.override.zod.params) } : {},
1098
+ ...outputOptions.override?.zod?.params ? { params: normalizeMutator(outputWorkspace, outputOptions.override.zod.params) } : {},
1099
+ version: outputOptions.override?.zod?.version ?? "auto",
1081
1100
  generateEachHttpStatus: outputOptions.override?.zod?.generateEachHttpStatus ?? false,
1082
1101
  useBrandedTypes: outputOptions.override?.zod?.useBrandedTypes ?? false,
1083
1102
  generateReusableSchemas: outputOptions.override?.zod?.generateReusableSchemas ?? false,
@@ -1101,6 +1120,7 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
1101
1120
  forceSuccessResponse: outputOptions.override?.fetch?.forceSuccessResponse ?? false,
1102
1121
  runtimeValidation: outputOptions.override?.fetch?.runtimeValidation ?? false,
1103
1122
  useRuntimeFetcher: outputOptions.override?.fetch?.useRuntimeFetcher ?? false,
1123
+ ...outputOptions.override?.fetch?.arrayFormat ? { arrayFormat: outputOptions.override.fetch.arrayFormat } : {},
1104
1124
  ...outputOptions.override?.fetch,
1105
1125
  ...outputOptions.override?.fetch?.jsonReviver ? { jsonReviver: normalizeMutator(outputWorkspace, outputOptions.override.fetch.jsonReviver) } : {}
1106
1126
  },
@@ -1121,6 +1141,11 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
1121
1141
  };
1122
1142
  if (!normalizedOptions.input.target) throw new Error(styleText("red", `Config requires an input target.`));
1123
1143
  if (!normalizedOptions.output.target && !normalizedOptions.output.schemas) throw new Error(styleText("red", `Config requires an output target or schemas.`));
1144
+ const fakerWithSchemasImportPath = normalizedOptions.output.mock.generators.find((g) => !isFunction(g) && g.type === OutputMockType.FAKER && !!g.schemasImportPath);
1145
+ if (fakerWithSchemasImportPath) {
1146
+ if (fakerWithSchemasImportPath.schemas !== true) throw new Error(styleText("red", `\`mock.generators[faker].schemasImportPath\` requires \`schemas: true\` on the same generator. Schema-level faker factories are only emitted when \`schemas: true\`.`));
1147
+ if (!(isObject(normalizedOptions.output.schemas) && normalizedOptions.output.schemas.importPath)) throw new Error(styleText("red", `\`mock.generators[faker].schemasImportPath\` requires \`schemas.importPath\` to also be set. It overrides the package specifier used for importing schema-level faker factories.`));
1148
+ }
1124
1149
  const usesAngularGenerator = normalizedOptions.output.client === OutputClient.ANGULAR || normalizedOptions.output.client === OutputClient.ANGULAR_QUERY && normalizedOptions.output.httpClient === OutputHttpClient.ANGULAR;
1125
1150
  if (normalizedOptions.output.override.paramsFilter && !usesAngularGenerator) throw new Error(styleText("red", `\`override.paramsFilter\` is only supported by the Angular generator (the \`angular\` client, or \`angular-query\` with \`httpClient: 'angular'\`). It has no effect for other clients — use \`override.paramsSerializer\` instead.`));
1126
1151
  if (!usesAngularGenerator) {
@@ -1210,8 +1235,19 @@ function normalizePath(path$1, workspace) {
1210
1235
  if (!isString(path$1)) return path$1;
1211
1236
  return path.resolve(workspace, path$1);
1212
1237
  }
1213
- function normalizeOperationsAndTags(operationsOrTags, workspace, global) {
1238
+ function normalizeOperationsAndTags(operationsOrTags, workspace, global, source) {
1239
+ const unsupportedZodKeys = [
1240
+ "version",
1241
+ "dateTimeOptions",
1242
+ "timeOptions",
1243
+ "generateEachHttpStatus",
1244
+ "generateReusableSchemas",
1245
+ "generateMeta"
1246
+ ];
1214
1247
  return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, paramsFilter, query, angular, zod, effect, ...rest }]) => {
1248
+ const unsupportedOperationZodKeys = zod && unsupportedZodKeys.filter((unsupportedKey) => zod[unsupportedKey] !== void 0);
1249
+ if (unsupportedOperationZodKeys && unsupportedOperationZodKeys.length) logWarning(`⚠️ override.${source}.${key}.zod only supports strict, generate, coerce, preprocess, params, and useBrandedTypes. Ignoring unsupported ${unsupportedOperationZodKeys.length === 1 ? "field" : "fields"}: ${unsupportedOperationZodKeys.map((unsupportedKey) => `zod.${unsupportedKey}`).join(", ")}.`);
1250
+ const hasSupportedOperationZodConfig = !!zod && (zod.strict !== void 0 || zod.generate !== void 0 || zod.coerce !== void 0 || zod.preprocess !== void 0 || zod.params !== void 0 || zod.useBrandedTypes !== void 0);
1215
1251
  return [key, {
1216
1252
  ...rest,
1217
1253
  ...angular ? { angular: {
@@ -1221,7 +1257,7 @@ function normalizeOperationsAndTags(operationsOrTags, workspace, global) {
1221
1257
  ...angular.httpResource ? { httpResource: angular.httpResource } : {}
1222
1258
  } } : {},
1223
1259
  ...query ? { query: normalizeQueryOptions(query, workspace, global.query) } : {},
1224
- ...zod ? { zod: {
1260
+ ...hasSupportedOperationZodConfig && zod ? { zod: {
1225
1261
  strict: {
1226
1262
  param: zod.strict?.param ?? false,
1227
1263
  query: zod.strict?.query ?? false,
@@ -1251,12 +1287,7 @@ function normalizeOperationsAndTags(operationsOrTags, workspace, global) {
1251
1287
  ...zod.preprocess?.response ? { response: normalizeMutator(workspace, zod.preprocess.response) } : {}
1252
1288
  },
1253
1289
  ...zod.params ? { params: normalizeMutator(workspace, zod.params) } : {},
1254
- generateEachHttpStatus: zod.generateEachHttpStatus ?? false,
1255
- useBrandedTypes: zod.useBrandedTypes ?? false,
1256
- generateReusableSchemas: zod.generateReusableSchemas ?? false,
1257
- generateMeta: zod.generateMeta ?? false,
1258
- dateTimeOptions: zod.dateTimeOptions ?? { offset: true },
1259
- timeOptions: zod.timeOptions ?? {}
1290
+ useBrandedTypes: zod.useBrandedTypes ?? false
1260
1291
  } } : {},
1261
1292
  ...effect ? { effect: normalizeEffectOptions(effect) } : {},
1262
1293
  ...transformer ? { transformer: normalizePath(transformer, workspace) } : {},
@@ -1591,6 +1622,23 @@ const rewriteReusableSchemas = (entries) => {
1591
1622
  function buildMutatorImportStatement(mutator) {
1592
1623
  return `import ${mutator.default ? mutator.name : `{ ${mutator.name} }`} from '${mutator.path}';`;
1593
1624
  }
1625
+ const ROOT_DIR = ".";
1626
+ function getSchemaDir(schemaTagMap, name) {
1627
+ return schemaTagMap?.get(name) ?? ROOT_DIR;
1628
+ }
1629
+ function computeCrossDirImportPath(schemasPath, fromDir, toDir, fileName, importExt) {
1630
+ if (fromDir === toDir) return `./${fileName}${importExt}`;
1631
+ const fromPath = fromDir === ROOT_DIR ? schemasPath : path.join(schemasPath, fromDir);
1632
+ const toPath = toDir === ROOT_DIR ? schemasPath : path.join(schemasPath, toDir);
1633
+ const relDir = upath.relativeSafe(fromPath, toPath);
1634
+ return `${upath.joinSafe(relDir, fileName)}${importExt}`;
1635
+ }
1636
+ function adjustMutatorPathForDir(mutatorPath, tagDir) {
1637
+ if (tagDir === ROOT_DIR) return mutatorPath;
1638
+ if (mutatorPath.startsWith("./")) return `../${mutatorPath.slice(2)}`;
1639
+ if (mutatorPath.startsWith("../")) return `../${mutatorPath}`;
1640
+ return mutatorPath;
1641
+ }
1594
1642
  /**
1595
1643
  * Whole-word substring check for a resolved mutator alias inside generated
1596
1644
  * code. Plain `String.includes` would false-positive when the user names the
@@ -1660,7 +1708,7 @@ const isValidSchemaIdentifier = (name) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
1660
1708
  * `resolveValue` producer surfaces aliases here today, so the integration
1661
1709
  * tests can't exercise it.
1662
1710
  */
1663
- function buildSiblingImports({ usedRefs, extraImports, entryName, componentNames, namingConvention, importExt }) {
1711
+ function buildSiblingImports({ usedRefs, extraImports, entryName, componentNames, namingConvention, importExt, schemaTagMap, currentDir, schemasPath }) {
1664
1712
  const importsByName = /* @__PURE__ */ new Map();
1665
1713
  for (const name of usedRefs) {
1666
1714
  if (name === entryName) continue;
@@ -1672,7 +1720,7 @@ function buildSiblingImports({ usedRefs, extraImports, entryName, componentNames
1672
1720
  }
1673
1721
  return [...importsByName.values()].toSorted((a, b) => a.name.localeCompare(b.name)).map(({ name, alias }) => {
1674
1722
  const importedFile = conventionName(name, namingConvention);
1675
- return `import { ${alias ? `${name} as ${alias}` : name} } from './${importedFile}${importExt}';`;
1723
+ return `import { ${alias ? `${name} as ${alias}` : name} } from '${schemaTagMap && currentDir && schemasPath ? computeCrossDirImportPath(schemasPath, currentDir, getSchemaDir(schemaTagMap, name), importedFile, importExt) : `./${importedFile}${importExt}`}';`;
1676
1724
  }).join("\n");
1677
1725
  }
1678
1726
  const isPrimitiveSchemaName = (name) => [
@@ -1698,8 +1746,8 @@ const groupSchemasByFilePath = (schemas) => {
1698
1746
  }
1699
1747
  return [...grouped.values()].map((group) => [...group].toSorted((a, b) => a.filePath.localeCompare(b.filePath, "en", { numeric: true }))).toSorted((a, b) => a[0].filePath.localeCompare(b[0].filePath, "en", { numeric: true }));
1700
1748
  };
1701
- async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNames, namingConvention, shouldMergeExisting = false) {
1702
- const importFileExtension = fileExtension.replace(/\.ts$/, "");
1749
+ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNames, namingConvention, shouldMergeExisting = false, tsconfig) {
1750
+ const importFileExtension = getImportExtension(fileExtension, tsconfig);
1703
1751
  const indexPath = path.join(schemasPath, `index.ts`);
1704
1752
  let existingExports = "";
1705
1753
  if (shouldMergeExisting && await fs.pathExists(indexPath)) {
@@ -1715,11 +1763,34 @@ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNam
1715
1763
  const uniqueExports = [...new Set(allExports.split("\n"))].filter((line) => line.trim()).toSorted().join("\n");
1716
1764
  await fs.outputFile(indexPath, `${header}\n${uniqueExports}\n`);
1717
1765
  }
1766
+ async function writeZodSchemaTagsSplitBarrel(schemasPath, fileExtension, header, componentDirs, verbDirs, namingConvention, tsconfig) {
1767
+ const importExt = getImportExtension(fileExtension, tsconfig);
1768
+ const indexImportExt = getImportExtension(".ts", tsconfig);
1769
+ const allDirs = /* @__PURE__ */ new Map();
1770
+ for (const [dir, names] of componentDirs) allDirs.set(dir, [...names]);
1771
+ for (const [dir, names] of verbDirs) if (allDirs.has(dir)) allDirs.get(dir).push(...names);
1772
+ else allDirs.set(dir, [...names]);
1773
+ for (const [dir, schemaNames] of allDirs) {
1774
+ if (dir === ROOT_DIR) continue;
1775
+ await writeZodSchemaIndex(path.join(schemasPath, dir), fileExtension, header, schemaNames, namingConvention, false, tsconfig);
1776
+ }
1777
+ const rootSchemas = allDirs.get(ROOT_DIR) ?? [];
1778
+ const rootExports = [...new Set(rootSchemas)].map((name) => {
1779
+ return `export * from './${conventionName(name, namingConvention)}${importExt}';`;
1780
+ }).toSorted();
1781
+ const tagExports = [...allDirs.keys()].filter((dir) => dir !== ROOT_DIR).toSorted((a, b) => a.localeCompare(b, "en", { numeric: true })).map((dir) => {
1782
+ return `export * from '${indexImportExt ? `./${dir}/index${indexImportExt}` : `./${dir}`}';`;
1783
+ });
1784
+ const allExports = [...rootExports, ...tagExports];
1785
+ const rootIndexPath = path.join(schemasPath, "index.ts");
1786
+ const content = `${header}\n${allExports.join("\n")}\n`;
1787
+ await fs.outputFile(rootIndexPath, content);
1788
+ }
1718
1789
  function generateZodSchemasInline(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
1719
1790
  if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output, includeZodImport, paramsMutator, includeParamsImport);
1720
1791
  const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
1721
1792
  if (schemasWithOpenApiDef.length === 0) return "";
1722
- const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1793
+ const isZodV4 = resolveIsZodV4(output.override.zod.version, output.packageJson);
1723
1794
  const strict = output.override.zod.strict.body;
1724
1795
  const coerce = output.override.zod.coerce.body;
1725
1796
  const schemas = [];
@@ -1745,7 +1816,7 @@ function generateZodSchemasInline(builder, output, includeZodImport = true, para
1745
1816
  return generateZodSchemaFileContent("", schemas, includeZodImport);
1746
1817
  }
1747
1818
  function generateZodSchemasInlineReusable(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
1748
- const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1819
+ const isZodV4 = resolveIsZodV4(output.override.zod.version, output.packageJson);
1749
1820
  const strict = output.override.zod.strict.body;
1750
1821
  const coerce = output.override.zod.coerce.body;
1751
1822
  const context = {
@@ -1769,21 +1840,20 @@ function generateZodSchemasInlineReusable(builder, output, includeZodImport = tr
1769
1840
  const paramsImport = paramsMutator && includeParamsImport && bodyReferencesMutator(body, paramsMutator) ? `${buildMutatorImportStatement(paramsMutator)}\n` : "";
1770
1841
  return `${zodImport || paramsImport ? `${zodImport}${paramsImport}\n` : ""}${body}\n`;
1771
1842
  }
1772
- async function writeZodSchemas(builder, schemasPath, fileExtension, header, output, paramsMutator) {
1773
- if (output.override.zod.generateReusableSchemas) {
1774
- await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator);
1775
- return;
1776
- }
1843
+ async function writeZodSchemas(builder, schemasPath, fileExtension, header, output, paramsMutator, schemaTagMap) {
1844
+ if (output.override.zod.generateReusableSchemas) return writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator, schemaTagMap);
1845
+ const isSplit = !!schemaTagMap;
1777
1846
  const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
1778
1847
  const schemasToWrite = [];
1779
- const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1848
+ const isZodV4 = resolveIsZodV4(output.override.zod.version, output.packageJson);
1780
1849
  const strict = output.override.zod.strict.body;
1781
1850
  const coerce = output.override.zod.coerce.body;
1782
1851
  for (const generatorSchema of schemasWithOpenApiDef) {
1783
1852
  const { name, schema: schemaObject } = generatorSchema;
1784
1853
  if (!schemaObject) continue;
1785
1854
  const fileName = conventionName(name, output.namingConvention);
1786
- const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
1855
+ const tagDir = getSchemaDir(schemaTagMap, name);
1856
+ const filePath = isSplit ? path.join(schemasPath, tagDir, `${fileName}${fileExtension}`) : path.join(schemasPath, `${fileName}${fileExtension}`);
1787
1857
  const context = {
1788
1858
  spec: builder.spec,
1789
1859
  target: builder.target,
@@ -1806,10 +1876,22 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
1806
1876
  const fileContent = generateZodSchemaFileContent(header, schemaGroup);
1807
1877
  await fs.outputFile(schemaGroup[0].filePath, fileContent);
1808
1878
  }
1809
- if (output.indexFiles) await writeZodSchemaIndex(schemasPath, fileExtension, header, groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName), output.namingConvention, false);
1879
+ const writtenSchemaNames = groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName);
1880
+ if (output.indexFiles && !isSplit) await writeZodSchemaIndex(schemasPath, fileExtension, header, writtenSchemaNames, output.namingConvention, false, output.tsconfig);
1881
+ if (isSplit) {
1882
+ const dirSchemas = /* @__PURE__ */ new Map();
1883
+ for (const name of writtenSchemaNames) {
1884
+ const dir = getSchemaDir(schemaTagMap, name);
1885
+ if (!dirSchemas.has(dir)) dirSchemas.set(dir, []);
1886
+ dirSchemas.get(dir).push(name);
1887
+ }
1888
+ return dirSchemas;
1889
+ }
1890
+ return new Map([[ROOT_DIR, writtenSchemaNames]]);
1810
1891
  }
1811
- async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator) {
1812
- const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1892
+ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator, schemaTagMap) {
1893
+ const isSplit = !!schemaTagMap;
1894
+ const isZodV4 = resolveIsZodV4(output.override.zod.version, output.packageJson);
1813
1895
  const strict = output.override.zod.strict.body;
1814
1896
  const coerce = output.override.zod.coerce.body;
1815
1897
  const context = {
@@ -1829,11 +1911,11 @@ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, head
1829
1911
  paramsMutator
1830
1912
  }));
1831
1913
  const componentNames = new Set(Object.keys(builder.spec.components?.schemas ?? {}).map((schemaName) => resolveSchemaName(`#/components/schemas/${schemaName}`, context)));
1832
- const paramsMutatorImport = paramsMutator ? buildMutatorImportStatement(paramsMutator) : void 0;
1833
1914
  for (const entry of rewritten) {
1834
1915
  const fileName = conventionName(entry.name, output.namingConvention);
1835
- const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
1836
- const importExt = fileExtension.replace(/\.ts$/, "");
1916
+ const tagDir = getSchemaDir(schemaTagMap, entry.name);
1917
+ const filePath = isSplit ? path.join(schemasPath, tagDir, `${fileName}${fileExtension}`) : path.join(schemasPath, `${fileName}${fileExtension}`);
1918
+ const importExt = getImportExtension(fileExtension, output.tsconfig);
1837
1919
  const rendered = renderReusableSchemaEntry(entry, context);
1838
1920
  const refImports = buildSiblingImports({
1839
1921
  usedRefs: entry.usedRefs,
@@ -1841,19 +1923,39 @@ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, head
1841
1923
  entryName: entry.name,
1842
1924
  componentNames,
1843
1925
  namingConvention: output.namingConvention,
1844
- importExt
1926
+ importExt,
1927
+ ...isSplit ? {
1928
+ schemaTagMap,
1929
+ currentDir: tagDir,
1930
+ schemasPath
1931
+ } : {}
1845
1932
  });
1846
- const imports = [...!!paramsMutator && bodyReferencesMutator(entry.zod, paramsMutator) && paramsMutatorImport ? [paramsMutatorImport] : [], ...refImports ? [refImports] : []].join("\n");
1933
+ const mutatorImportStr = !!paramsMutator && bodyReferencesMutator(entry.zod, paramsMutator) ? buildMutatorImportStatement({
1934
+ ...paramsMutator,
1935
+ path: isSplit ? adjustMutatorPathForDir(paramsMutator.path, tagDir) : paramsMutator.path
1936
+ }) : void 0;
1937
+ const imports = [...mutatorImportStr ? [mutatorImportStr] : [], ...refImports ? [refImports] : []].join("\n");
1847
1938
  const fileContent = `${header}import { z as zod } from 'zod';\n` + (imports ? `${imports}\n\n` : "\n") + `${rendered.content}\n`;
1848
1939
  await fs.outputFile(filePath, fileContent);
1849
1940
  }
1850
- if (output.indexFiles && rewritten.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, rewritten.map((e) => e.name), output.namingConvention, true);
1941
+ if (output.indexFiles && !isSplit && rewritten.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, rewritten.map((e) => e.name), output.namingConvention, true, output.tsconfig);
1942
+ if (isSplit) {
1943
+ const dirSchemas = /* @__PURE__ */ new Map();
1944
+ for (const entry of rewritten) {
1945
+ const dir = getSchemaDir(schemaTagMap, entry.name);
1946
+ if (!dirSchemas.has(dir)) dirSchemas.set(dir, []);
1947
+ dirSchemas.get(dir).push(entry.name);
1948
+ }
1949
+ return dirSchemas;
1950
+ }
1951
+ return new Map([[ROOT_DIR, rewritten.map((e) => e.name)]]);
1851
1952
  }
1852
- async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension, header, output, context) {
1953
+ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension, header, output, context, schemaTagMap) {
1954
+ const isSplit = !!schemaTagMap;
1853
1955
  const zodContext = context;
1854
1956
  const verbOptionsArray = Object.values(verbOptions);
1855
- if (verbOptionsArray.length === 0) return;
1856
- const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
1957
+ if (verbOptionsArray.length === 0) return /* @__PURE__ */ new Map();
1958
+ const isZodV4 = resolveIsZodV4(output.override.zod.version, output.packageJson);
1857
1959
  const strict = output.override.zod.strict.body;
1858
1960
  const coerce = output.override.zod.coerce.body;
1859
1961
  const useReusableSchemas = output.override.zod.generateReusableSchemas === true;
@@ -1915,14 +2017,18 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
1915
2017
  ...queryParamsSchemas,
1916
2018
  ...headerParamsSchemas,
1917
2019
  ...responseSchemas
1918
- ]);
2020
+ ]).map((s) => ({
2021
+ ...s,
2022
+ verbTagDir: isSplit ? kebab(verbOption.tags?.[0] ?? DefaultTag) : ROOT_DIR
2023
+ }));
1919
2024
  }));
1920
2025
  const schemasToWrite = [];
1921
2026
  for (const entry of uniqueVerbsSchemas) {
1922
2027
  if (useReusableSchemas && entry.schema && typeof entry.schema.$ref === "string" && Object.keys(entry.schema).length === 1) continue;
1923
2028
  const { name, schema } = entry;
1924
2029
  const fileName = conventionName(name, output.namingConvention);
1925
- const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
2030
+ const tagDir = entry.verbTagDir ?? ROOT_DIR;
2031
+ const filePath = isSplit ? path.join(schemasPath, tagDir, `${fileName}${fileExtension}`) : path.join(schemasPath, `${fileName}${fileExtension}`);
1926
2032
  const parsedZodDefinition = parseZodValidationSchemaDefinition("bodyContentType" in entry && entry.bodyContentType === "multipart/form-data" ? generateFormDataZodSchema(schema, zodContext, name, strict, isZodV4, "encoding" in entry ? entry.encoding : void 0, useReusableSchemas) : generateZodValidationSchemaDefinition(schema, zodContext, name, strict, isZodV4, {
1927
2033
  required: true,
1928
2034
  useReusableSchemas
@@ -1931,9 +2037,10 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
1931
2037
  let importStatements;
1932
2038
  if (useReusableSchemas && parsedZodDefinition.usedRefs.size > 0) {
1933
2039
  zodExpression = rewriteSentinelsToDirect(zodExpression);
1934
- const importExt = fileExtension.replace(/\.ts$/, "");
2040
+ const importExt = getImportExtension(fileExtension, output.tsconfig);
1935
2041
  importStatements = [...parsedZodDefinition.usedRefs].filter((refName) => refName !== name).toSorted().map((refName) => {
1936
- return `import { ${refName} } from './${conventionName(refName, output.namingConvention)}${importExt}';`;
2042
+ const importedFile = conventionName(refName, output.namingConvention);
2043
+ return `import { ${refName} } from '${isSplit ? computeCrossDirImportPath(schemasPath, tagDir, getSchemaDir(schemaTagMap, refName), importedFile, importExt) : `./${importedFile}${importExt}`}';`;
1937
2044
  });
1938
2045
  }
1939
2046
  schemasToWrite.push({
@@ -1949,7 +2056,19 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
1949
2056
  const fileContent = generateZodSchemaFileContent(header, schemaGroup);
1950
2057
  await fs.outputFile(schemaGroup[0].filePath, fileContent);
1951
2058
  }
1952
- if (output.indexFiles && uniqueVerbsSchemas.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName), output.namingConvention, true);
2059
+ const writtenSchemaNames = groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName);
2060
+ if (output.indexFiles && !isSplit && uniqueVerbsSchemas.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, writtenSchemaNames, output.namingConvention, true, output.tsconfig);
2061
+ if (isSplit) {
2062
+ const dirSchemas = /* @__PURE__ */ new Map();
2063
+ for (const entry of uniqueVerbsSchemas) {
2064
+ if (useReusableSchemas && entry.schema && typeof entry.schema.$ref === "string" && Object.keys(entry.schema).length === 1) continue;
2065
+ const dir = entry.verbTagDir ?? ROOT_DIR;
2066
+ if (!dirSchemas.has(dir)) dirSchemas.set(dir, []);
2067
+ dirSchemas.get(dir).push(entry.name);
2068
+ }
2069
+ return dirSchemas;
2070
+ }
2071
+ return new Map([[ROOT_DIR, writtenSchemaNames]]);
1953
2072
  }
1954
2073
  //#endregion
1955
2074
  //#region src/write-specs.ts
@@ -1989,6 +2108,10 @@ function getHeader(option, info) {
1989
2108
  /**
1990
2109
  * Add re-export of operation schemas from the main schemas index file.
1991
2110
  * Handles the case where the index file doesn't exist (no regular schemas).
2111
+ *
2112
+ * NOTE: `operationSchemasPath` is a directory, so under NodeNext the re-export
2113
+ * would need an `/index.js` suffix rather than a bare `.js`. That directory-
2114
+ * import case is tracked separately and intentionally left as-is here.
1992
2115
  */
1993
2116
  async function addOperationSchemasReExport(schemaPath, operationSchemasPath, header) {
1994
2117
  const schemaIndexPath = path.join(schemaPath, `index.ts`);
@@ -2010,13 +2133,13 @@ async function addOperationSchemasReExport(schemaPath, operationSchemasPath, hea
2010
2133
  * file path so callers can include it in formatter / hook runs, or
2011
2134
  * `undefined` if no file was written.
2012
2135
  */
2013
- async function writeFakerSchemaMocks(builder, options, header) {
2136
+ async function writeFakerSchemaMocks(builder, options, header, schemaTagMap) {
2014
2137
  const { output } = options;
2015
2138
  const fakerEntry = output.mock.generators.find((g) => !isFunction(g) && g.type === OutputMockType.FAKER && g.schemas === true);
2016
2139
  if (!fakerEntry) return;
2017
2140
  const schemasWithDef = builder.schemas.filter((s) => !!s.schema);
2018
2141
  if (schemasWithDef.length === 0) return;
2019
- const { implementation, imports, strictMockSchemaTypeNames } = generateFakerForSchemas(schemasWithDef, {
2142
+ const { implementation, imports, strictMockSchemaTypeNames, strictMockSchemaKinds } = generateFakerForSchemas(schemasWithDef, {
2020
2143
  spec: builder.spec,
2021
2144
  target: builder.target,
2022
2145
  workspace: "",
@@ -2025,7 +2148,8 @@ async function writeFakerSchemaMocks(builder, options, header) {
2025
2148
  if (!implementation.trim()) return;
2026
2149
  const finalizedImplementation = builder.finalizeMockImplementation ? builder.finalizeMockImplementation(implementation, {
2027
2150
  mockOptions: output.override.mock,
2028
- strictSchemaTypeNames: strictMockSchemaTypeNames
2151
+ strictSchemaTypeNames: strictMockSchemaTypeNames,
2152
+ strictMockSchemaKinds
2029
2153
  }) : implementation;
2030
2154
  let filePath;
2031
2155
  let schemaImportPath;
@@ -2040,9 +2164,32 @@ async function writeFakerSchemaMocks(builder, options, header) {
2040
2164
  filePath = path.join(dir, `schemas.faker${fileExtension}`);
2041
2165
  schemaImportPath = targetInfo ? `./${targetInfo.filename}${getImportExtension(fileExtension, output.tsconfig)}` : void 0;
2042
2166
  }
2043
- const reroutedImports = imports.map((imp) => imp.importPath ? imp : {
2044
- ...imp,
2045
- importPath: schemaImportPath
2167
+ const isZodSchemaOutput = isObject(output.schemas) && output.schemas.type === "zod";
2168
+ const importExtension = getImportExtension(fileExtension, output.tsconfig);
2169
+ const schemaSuffix = isZodSchemaOutput ? ".zod" : "";
2170
+ const perSchemaImportPath = /* @__PURE__ */ new Map();
2171
+ if (schemaImportPath === "." && !output.indexFiles && isObject(output.schemas)) for (const schema of builder.schemas) {
2172
+ const tsName = pascal(schema.name);
2173
+ const fileName = conventionName(schema.name, output.namingConvention);
2174
+ const tagDir = schemaTagMap?.get(schema.name);
2175
+ const tagSegment = tagDir && tagDir !== "." ? `${tagDir}/` : "";
2176
+ perSchemaImportPath.set(tsName, `./${tagSegment}${fileName}${schemaSuffix}${importExtension}`);
2177
+ }
2178
+ const reroutedImports = imports.map((imp) => {
2179
+ if (imp.importPath) return imp;
2180
+ if (imp.schemaFactory) return {
2181
+ ...imp,
2182
+ importPath: "."
2183
+ };
2184
+ const resolved = perSchemaImportPath.get(imp.name);
2185
+ if (resolved) return {
2186
+ ...imp,
2187
+ importPath: resolved
2188
+ };
2189
+ return {
2190
+ ...imp,
2191
+ importPath: schemaImportPath
2192
+ };
2046
2193
  });
2047
2194
  const grouped = /* @__PURE__ */ new Map();
2048
2195
  for (const imp of reroutedImports) {
@@ -2079,28 +2226,62 @@ function shouldGenerateSchemas(output, hasOperations) {
2079
2226
  async function writeSpecs(builder, workspace, options, projectName) {
2080
2227
  const { info, schemas, target } = builder;
2081
2228
  const { output } = options;
2229
+ const shouldSplitSchemasByTags = isObject(output.schemas) && output.schemas.splitByTags;
2230
+ const schemaTagMap = shouldSplitSchemasByTags ? buildSchemaTagMap(Object.values(builder.operations).map((op) => ({
2231
+ imports: op.imports,
2232
+ tags: op.tags
2233
+ })), schemas) : void 0;
2082
2234
  const projectTitle = projectName ?? info.title;
2083
2235
  const header = getHeader(output.override.header, info);
2084
2236
  if (output.schemas) {
2085
2237
  const schemasPath = isString(output.schemas) ? output.schemas : output.schemas.path;
2086
- if (!isString(output.schemas) && output.schemas.type === "zod" || isString(output.schemas) && output.client === "zod" && output.override.zod.generateReusableSchemas) {
2238
+ const isZodSchemas = !isString(output.schemas) && output.schemas.type === "zod" || isString(output.schemas) && output.client === "zod" && output.override.zod.generateReusableSchemas;
2239
+ if (shouldSplitSchemasByTags && output.operationSchemas) throw new Error("schemas.splitByTags cannot be used with output.operationSchemas. The tags-split schema mode handles operation type placement within tag directories.");
2240
+ if (isZodSchemas) {
2087
2241
  const fileExtension = output.schemaFileExtension;
2088
- await writeZodSchemas(builder, schemasPath, fileExtension, header, output, output.override.zod.params ? await generateMutator({
2242
+ const schemasParamsMutator = output.override.zod.params ? await generateMutator({
2089
2243
  output: path.join(schemasPath, `__params__${fileExtension}`),
2090
2244
  mutator: output.override.zod.params,
2091
2245
  name: "zodParams",
2092
2246
  workspace,
2093
2247
  tsconfig: output.tsconfig
2094
- }) : void 0);
2095
- await writeZodSchemasFromVerbs(builder.verbOptions, schemasPath, fileExtension, header, output, {
2096
- spec: builder.spec,
2097
- target: builder.target,
2098
- workspace,
2099
- output
2100
- });
2248
+ }) : void 0;
2249
+ if (shouldSplitSchemasByTags) {
2250
+ const componentDirs = await writeZodSchemas(builder, schemasPath, fileExtension, header, output, schemasParamsMutator, schemaTagMap);
2251
+ const verbDirs = await writeZodSchemasFromVerbs(builder.verbOptions, schemasPath, fileExtension, header, output, {
2252
+ spec: builder.spec,
2253
+ target: builder.target,
2254
+ workspace,
2255
+ output
2256
+ }, schemaTagMap);
2257
+ if (output.indexFiles) await writeZodSchemaTagsSplitBarrel(schemasPath, fileExtension, header, componentDirs, verbDirs, output.namingConvention, output.tsconfig);
2258
+ } else {
2259
+ await writeZodSchemas(builder, schemasPath, fileExtension, header, output, schemasParamsMutator);
2260
+ await writeZodSchemasFromVerbs(builder.verbOptions, schemasPath, fileExtension, header, output, {
2261
+ spec: builder.spec,
2262
+ target: builder.target,
2263
+ workspace,
2264
+ output
2265
+ });
2266
+ }
2101
2267
  } else {
2102
2268
  const fileExtension = output.fileExtension || ".ts";
2103
- if (output.operationSchemas) {
2269
+ if (shouldSplitSchemasByTags) await writeSchemasTagsSplit({
2270
+ schemaPath: schemasPath,
2271
+ schemas,
2272
+ target,
2273
+ namingConvention: output.namingConvention,
2274
+ fileExtension,
2275
+ header,
2276
+ indexFiles: output.indexFiles,
2277
+ tsconfig: output.tsconfig,
2278
+ factoryOutputDirectory: output.factoryMethods?.outputDirectory,
2279
+ operations: Object.values(builder.operations).map((op) => ({
2280
+ imports: op.imports,
2281
+ tags: op.tags
2282
+ }))
2283
+ });
2284
+ else if (output.operationSchemas) {
2104
2285
  const { regularSchemas, operationSchemas: opSchemas } = splitSchemasByType(schemas);
2105
2286
  const regularSchemaNames = new Set(regularSchemas.map((s) => s.name));
2106
2287
  const operationSchemaNames = new Set(opSchemas.map((s) => s.name));
@@ -2144,7 +2325,7 @@ async function writeSpecs(builder, workspace, options, projectName) {
2144
2325
  });
2145
2326
  }
2146
2327
  }
2147
- const fakerSchemaPath = await writeFakerSchemaMocks(builder, options, header);
2328
+ const fakerSchemaPath = await writeFakerSchemaMocks(builder, options, header, schemaTagMap);
2148
2329
  let implementationPaths = [];
2149
2330
  if (output.target) {
2150
2331
  const writeMode = getWriteMode(output.mode);
@@ -2167,6 +2348,7 @@ async function writeSpecs(builder, workspace, options, projectName) {
2167
2348
  projectName,
2168
2349
  header,
2169
2350
  needSchema: shouldGenerateSchemas(output, hasOperations),
2351
+ schemaTagMap,
2170
2352
  generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output, includeZodImport, inlineSchemasParamsMutator, includeParamsImport) : void 0
2171
2353
  });
2172
2354
  }
@@ -2174,7 +2356,11 @@ async function writeSpecs(builder, workspace, options, projectName) {
2174
2356
  const workspacePath = output.workspace;
2175
2357
  const indexFile = path.join(workspacePath, "index.ts");
2176
2358
  const mockExtensions = output.mock.generators.map((g) => getMockFileExtensionByTypeName(g));
2177
- const imports = implementationPaths.filter((p) => mockExtensions.length === 0 || !mockExtensions.some((ext) => p.endsWith(`.${ext}.ts`))).map((p) => upath.getRelativeImportPath(indexFile, getFileInfo(p).pathWithoutExtension, true));
2359
+ const importExtension = getImportExtension(output.fileExtension, output.tsconfig);
2360
+ const imports = implementationPaths.filter((p) => mockExtensions.length === 0 || !mockExtensions.some((ext) => p.endsWith(`.${ext}.ts`))).map((p) => {
2361
+ const relative = upath.getRelativeImportPath(indexFile, p, true);
2362
+ return (relative.endsWith(output.fileExtension) ? relative.slice(0, -output.fileExtension.length) : relative.replace(/\.[^/.]+$/, "")) + importExtension;
2363
+ });
2178
2364
  if (output.schemas) {
2179
2365
  const schemasPath = isString(output.schemas) ? output.schemas : output.schemas.path;
2180
2366
  imports.push(upath.getRelativeImportPath(indexFile, getFileInfo(schemasPath).dirname));
@@ -2326,4 +2512,4 @@ async function loadConfigFile(configFilePath) {
2326
2512
  //#endregion
2327
2513
  export { defineConfig as a, description as c, startWatcher as i, name as l, loadConfigFile as n, defineTransformer as o, generateSpec as r, normalizeOptions as s, findConfigFile as t, version as u };
2328
2514
 
2329
- //# sourceMappingURL=config-DKT1smAv.mjs.map
2515
+ //# sourceMappingURL=config-D-TQRn-G.mjs.map