@orval/core 8.5.2 → 8.6.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.
package/dist/index.mjs CHANGED
@@ -2,10 +2,11 @@ import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import { entries, groupBy, isArray, isBoolean, isBoolean as isBoolean$1, isEmptyish, isFunction, isNullish, isNullish as isNullish$1, isNumber, isString, isString as isString$1, prop, unique, uniqueBy, uniqueWith } from "remeda";
4
4
  import { keyword } from "esutils";
5
- import path from "node:path";
5
+ import nodePath from "node:path";
6
6
  import { compare } from "compare-versions";
7
7
  import debug from "debug";
8
8
  import { pathToFileURL } from "node:url";
9
+ import { createJiti } from "jiti";
9
10
  import fs, { existsSync, readFileSync } from "node:fs";
10
11
  import { globby } from "globby";
11
12
  import readline from "node:readline";
@@ -134,7 +135,7 @@ function isReference(obj) {
134
135
  return !isNullish$1(obj) && Object.hasOwn(obj, "$ref");
135
136
  }
136
137
  function isDirectory(pathValue) {
137
- return !path.extname(pathValue);
138
+ return !nodePath.extname(pathValue);
138
139
  }
139
140
  function isObject(x) {
140
141
  return Object.prototype.toString.call(x) === "[object Object]";
@@ -245,10 +246,10 @@ function camel(s = "") {
245
246
  const camelString = decap(pascal(s), isStartWithUnderscore ? 1 : 0);
246
247
  return isStartWithUnderscore ? `_${camelString}` : camelString;
247
248
  }
248
- function snake(s) {
249
+ function snake(s = "") {
249
250
  return lower(s, "_", true);
250
251
  }
251
- function kebab(s) {
252
+ function kebab(s = "") {
252
253
  return lower(s, "-", true);
253
254
  }
254
255
  function upper(s, fillWith, isDeapostrophe) {
@@ -457,18 +458,32 @@ function keyValuePairsToJsDoc(keyValues) {
457
458
 
458
459
  //#endregion
459
460
  //#region src/utils/dynamic-import.ts
461
+ const TS_MODULE_EXTENSIONS = new Set([
462
+ ".ts",
463
+ ".mts",
464
+ ".cts",
465
+ ".tsx",
466
+ ".jsx"
467
+ ]);
460
468
  async function dynamicImport(toImport, from = process.cwd(), takeDefault = true) {
461
469
  if (!toImport) return toImport;
462
470
  try {
463
471
  if (isString(toImport)) {
464
- const fileUrl = pathToFileURL(path.resolve(from, toImport));
465
- const data = path.extname(fileUrl.href) === ".json" ? await import(fileUrl.href, { with: { type: "json" } }) : await import(fileUrl.href);
472
+ const filePath = nodePath.resolve(from, toImport);
473
+ const extension = nodePath.extname(filePath);
474
+ if (TS_MODULE_EXTENSIONS.has(extension)) {
475
+ const data = await createJiti(from, { interopDefault: true }).import(filePath);
476
+ if (takeDefault && (isObject(data) || isModule(data)) && data.default) return data.default;
477
+ return data;
478
+ }
479
+ const fileUrl = pathToFileURL(filePath);
480
+ const data = extension === ".json" ? await import(fileUrl.href, { with: { type: "json" } }) : await import(fileUrl.href);
466
481
  if (takeDefault && (isObject(data) || isModule(data)) && data.default) return data.default;
467
482
  return data;
468
483
  }
469
484
  return toImport;
470
485
  } catch (error) {
471
- throw new Error(`Oups... 🍻. Path: ${String(toImport)} => ${String(error)}`);
486
+ throw new Error(`Oups... 🍻. Path: ${String(toImport)} => ${String(error)}`, { cause: error });
472
487
  }
473
488
  }
474
489
 
@@ -482,14 +497,14 @@ function getExtension(path) {
482
497
  //#region src/utils/file.ts
483
498
  function getFileInfo(target = "", { backupFilename = "filename", extension = ".ts" } = {}) {
484
499
  const isDir = isDirectory(target);
485
- const filePath = isDir ? path.join(target, backupFilename + extension) : target;
500
+ const filePath = isDir ? nodePath.join(target, backupFilename + extension) : target;
486
501
  return {
487
502
  path: filePath,
488
503
  pathWithoutExtension: filePath.replace(/\.[^/.]+$/, ""),
489
504
  extension,
490
505
  isDirectory: isDir,
491
- dirname: path.dirname(filePath),
492
- filename: path.basename(filePath, extension.startsWith(".") ? extension : `.${extension}`)
506
+ dirname: nodePath.dirname(filePath),
507
+ filename: nodePath.basename(filePath, extension.startsWith(".") ? extension : `.${extension}`)
493
508
  };
494
509
  }
495
510
  async function removeFilesAndEmptyFolders(patterns, dir) {
@@ -565,7 +580,7 @@ function startMessage({ name, version, description }) {
565
580
  return `🍻 ${styleText(["cyan", "bold"], name)} ${styleText("green", `v${version}`)}${description ? ` - ${description}` : ""}`;
566
581
  }
567
582
  function logError(err, tag) {
568
- let message = "";
583
+ let message;
569
584
  if (err instanceof Error) {
570
585
  message = (err.message || err.stack) ?? "Unknown error";
571
586
  if (err.cause) {
@@ -693,13 +708,13 @@ function toUnix(value) {
693
708
  return value;
694
709
  }
695
710
  function join(...args) {
696
- return toUnix(path.join(...args.map((a) => toUnix(a))));
711
+ return toUnix(nodePath.join(...args.map((a) => toUnix(a))));
697
712
  }
698
713
  /**
699
714
  * Behaves exactly like `path.relative(from, to)`, but keeps the first meaningful "./"
700
715
  */
701
716
  function relativeSafe(from, to) {
702
- return normalizeSafe(`.${separator}${toUnix(path.relative(toUnix(from), toUnix(to)))}`);
717
+ return normalizeSafe(`.${separator}${toUnix(nodePath.relative(toUnix(from), toUnix(to)))}`);
703
718
  }
704
719
  function getSchemaFileName(path) {
705
720
  return path.replace(`.${getExtension(path)}`, "").slice(path.lastIndexOf("/") + 1);
@@ -708,13 +723,13 @@ const separator = "/";
708
723
  function normalizeSafe(value) {
709
724
  let result;
710
725
  value = toUnix(value);
711
- result = toUnix(path.normalize(value));
726
+ result = toUnix(nodePath.normalize(value));
712
727
  if (value.startsWith("./") && !result.startsWith("./") && !result.startsWith("..")) result = "./" + result;
713
728
  else if (value.startsWith("//") && !result.startsWith("//")) result = value.startsWith("//./") ? "//." + result : "/" + result;
714
729
  return result;
715
730
  }
716
731
  function joinSafe(...values) {
717
- let result = toUnix(path.join(...values.map((v) => toUnix(v))));
732
+ let result = toUnix(nodePath.join(...values.map((v) => toUnix(v))));
718
733
  if (values.length > 0) {
719
734
  const firstValue = toUnix(values[0]);
720
735
  if (firstValue.startsWith("./") && !result.startsWith("./") && !result.startsWith("..")) result = "./" + result;
@@ -745,14 +760,14 @@ function joinSafe(...values) {
745
760
  * @returns The relative import path string.
746
761
  */
747
762
  function getRelativeImportPath(importerFilePath, exporterFilePath, includeFileExtension = false) {
748
- if (!path.isAbsolute(importerFilePath)) throw new Error(`'importerFilePath' is not an absolute path. "${importerFilePath}"`);
749
- if (!path.isAbsolute(exporterFilePath)) throw new Error(`'exporterFilePath' is not an absolute path. "${exporterFilePath}"`);
750
- const importerDir = path.dirname(importerFilePath);
751
- const relativePath = path.relative(importerDir, exporterFilePath);
752
- let posixPath = path.posix.join(...relativePath.split(path.sep));
763
+ if (!nodePath.isAbsolute(importerFilePath)) throw new Error(`'importerFilePath' is not an absolute path. "${importerFilePath}"`);
764
+ if (!nodePath.isAbsolute(exporterFilePath)) throw new Error(`'exporterFilePath' is not an absolute path. "${exporterFilePath}"`);
765
+ const importerDir = nodePath.dirname(importerFilePath);
766
+ const relativePath = nodePath.relative(importerDir, exporterFilePath);
767
+ let posixPath = nodePath.posix.join(...relativePath.split(nodePath.sep));
753
768
  if (!posixPath.startsWith("./") && !posixPath.startsWith("../")) posixPath = `./${posixPath}`;
754
769
  if (!includeFileExtension) {
755
- const ext = path.extname(posixPath);
770
+ const ext = nodePath.extname(posixPath);
756
771
  if (ext && posixPath.endsWith(ext)) posixPath = posixPath.slice(0, -ext.length);
757
772
  }
758
773
  return posixPath;
@@ -762,20 +777,20 @@ function getRelativeImportPath(importerFilePath, exporterFilePath, includeFileEx
762
777
  //#region src/utils/resolve-version.ts
763
778
  function resolveInstalledVersion(packageName, fromDir) {
764
779
  try {
765
- const require = createRequire(path.join(fromDir, "noop.js"));
780
+ const require = createRequire(nodePath.join(fromDir, "noop.js"));
766
781
  try {
767
782
  return require(`${packageName}/package.json`).version;
768
783
  } catch (directError) {
769
784
  if (directError instanceof Error && "code" in directError && directError.code === "ERR_PACKAGE_PATH_NOT_EXPORTED") {
770
785
  const entryPath = require.resolve(packageName);
771
- let dir = path.dirname(entryPath);
772
- while (dir !== path.parse(dir).root) {
773
- const pkgPath = path.join(dir, "package.json");
786
+ let dir = nodePath.dirname(entryPath);
787
+ while (dir !== nodePath.parse(dir).root) {
788
+ const pkgPath = nodePath.join(dir, "package.json");
774
789
  if (existsSync(pkgPath)) {
775
790
  const pkgData = JSON.parse(readFileSync(pkgPath, "utf8"));
776
791
  if (pkgData.name === packageName) return pkgData.version;
777
792
  }
778
- dir = path.dirname(dir);
793
+ dir = nodePath.dirname(dir);
779
794
  }
780
795
  return;
781
796
  }
@@ -817,7 +832,7 @@ const sortByPriority = (arr) => arr.toSorted((a, b) => {
817
832
  * Handles strings, numbers, booleans, functions, arrays, and objects.
818
833
  *
819
834
  * @param data - The data to stringify. Can be a string, array, object, number, boolean, function, null, or undefined.
820
- * @returns A string representation of the data, or undefined if data is null or undefined.
835
+ * @returns A string representation of the data, `null` for null, or undefined if data is undefined.
821
836
  * @example
822
837
  * stringify('hello') // returns "'hello'"
823
838
  * stringify(42) // returns "42"
@@ -825,7 +840,8 @@ const sortByPriority = (arr) => arr.toSorted((a, b) => {
825
840
  * stringify({ a: 1, b: 'test' }) // returns "{ a: 1, b: 'test', }"
826
841
  */
827
842
  function stringify(data) {
828
- if (isNullish$1(data)) return;
843
+ if (data === void 0) return;
844
+ if (data === null) return "null";
829
845
  if (isString(data)) return `'${data.replaceAll("'", String.raw`\'`)}'`;
830
846
  if (isNumber(data) || isBoolean(data) || isFunction(data)) return String(data);
831
847
  if (Array.isArray(data)) return `[${data.map((item) => stringify(item)).join(", ")}]`;
@@ -918,17 +934,31 @@ function getNumberWord(num) {
918
934
  return [...num.toString()].reduce((acc, n) => acc + NUMBERS[n], "");
919
935
  }
920
936
  /**
921
- * Escapes a specific character in a string by prefixing it with a backslash.
937
+ * Escapes a specific character in a string by prefixing all of its occurrences with a backslash.
922
938
  *
923
939
  * @param str - The string to escape, or null.
924
940
  * @param char - The character to escape. Defaults to single quote (').
925
941
  * @returns The escaped string, or null if the input is null.
926
942
  * @example
927
943
  * escape("don't") // returns "don\'t"
944
+ * escape("it's John's") // returns "it\'s John\'s"
928
945
  * escape('say "hello"', '"') // returns 'say \\"hello\\"'
946
+ * escape("a'''b", "'") // returns "a\'\'\'b"
929
947
  */
930
948
  function escape(str, char = "'") {
931
- return str?.replace(char, `\\${char}`);
949
+ return str?.replaceAll(char, `\\${char}`);
950
+ }
951
+ /**
952
+ * Escapes regular expression metacharacters in a string so it can be safely
953
+ * embedded inside a RegExp pattern.
954
+ *
955
+ * @param value - The raw string value to escape for regex usage.
956
+ * @returns The escaped string.
957
+ * @example
958
+ * escapeRegExp('foo$bar') // returns 'foo\\$bar'
959
+ */
960
+ function escapeRegExp(value) {
961
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
932
962
  }
933
963
  /**
934
964
  * Escape all characters not included in SingleStringCharacters and
@@ -996,6 +1026,38 @@ function isSyntheticDefaultImportsAllow(config) {
996
1026
 
997
1027
  //#endregion
998
1028
  //#region src/getters/enum.ts
1029
+ /**
1030
+ * Map of special characters to semantic word replacements.
1031
+ *
1032
+ * Applied before naming convention transforms (PascalCase, camelCase, …) so
1033
+ * that characters which would otherwise be stripped still contribute a unique
1034
+ * segment to the generated key. Without this, values like "created_at" and
1035
+ * "-created_at" both PascalCase to "CreatedAt", silently overwriting one
1036
+ * another in the generated const/enum object.
1037
+ *
1038
+ * Only characters that appear as leading/trailing modifiers in real-world
1039
+ * OpenAPI enums are mapped — the list is intentionally conservative to avoid
1040
+ * changing output for schemas that don't hit collisions.
1041
+ */
1042
+ const ENUM_SPECIAL_CHARACTER_MAP = {
1043
+ "-": "minus",
1044
+ "+": "plus"
1045
+ };
1046
+ /**
1047
+ * Replace special characters with semantic words (plus an underscore separator)
1048
+ * so that naming convention transforms (PascalCase, etc.) produce unique keys.
1049
+ *
1050
+ * The trailing underscore acts as a word boundary so that PascalCase treats the
1051
+ * replacement as a separate word: "-created_at" → "minus_created_at" → "MinusCreatedAt".
1052
+ */
1053
+ function replaceSpecialCharacters(key) {
1054
+ let result = "";
1055
+ for (const char of key) {
1056
+ const replacement = ENUM_SPECIAL_CHARACTER_MAP[char];
1057
+ result += replacement ? replacement + "_" : char;
1058
+ }
1059
+ return result;
1060
+ }
999
1061
  function getEnumNames(schemaObject) {
1000
1062
  const names = schemaObject?.["x-enumNames"] ?? schemaObject?.["x-enumnames"] ?? schemaObject?.["x-enum-varnames"];
1001
1063
  if (!names) return;
@@ -1023,9 +1085,33 @@ const getTypeConstEnum = (value, enumName, names, descriptions, enumNamingConven
1023
1085
  enumValue += `export const ${enumName} = {\n${implementation}} as const;\n`;
1024
1086
  return enumValue;
1025
1087
  };
1088
+ /**
1089
+ * Derive the object/enum key for a single enum value.
1090
+ *
1091
+ * Handles numeric prefixes, sanitization, and optional naming convention
1092
+ * transforms. When `disambiguate` is true, special characters (-/+) are
1093
+ * replaced with semantic words before the convention transform to prevent
1094
+ * key collisions.
1095
+ */
1096
+ function deriveEnumKey(val, enumNamingConvention, disambiguate = false) {
1097
+ let key = val.startsWith("'") ? val.slice(1, -1) : val;
1098
+ if (isNumeric(key)) key = toNumberKey(key);
1099
+ if (key.length > 1) key = sanitize(key, {
1100
+ whitespace: "_",
1101
+ underscore: true,
1102
+ dash: true,
1103
+ special: true
1104
+ });
1105
+ if (enumNamingConvention) {
1106
+ if (disambiguate) key = replaceSpecialCharacters(key);
1107
+ key = conventionName(key, enumNamingConvention);
1108
+ }
1109
+ return key;
1110
+ }
1026
1111
  function getEnumImplementation(value, names, descriptions, enumNamingConvention) {
1027
1112
  if (value === "") return "";
1028
1113
  const uniqueValues = [...new Set(value.split(" | "))];
1114
+ const disambiguate = !!enumNamingConvention && new Set(uniqueValues.map((v) => deriveEnumKey(v, enumNamingConvention))).size < uniqueValues.length;
1029
1115
  let result = "";
1030
1116
  for (const [index, val] of uniqueValues.entries()) {
1031
1117
  const name = names?.[index];
@@ -1035,15 +1121,7 @@ function getEnumImplementation(value, names, descriptions, enumNamingConvention)
1035
1121
  result += comment + ` ${keyword.isIdentifierNameES5(name) ? name : `'${name}'`}: ${val},\n`;
1036
1122
  continue;
1037
1123
  }
1038
- let key = val.startsWith("'") ? val.slice(1, -1) : val;
1039
- if (isNumeric(key)) key = toNumberKey(key);
1040
- if (key.length > 1) key = sanitize(key, {
1041
- whitespace: "_",
1042
- underscore: true,
1043
- dash: true,
1044
- special: true
1045
- });
1046
- if (enumNamingConvention) key = conventionName(key, enumNamingConvention);
1124
+ const key = deriveEnumKey(val, enumNamingConvention, disambiguate);
1047
1125
  result += comment + ` ${keyword.isIdentifierNameES5(key) ? key : `'${key}'`}: ${val},\n`;
1048
1126
  }
1049
1127
  return result;
@@ -1054,6 +1132,7 @@ const getNativeEnum = (value, enumName, names, enumNamingConvention) => {
1054
1132
  const getNativeEnumItems = (value, names, enumNamingConvention) => {
1055
1133
  if (value === "") return "";
1056
1134
  const uniqueValues = [...new Set(value.split(" | "))];
1135
+ const disambiguate = !!enumNamingConvention && new Set(uniqueValues.map((v) => deriveEnumKey(v, enumNamingConvention))).size < uniqueValues.length;
1057
1136
  let result = "";
1058
1137
  for (const [index, val] of uniqueValues.entries()) {
1059
1138
  const name = names?.[index];
@@ -1061,15 +1140,7 @@ const getNativeEnumItems = (value, names, enumNamingConvention) => {
1061
1140
  result += ` ${keyword.isIdentifierNameES5(name) ? name : `'${name}'`}= ${val},\n`;
1062
1141
  continue;
1063
1142
  }
1064
- let key = val.startsWith("'") ? val.slice(1, -1) : val;
1065
- if (isNumeric(key)) key = toNumberKey(key);
1066
- if (key.length > 1) key = sanitize(key, {
1067
- whitespace: "_",
1068
- underscore: true,
1069
- dash: true,
1070
- special: true
1071
- });
1072
- if (enumNamingConvention) key = conventionName(key, enumNamingConvention);
1143
+ const key = deriveEnumKey(val, enumNamingConvention, disambiguate);
1073
1144
  result += ` ${keyword.isIdentifierNameES5(key) ? key : `'${key}'`}= ${val},\n`;
1074
1145
  }
1075
1146
  return result;
@@ -1176,7 +1247,7 @@ function getRefInfo($ref, context) {
1176
1247
  return firstLevel[paths[1]]?.suffix ?? "";
1177
1248
  };
1178
1249
  const suffix = getOverrideSuffix(context.output.override, refPaths);
1179
- const originalName = ref ? refPaths[refPaths.length - 1] : getSchemaFileName(pathname);
1250
+ const originalName = ref ? refPaths.at(-1) ?? "" : getSchemaFileName(pathname);
1180
1251
  if (!pathname) return {
1181
1252
  name: sanitize(pascal(originalName) + suffix, {
1182
1253
  es5keyword: true,
@@ -1263,10 +1334,9 @@ function getSchema$1(schema, context) {
1263
1334
  if (!schema.$ref) throw new Error(`${REF_NOT_FOUND_PREFIX}: missing $ref`);
1264
1335
  const refInfo = getRefInfo(schema.$ref, context);
1265
1336
  const { refPaths } = refInfo;
1266
- let schemaByRefPaths = Array.isArray(refPaths) ? prop(context.spec, ...refPaths) : void 0;
1267
- schemaByRefPaths ??= context.spec;
1268
- if (isReference(schemaByRefPaths)) return getSchema$1(schemaByRefPaths, context);
1269
- let currentSchema = schemaByRefPaths || context.spec;
1337
+ const schemaByRefPaths = Array.isArray(refPaths) ? prop(context.spec, ...refPaths) : void 0;
1338
+ if (isObject(schemaByRefPaths) && isReference(schemaByRefPaths)) return getSchema$1(schemaByRefPaths, context);
1339
+ let currentSchema = schemaByRefPaths;
1270
1340
  if (isObject(currentSchema) && "nullable" in schema) {
1271
1341
  const nullable = schema.nullable;
1272
1342
  currentSchema = {
@@ -1503,7 +1573,7 @@ function getArray({ schema, name, context, formDataContext }) {
1503
1573
  example: schemaExample,
1504
1574
  examples: resolveExampleRefs(schemaExamples, context)
1505
1575
  };
1506
- } else if (compareVersions(context.spec.openapi, "3.1", ">=")) return {
1576
+ } else if (compareVersions(context.spec.openapi ?? "3.0.0", "3.1", ">=")) return {
1507
1577
  value: "unknown[]",
1508
1578
  imports: [],
1509
1579
  schemas: [],
@@ -1561,11 +1631,12 @@ function getResReqTypes(responsesOrRequests, name, context, defaultType = "unkno
1561
1631
  isEnum: false,
1562
1632
  isRef: true,
1563
1633
  hasReadonlyProps: false,
1634
+ dependencies: [name],
1564
1635
  originalSchema: void 0,
1565
1636
  example: void 0,
1566
1637
  examples: void 0,
1567
1638
  key,
1568
- contentType: void 0
1639
+ contentType: ""
1569
1640
  }];
1570
1641
  const [contentType, mediaType] = firstEntry;
1571
1642
  const isFormData = formDataContentTypes.has(contentType);
@@ -1581,6 +1652,7 @@ function getResReqTypes(responsesOrRequests, name, context, defaultType = "unkno
1581
1652
  isEnum: false,
1582
1653
  isRef: true,
1583
1654
  hasReadonlyProps: false,
1655
+ dependencies: [name],
1584
1656
  originalSchema: mediaType.schema,
1585
1657
  example: mediaType.example,
1586
1658
  examples: resolveExampleRefs(mediaType.examples, context),
@@ -1618,6 +1690,7 @@ function getResReqTypes(responsesOrRequests, name, context, defaultType = "unkno
1618
1690
  type: "unknown",
1619
1691
  isEnum: false,
1620
1692
  hasReadonlyProps: false,
1693
+ dependencies: [name],
1621
1694
  formData,
1622
1695
  formUrlEncoded,
1623
1696
  isRef: true,
@@ -1662,6 +1735,7 @@ function getResReqTypes(responsesOrRequests, name, context, defaultType = "unkno
1662
1735
  if (!isFormData && !isFormUrlEncoded || !effectivePropName || !mediaType.schema) return {
1663
1736
  ...resolvedValue,
1664
1737
  imports: resolvedValue.imports,
1738
+ dependencies: resolvedValue.dependencies,
1665
1739
  contentType,
1666
1740
  example: mediaType.example,
1667
1741
  examples: resolveExampleRefs(mediaType.examples, context)
@@ -1716,6 +1790,7 @@ function getResReqTypes(responsesOrRequests, name, context, defaultType = "unkno
1716
1790
  schemas: [],
1717
1791
  type: defaultType,
1718
1792
  isEnum: false,
1793
+ dependencies: [],
1719
1794
  key,
1720
1795
  isRef: false,
1721
1796
  hasReadonlyProps: false,
@@ -1927,7 +2002,7 @@ function getBody({ requestBody, operationName, context, contentType }) {
1927
2002
  const imports = filteredBodyTypes.flatMap(({ imports }) => imports);
1928
2003
  const schemas = filteredBodyTypes.flatMap(({ schemas }) => schemas);
1929
2004
  const definition = filteredBodyTypes.map(({ value }) => value).join(" | ");
1930
- const nonReadonlyDefinition = filteredBodyTypes.some((x) => x.hasReadonlyProps) && definition ? `NonReadonly<${definition}>` : definition;
2005
+ const nonReadonlyDefinition = filteredBodyTypes.some((x) => x.hasReadonlyProps) && definition && context.output.override.preserveReadonlyRequestBodies !== "preserve" ? `NonReadonly<${definition}>` : definition;
1931
2006
  let implementation = generalJSTypesWithArray.includes(definition.toLowerCase()) || filteredBodyTypes.length > 1 ? camel(operationName) + context.output.override.components.requestBodies.suffix : camel(definition);
1932
2007
  let isOptional = false;
1933
2008
  if (implementation) {
@@ -2037,17 +2112,22 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2037
2112
  examples: resolveExampleRefs(item.examples, context)
2038
2113
  };
2039
2114
  }
2040
- if (item.allOf || item.oneOf || item.anyOf) return combineSchemas({
2041
- schema: item,
2115
+ const schemaItem = item;
2116
+ const itemAllOf = schemaItem.allOf;
2117
+ const itemOneOf = schemaItem.oneOf;
2118
+ const itemAnyOf = schemaItem.anyOf;
2119
+ const itemType = schemaItem.type;
2120
+ if (itemAllOf || itemOneOf || itemAnyOf) return combineSchemas({
2121
+ schema: schemaItem,
2042
2122
  name,
2043
- separator: item.allOf ? "allOf" : item.oneOf ? "oneOf" : "anyOf",
2123
+ separator: itemAllOf ? "allOf" : itemOneOf ? "oneOf" : "anyOf",
2044
2124
  context,
2045
2125
  nullable,
2046
2126
  formDataContext
2047
2127
  });
2048
- if (Array.isArray(item.type)) {
2049
- const typeArray = item.type;
2050
- const baseItem = item;
2128
+ if (Array.isArray(itemType)) {
2129
+ const typeArray = itemType;
2130
+ const baseItem = schemaItem;
2051
2131
  return combineSchemas({
2052
2132
  schema: { anyOf: typeArray.map((type) => ({
2053
2133
  ...baseItem,
@@ -2059,7 +2139,7 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2059
2139
  nullable
2060
2140
  });
2061
2141
  }
2062
- const itemProperties = item.properties;
2142
+ const itemProperties = schemaItem.properties;
2063
2143
  if (itemProperties && Object.entries(itemProperties).length > 0) {
2064
2144
  const entries = Object.entries(itemProperties);
2065
2145
  if (context.output.propertySortOrder === PropertySortOrder.ALPHABETICAL) entries.sort((a, b) => {
@@ -2072,14 +2152,13 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2072
2152
  isEnum: false,
2073
2153
  type: "object",
2074
2154
  isRef: false,
2075
- schema: {},
2076
2155
  hasReadonlyProps: false,
2077
2156
  useTypeAlias: false,
2078
2157
  dependencies: [],
2079
- example: item.example,
2080
- examples: resolveExampleRefs(item.examples, context)
2158
+ example: schemaItem.example,
2159
+ examples: resolveExampleRefs(schemaItem.examples, context)
2081
2160
  };
2082
- const itemRequired = item.required;
2161
+ const itemRequired = schemaItem.required;
2083
2162
  for (const [index, [key, schema]] of entries.entries()) {
2084
2163
  const isRequired = (Array.isArray(itemRequired) ? itemRequired : []).includes(key);
2085
2164
  let propName = "";
@@ -2099,10 +2178,11 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2099
2178
  context,
2100
2179
  formDataContext: propertyFormDataContext
2101
2180
  });
2102
- const isReadOnly = item.readOnly ?? schema.readOnly;
2181
+ const isReadOnly = Boolean(schemaItem.readOnly) || Boolean(schema.readOnly);
2103
2182
  if (!index) acc.value += "{";
2104
2183
  const doc = jsDoc(schema, true, context);
2105
- if (isReadOnly ?? false) acc.hasReadonlyProps = true;
2184
+ const propertyDoc = doc ? `${doc.trimEnd().split("\n").map((line) => ` ${line}`).join("\n")}\n` : "";
2185
+ if (isReadOnly) acc.hasReadonlyProps = true;
2106
2186
  const constValue = "const" in schema ? schema.const : void 0;
2107
2187
  const hasConst = constValue !== void 0;
2108
2188
  let constLiteral;
@@ -2110,6 +2190,7 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2110
2190
  else if (isString(constValue)) constLiteral = `'${escape(constValue)}'`;
2111
2191
  else constLiteral = JSON.stringify(constValue);
2112
2192
  const needsValueImport = hasConst && (resolvedValue.isEnum || resolvedValue.type === "enum");
2193
+ const usedResolvedValue = !hasConst || needsValueImport;
2113
2194
  const aliasedImports = needsValueImport ? resolvedValue.imports.map((imp) => ({
2114
2195
  ...imp,
2115
2196
  isConstant: true
@@ -2126,19 +2207,21 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2126
2207
  });
2127
2208
  const propValue = needsValueImport ? alias : constLiteral ?? alias;
2128
2209
  const finalPropValue = isRequired ? propValue : context.output.override.useNullForOptional === true ? `${propValue} | null` : propValue;
2129
- acc.value += `\n ${doc ? `${doc} ` : ""}${isReadOnly && !context.output.override.suppressReadonlyModifier ? "readonly " : ""}${getKey(key)}${isRequired ? "" : "?"}: ${finalPropValue};`;
2130
- acc.schemas.push(...resolvedValue.schemas);
2131
- acc.dependencies.push(...resolvedValue.dependencies);
2210
+ acc.value += `\n${propertyDoc}${isReadOnly && !context.output.override.suppressReadonlyModifier ? " readonly " : " "}${getKey(key)}${isRequired ? "" : "?"}: ${finalPropValue};`;
2211
+ if (usedResolvedValue) {
2212
+ acc.schemas.push(...resolvedValue.schemas);
2213
+ acc.dependencies.push(...resolvedValue.dependencies);
2214
+ }
2132
2215
  if (entries.length - 1 === index) {
2133
- const additionalProps = item.additionalProperties;
2134
- if (additionalProps) if (isBoolean(additionalProps)) {
2135
- const recordType = getPropertyNamesRecordType(item, "unknown");
2216
+ const additionalProps = schemaItem.additionalProperties;
2217
+ if (additionalProps) if (additionalProps === true) {
2218
+ const recordType = getPropertyNamesRecordType(schemaItem, "unknown");
2136
2219
  if (recordType) {
2137
2220
  acc.value += "\n}";
2138
2221
  acc.value += ` & ${recordType}`;
2139
2222
  acc.useTypeAlias = true;
2140
2223
  } else {
2141
- const keyType = getIndexSignatureKey(item);
2224
+ const keyType = getIndexSignatureKey(schemaItem);
2142
2225
  acc.value += `\n [key: ${keyType}]: unknown;\n }`;
2143
2226
  }
2144
2227
  } else {
@@ -2147,13 +2230,13 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2147
2230
  name,
2148
2231
  context
2149
2232
  });
2150
- const recordType = getPropertyNamesRecordType(item, resolvedValue.value);
2233
+ const recordType = getPropertyNamesRecordType(schemaItem, resolvedValue.value);
2151
2234
  if (recordType) {
2152
2235
  acc.value += "\n}";
2153
2236
  acc.value += ` & ${recordType}`;
2154
2237
  acc.useTypeAlias = true;
2155
2238
  } else {
2156
- const keyType = getIndexSignatureKey(item);
2239
+ const keyType = getIndexSignatureKey(schemaItem);
2157
2240
  acc.value += `\n [key: ${keyType}]: ${resolvedValue.value};\n}`;
2158
2241
  }
2159
2242
  acc.dependencies.push(...resolvedValue.dependencies);
@@ -2164,11 +2247,11 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2164
2247
  }
2165
2248
  return acc;
2166
2249
  }
2167
- const outerAdditionalProps = item.additionalProperties;
2168
- const readOnlyFlag = item.readOnly;
2250
+ const outerAdditionalProps = schemaItem.additionalProperties;
2251
+ const readOnlyFlag = schemaItem.readOnly;
2169
2252
  if (outerAdditionalProps) {
2170
- if (isBoolean(outerAdditionalProps)) {
2171
- const recordType = getPropertyNamesRecordType(item, "unknown");
2253
+ if (outerAdditionalProps === true) {
2254
+ const recordType = getPropertyNamesRecordType(schemaItem, "unknown");
2172
2255
  if (recordType) return {
2173
2256
  value: recordType + nullable,
2174
2257
  imports: [],
@@ -2181,7 +2264,7 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2181
2264
  dependencies: []
2182
2265
  };
2183
2266
  return {
2184
- value: `{ [key: ${getIndexSignatureKey(item)}]: unknown }` + nullable,
2267
+ value: `{ [key: ${getIndexSignatureKey(schemaItem)}]: unknown }` + nullable,
2185
2268
  imports: [],
2186
2269
  schemas: [],
2187
2270
  isEnum: false,
@@ -2197,7 +2280,7 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2197
2280
  name,
2198
2281
  context
2199
2282
  });
2200
- const recordType = getPropertyNamesRecordType(item, resolvedValue.value);
2283
+ const recordType = getPropertyNamesRecordType(schemaItem, resolvedValue.value);
2201
2284
  if (recordType) return {
2202
2285
  value: recordType + nullable,
2203
2286
  imports: resolvedValue.imports,
@@ -2210,7 +2293,7 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2210
2293
  dependencies: resolvedValue.dependencies
2211
2294
  };
2212
2295
  return {
2213
- value: `{[key: ${getIndexSignatureKey(item)}]: ${resolvedValue.value}}` + nullable,
2296
+ value: `{[key: ${getIndexSignatureKey(schemaItem)}]: ${resolvedValue.value}}` + nullable,
2214
2297
  imports: resolvedValue.imports,
2215
2298
  schemas: resolvedValue.schemas,
2216
2299
  isEnum: false,
@@ -2221,20 +2304,29 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2221
2304
  dependencies: resolvedValue.dependencies
2222
2305
  };
2223
2306
  }
2224
- const constValue = item.const;
2225
- if (constValue) return {
2226
- value: `'${constValue}'`,
2227
- imports: [],
2228
- schemas: [],
2229
- isEnum: false,
2230
- type: "string",
2231
- isRef: false,
2232
- hasReadonlyProps: readOnlyFlag ?? false,
2233
- dependencies: []
2234
- };
2235
- const keyType = item.type === "object" ? getIndexSignatureKey(item) : "string";
2236
- const recordType = getPropertyNamesRecordType(item, "unknown");
2237
- if (item.type === "object" && recordType) return {
2307
+ const constValue = schemaItem.const;
2308
+ if (constValue !== void 0) {
2309
+ let type;
2310
+ if (Array.isArray(constValue)) type = "array";
2311
+ else if (constValue === null) type = "null";
2312
+ else if (typeof constValue === "string") type = "string";
2313
+ else if (typeof constValue === "number") type = "number";
2314
+ else if (typeof constValue === "boolean") type = "boolean";
2315
+ else type = "object";
2316
+ return {
2317
+ value: typeof constValue === "string" ? `'${escape(constValue)}'` : JSON.stringify(constValue),
2318
+ imports: [],
2319
+ schemas: [],
2320
+ isEnum: false,
2321
+ type,
2322
+ isRef: false,
2323
+ hasReadonlyProps: readOnlyFlag ?? false,
2324
+ dependencies: []
2325
+ };
2326
+ }
2327
+ const keyType = itemType === "object" ? getIndexSignatureKey(schemaItem) : "string";
2328
+ const recordType = getPropertyNamesRecordType(schemaItem, "unknown");
2329
+ if (itemType === "object" && recordType) return {
2238
2330
  value: recordType + nullable,
2239
2331
  imports: [],
2240
2332
  schemas: [],
@@ -2246,7 +2338,7 @@ function getObject({ item, name, context, nullable, formDataContext }) {
2246
2338
  dependencies: []
2247
2339
  };
2248
2340
  return {
2249
- value: (item.type === "object" ? `{ [key: ${keyType}]: unknown }` : "unknown") + nullable,
2341
+ value: (itemType === "object" ? `{ [key: ${keyType}]: unknown }` : "unknown") + nullable,
2250
2342
  imports: [],
2251
2343
  schemas: [],
2252
2344
  isEnum: false,
@@ -2434,7 +2526,7 @@ function normalizeAllOfSchema(schema) {
2434
2526
  let didMerge = false;
2435
2527
  const schemaProperties = schema.properties;
2436
2528
  const schemaRequired = schema.required;
2437
- const mergedProperties = { ...schemaProperties };
2529
+ const mergedProperties = schemaProperties ? { ...schemaProperties } : {};
2438
2530
  const mergedRequired = new Set(schemaRequired);
2439
2531
  const remainingAllOf = [];
2440
2532
  for (const subSchema of schemaAllOf) {
@@ -2581,15 +2673,18 @@ function combineSchemas({ name, schema, separator, context, nullable, formDataCo
2581
2673
  };
2582
2674
  }
2583
2675
  let resolvedValue;
2584
- if (normalizedSchema.properties) resolvedValue = getScalar({
2676
+ const normalizedProperties = normalizedSchema.properties;
2677
+ const schemaOneOf = schema.oneOf;
2678
+ const schemaAnyOf = schema.anyOf;
2679
+ if (normalizedProperties) resolvedValue = getScalar({
2585
2680
  item: Object.fromEntries(Object.entries(normalizedSchema).filter(([key]) => key !== separator)),
2586
2681
  name,
2587
2682
  context,
2588
2683
  formDataContext
2589
2684
  });
2590
- else if (separator === "allOf" && (schema.oneOf || schema.anyOf)) {
2591
- const siblingCombiner = schema.oneOf ? "oneOf" : "anyOf";
2592
- const siblingSchemas = schema[siblingCombiner];
2685
+ else if (separator === "allOf" && (schemaOneOf || schemaAnyOf)) {
2686
+ const siblingCombiner = schemaOneOf ? "oneOf" : "anyOf";
2687
+ const siblingSchemas = schemaOneOf ?? schemaAnyOf;
2593
2688
  resolvedValue = combineSchemas({
2594
2689
  schema: { [siblingCombiner]: siblingSchemas },
2595
2690
  name,
@@ -2783,8 +2878,8 @@ function getProps({ body, queryParams, params, operationName, headers, context }
2783
2878
  const parameterTypeName = `${pascal(operationName)}PathParameters`;
2784
2879
  const name = "pathParams";
2785
2880
  const namedParametersTypeDefinition = `export type ${parameterTypeName} = {\n ${params.map((property) => property.definition).join(",\n ")},\n }`;
2786
- const isOptional = context.output.optionsParamRequired || params.every((param) => param.default);
2787
- const implementation = `{ ${params.map((property) => property.default ? `${property.name} = ${property.default}` : property.name).join(", ")} }: ${parameterTypeName}${isOptional ? " = {}" : ""}`;
2881
+ const isOptional = context.output.optionsParamRequired || params.every((param) => param.default !== void 0);
2882
+ const implementation = `{ ${params.map((property) => property.default === void 0 ? property.name : `${property.name} = ${stringify(property.default)}`).join(", ")} }: ${parameterTypeName}${isOptional ? " = {}" : ""}`;
2788
2883
  const destructured = `{ ${params.map((property) => property.name).join(", ")} }`;
2789
2884
  paramGetterProps = [{
2790
2885
  type: GetterPropType.NAMED_PATH_PARAMS,
@@ -2949,13 +3044,15 @@ const getRoutePath = (path) => {
2949
3044
  const matches = /([^{]*){?([\w*_-]*)}?(.*)/.exec(path);
2950
3045
  if (!matches?.length) return path;
2951
3046
  const prev = matches[1];
2952
- const param = sanitize(camel(matches[2]), {
3047
+ const rawParam = matches[2];
3048
+ const rest = matches[3];
3049
+ const param = sanitize(camel(rawParam), {
2953
3050
  es5keyword: true,
2954
3051
  underscore: true,
2955
3052
  dash: true,
2956
3053
  dot: true
2957
3054
  });
2958
- const next = hasParam(matches[3]) ? getRoutePath(matches[3]) : matches[3];
3055
+ const next = hasParam(rest) ? getRoutePath(rest) : rest;
2959
3056
  return hasParam(path) ? `${prev}\${${param}}${next}` : `${prev}${param}${next}`;
2960
3057
  };
2961
3058
  function getRoute(route) {
@@ -2975,13 +3072,14 @@ function getFullRoute(route, servers, baseUrl) {
2975
3072
  if (!servers) throw new Error("Orval is configured to use baseUrl from the specifications 'servers' field, but there exist no servers in the specification.");
2976
3073
  const server = servers.at(Math.min(baseUrl.index ?? 0, servers.length - 1));
2977
3074
  if (!server) return "";
2978
- if (!server.variables) return server.url;
2979
- let url = server.url;
3075
+ const serverUrl = server.url ?? "";
3076
+ if (!server.variables) return serverUrl;
3077
+ let url = serverUrl;
2980
3078
  const variables = baseUrl.variables;
2981
3079
  for (const variableKey of Object.keys(server.variables)) {
2982
3080
  const variable = server.variables[variableKey];
2983
3081
  if (variables?.[variableKey]) {
2984
- if (variable.enum && !variable.enum.some((e) => e == variables[variableKey])) throw new Error(`Invalid variable value '${variables[variableKey]}' for variable '${variableKey}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`);
3082
+ if (variable.enum && !variable.enum.some((e) => e == variables[variableKey])) throw new Error(`Invalid variable value '${variables[variableKey]}' for variable '${variableKey}' when resolving ${serverUrl}. Valid values are: ${variable.enum.join(", ")}.`);
2985
3083
  url = url.replaceAll(`{${variableKey}}`, variables[variableKey]);
2986
3084
  } else url = url.replaceAll(`{${variableKey}}`, String(variable.default));
2987
3085
  }
@@ -3081,7 +3179,8 @@ function generateDependency({ deps, isAllowSyntheticDefaultImports, dependency,
3081
3179
  }
3082
3180
  function addDependency({ implementation, exports, dependency, projectName, isAllowSyntheticDefaultImports }) {
3083
3181
  const toAdds = exports.filter((e) => {
3084
- const searchWords = [e.alias, e.name].filter((p) => p?.length).join("|");
3182
+ const searchWords = [e.alias, e.name].filter((p) => Boolean(p?.length)).map((part) => escapeRegExp(part)).join("|");
3183
+ if (!searchWords) return false;
3085
3184
  const pattern = new RegExp(String.raw`\b(${searchWords})\b`, "g");
3086
3185
  return implementation.match(pattern);
3087
3186
  });
@@ -3124,8 +3223,7 @@ function addDependency({ implementation, exports, dependency, projectName, isAll
3124
3223
  }).join("\n") + "\n";
3125
3224
  }
3126
3225
  function getLibName(code) {
3127
- const splitString = code.split(" from ");
3128
- return splitString[splitString.length - 1].split(";")[0].trim();
3226
+ return (code.split(" from ").at(-1) ?? "").split(";")[0].trim();
3129
3227
  }
3130
3228
  function generateDependencyImports(implementation, imports, projectName, hasSchemaDir, isAllowSyntheticDefaultImports) {
3131
3229
  const dependencies = imports.map((dep) => addDependency({
@@ -3170,7 +3268,7 @@ function generateModelInline(acc, model) {
3170
3268
  return acc + `${model}\n`;
3171
3269
  }
3172
3270
  function generateModelsInline(obj) {
3173
- const schemas = Object.values(obj).flat();
3271
+ const schemas = Array.isArray(obj) ? obj : Object.values(obj).flat();
3174
3272
  let result = "";
3175
3273
  for (const { model } of schemas) result = generateModelInline(result, model);
3176
3274
  return result;
@@ -3334,9 +3432,14 @@ function removeComments(file) {
3334
3432
  * (e.g. observe-mode branches), prefer getAngularFilteredParamsCallExpression +
3335
3433
  * getAngularFilteredParamsHelperBody instead.
3336
3434
  */
3337
- const getAngularFilteredParamsExpression = (paramsExpression, requiredNullableParamKeys = []) => `(() => {
3435
+ const getAngularFilteredParamsExpression = (paramsExpression, requiredNullableParamKeys = [], preserveRequiredNullables = false) => {
3436
+ const filteredParamValueType = `string | number | boolean${preserveRequiredNullables ? " | null" : ""} | Array<string | number | boolean>`;
3437
+ const preserveNullableBranch = preserveRequiredNullables ? ` } else if (value === null && requiredNullableParamKeys.has(key)) {
3438
+ filteredParams[key] = value;
3439
+ ` : "";
3440
+ return `(() => {
3338
3441
  const requiredNullableParamKeys = new Set<string>(${JSON.stringify(requiredNullableParamKeys)});
3339
- const filteredParams = {} as Record<string, string | number | boolean | null | Array<string | number | boolean>>;
3442
+ const filteredParams: Record<string, ${filteredParamValueType}> = {};
3340
3443
  for (const [key, value] of Object.entries(${paramsExpression})) {
3341
3444
  if (Array.isArray(value)) {
3342
3445
  const filtered = value.filter(
@@ -3345,33 +3448,46 @@ const getAngularFilteredParamsExpression = (paramsExpression, requiredNullablePa
3345
3448
  (typeof item === 'string' ||
3346
3449
  typeof item === 'number' ||
3347
3450
  typeof item === 'boolean'),
3348
- ) as Array<string | number | boolean>;
3451
+ ) as Array<string | number | boolean>;
3349
3452
  if (filtered.length) {
3350
3453
  filteredParams[key] = filtered;
3351
3454
  }
3352
- } else if (value === null && requiredNullableParamKeys.has(key)) {
3353
- filteredParams[key] = value;
3354
- } else if (
3455
+ ${preserveNullableBranch} } else if (
3355
3456
  value != null &&
3356
3457
  (typeof value === 'string' ||
3357
3458
  typeof value === 'number' ||
3358
3459
  typeof value === 'boolean')
3359
3460
  ) {
3360
- filteredParams[key] = value as string | number | boolean;
3461
+ filteredParams[key] = value;
3361
3462
  }
3362
3463
  }
3363
- return filteredParams as unknown as Record<string, string | number | boolean | Array<string | number | boolean>>;
3464
+ return filteredParams;
3364
3465
  })()`;
3466
+ };
3365
3467
  /**
3366
3468
  * Returns the body of a standalone `filterParams` helper function
3367
3469
  * to be emitted once in the generated file header, replacing the
3368
3470
  * inline IIFE that was previously duplicated in every method.
3369
3471
  */
3370
- const getAngularFilteredParamsHelperBody = () => `function filterParams(
3472
+ const getAngularFilteredParamsHelperBody = () => `type AngularHttpParamValue = string | number | boolean | Array<string | number | boolean>;
3473
+ type AngularHttpParamValueWithNullable = AngularHttpParamValue | null;
3474
+
3475
+ function filterParams(
3476
+ params: Record<string, unknown>,
3477
+ requiredNullableKeys?: ReadonlySet<string>,
3478
+ preserveRequiredNullables?: false,
3479
+ ): Record<string, AngularHttpParamValue>;
3480
+ function filterParams(
3371
3481
  params: Record<string, unknown>,
3372
- requiredNullableKeys: Set<string> = new Set(),
3373
- ): Record<string, string | number | boolean | Array<string | number | boolean>> {
3374
- const filteredParams: Record<string, string | number | boolean | null | Array<string | number | boolean>> = {};
3482
+ requiredNullableKeys: ReadonlySet<string> | undefined,
3483
+ preserveRequiredNullables: true,
3484
+ ): Record<string, AngularHttpParamValueWithNullable>;
3485
+ function filterParams(
3486
+ params: Record<string, unknown>,
3487
+ requiredNullableKeys: ReadonlySet<string> = new Set(),
3488
+ preserveRequiredNullables = false,
3489
+ ): Record<string, AngularHttpParamValueWithNullable> {
3490
+ const filteredParams: Record<string, AngularHttpParamValueWithNullable> = {};
3375
3491
  for (const [key, value] of Object.entries(params)) {
3376
3492
  if (Array.isArray(value)) {
3377
3493
  const filtered = value.filter(
@@ -3384,7 +3500,11 @@ const getAngularFilteredParamsHelperBody = () => `function filterParams(
3384
3500
  if (filtered.length) {
3385
3501
  filteredParams[key] = filtered;
3386
3502
  }
3387
- } else if (value === null && requiredNullableKeys.has(key)) {
3503
+ } else if (
3504
+ preserveRequiredNullables &&
3505
+ value === null &&
3506
+ requiredNullableKeys.has(key)
3507
+ ) {
3388
3508
  filteredParams[key] = value;
3389
3509
  } else if (
3390
3510
  value != null &&
@@ -3392,15 +3512,15 @@ const getAngularFilteredParamsHelperBody = () => `function filterParams(
3392
3512
  typeof value === 'number' ||
3393
3513
  typeof value === 'boolean')
3394
3514
  ) {
3395
- filteredParams[key] = value as string | number | boolean;
3515
+ filteredParams[key] = value;
3396
3516
  }
3397
3517
  }
3398
- return filteredParams as Record<string, string | number | boolean | Array<string | number | boolean>>;
3518
+ return filteredParams;
3399
3519
  }`;
3400
3520
  /**
3401
3521
  * Returns a call expression to the `filterParams` helper function.
3402
3522
  */
3403
- const getAngularFilteredParamsCallExpression = (paramsExpression, requiredNullableParamKeys = []) => `filterParams(${paramsExpression}, new Set<string>(${JSON.stringify(requiredNullableParamKeys)}))`;
3523
+ const getAngularFilteredParamsCallExpression = (paramsExpression, requiredNullableParamKeys = [], preserveRequiredNullables = false) => `filterParams(${paramsExpression}, new Set<string>(${JSON.stringify(requiredNullableParamKeys)})${preserveRequiredNullables ? ", true" : ""})`;
3404
3524
  function generateBodyOptions(body, isFormData, isFormUrlEncoded) {
3405
3525
  if (isFormData && body.formData) return "\n formData,";
3406
3526
  if (isFormUrlEncoded && body.formUrlEncoded) return "\n formUrlEncoded,";
@@ -3422,7 +3542,7 @@ function generateAxiosOptions({ response, isExactOptionalPropertyTypes, angularO
3422
3542
  let value = "";
3423
3543
  if (!isRequestOptions) {
3424
3544
  if (queryParams) if (isAngular) {
3425
- const iifeExpr = getAngularFilteredParamsExpression("params ?? {}", requiredNullableQueryParamKeys);
3545
+ const iifeExpr = getAngularFilteredParamsExpression("params ?? {}", requiredNullableQueryParamKeys, !!paramsSerializer);
3426
3546
  value += paramsSerializer ? `\n params: ${paramsSerializer.name}(${iifeExpr}),` : `\n params: ${iifeExpr},`;
3427
3547
  } else value += "\n params,";
3428
3548
  if (headers) value += "\n headers,";
@@ -3439,7 +3559,7 @@ function generateAxiosOptions({ response, isExactOptionalPropertyTypes, angularO
3439
3559
  if (queryParams) if (isVue) value += "\n params: {...unref(params), ...options?.params},";
3440
3560
  else if (isAngular && angularParamsRef) value += `\n params: ${angularParamsRef},`;
3441
3561
  else if (isAngular && paramsSerializer) {
3442
- const callExpr = getAngularFilteredParamsCallExpression("{...params, ...options?.params}", requiredNullableQueryParamKeys);
3562
+ const callExpr = getAngularFilteredParamsCallExpression("{...params, ...options?.params}", requiredNullableQueryParamKeys, true);
3443
3563
  value += `\n params: ${paramsSerializer.name}(${callExpr}),`;
3444
3564
  } else if (isAngular) value += `\n params: ${getAngularFilteredParamsCallExpression("{...params, ...options?.params}", requiredNullableQueryParamKeys)},`;
3445
3565
  else value += "\n params: {...params, ...options?.params},";
@@ -3887,6 +4007,20 @@ function _filteredVerbs(verbs, filters) {
3887
4007
  });
3888
4008
  }
3889
4009
 
4010
+ //#endregion
4011
+ //#region src/writers/file.ts
4012
+ const TRAILING_WHITESPACE_RE = /[^\S\r\n]+$/gm;
4013
+ /**
4014
+ * Write generated code to a file, stripping trailing whitespace from each line.
4015
+ *
4016
+ * Template literals in code generators can produce lines with only whitespace
4017
+ * when conditional expressions evaluate to empty strings. This function
4018
+ * ensures the output is always clean regardless of generator implementation.
4019
+ */
4020
+ async function writeGeneratedFile(filePath, content) {
4021
+ await fs$1.outputFile(filePath, content.replaceAll(TRAILING_WHITESPACE_RE, ""));
4022
+ }
4023
+
3890
4024
  //#endregion
3891
4025
  //#region src/writers/schemas.ts
3892
4026
  /**
@@ -3969,19 +4103,26 @@ function getSchemaGroups(schemaPath, schemas, namingConvention, fileExtension) {
3969
4103
  }
3970
4104
  function getCanonicalMap(schemaGroups, schemaPath, namingConvention, fileExtension) {
3971
4105
  const canonicalPathMap = /* @__PURE__ */ new Map();
4106
+ const canonicalNameMap = /* @__PURE__ */ new Map();
3972
4107
  for (const [key, groupSchemas] of Object.entries(schemaGroups)) {
3973
- const canonicalPath = getPath(schemaPath, conventionName(groupSchemas[0].name, namingConvention), fileExtension);
3974
- canonicalPathMap.set(key, {
3975
- importPath: canonicalPath,
4108
+ const canonicalInfo = {
4109
+ importPath: getPath(schemaPath, conventionName(groupSchemas[0].name, namingConvention), fileExtension),
3976
4110
  name: groupSchemas[0].name
3977
- });
4111
+ };
4112
+ canonicalPathMap.set(key, canonicalInfo);
4113
+ for (const schema of groupSchemas) canonicalNameMap.set(schema.name, canonicalInfo);
3978
4114
  }
3979
- return canonicalPathMap;
4115
+ return {
4116
+ canonicalPathMap,
4117
+ canonicalNameMap
4118
+ };
3980
4119
  }
3981
- function normalizeCanonicalImportPaths(schemas, canonicalPathMap, schemaPath, namingConvention, fileExtension) {
4120
+ function normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension) {
3982
4121
  for (const schema of schemas) schema.imports = schema.imports.map((imp) => {
4122
+ const canonicalByName = canonicalNameMap.get(imp.name);
3983
4123
  const resolvedImportKey = resolveImportKey(schemaPath, imp.importPath ?? `./${conventionName(imp.name, namingConvention)}`, fileExtension);
3984
- const canonical = canonicalPathMap.get(resolvedImportKey);
4124
+ const canonicalByPath = canonicalPathMap.get(resolvedImportKey);
4125
+ const canonical = canonicalByName ?? canonicalByPath;
3985
4126
  if (!canonical?.importPath) return imp;
3986
4127
  const importPath = removeFileExtension(relativeSafe(schemaPath, canonical.importPath.replaceAll("\\", "/")), fileExtension);
3987
4128
  return {
@@ -4009,19 +4150,18 @@ function resolveImportKey(schemaPath, importPath, fileExtension) {
4009
4150
  function removeFileExtension(path, fileExtension) {
4010
4151
  return path.endsWith(fileExtension) ? path.slice(0, path.length - fileExtension.length) : path;
4011
4152
  }
4012
- function getSchema({ schema: { imports, model }, target, header, namingConvention = NamingConvention.CAMEL_CASE }) {
4153
+ function getSchema({ schema: { imports, model }, header, namingConvention = NamingConvention.CAMEL_CASE }) {
4013
4154
  let file = header;
4014
4155
  file += generateImports({
4015
4156
  imports: imports.filter((imp) => !model.includes(`type ${imp.alias ?? imp.name} =`) && !model.includes(`interface ${imp.alias ?? imp.name} {`)),
4016
- target,
4017
4157
  namingConvention
4018
4158
  });
4019
4159
  file += imports.length > 0 ? "\n\n" : "\n";
4020
4160
  file += model;
4021
4161
  return file;
4022
4162
  }
4023
- function getPath(path$1, name, fileExtension) {
4024
- return path.join(path$1, `${name}${fileExtension}`);
4163
+ function getPath(path, name, fileExtension) {
4164
+ return nodePath.join(path, `${name}${fileExtension}`);
4025
4165
  }
4026
4166
  function writeModelInline(acc, model) {
4027
4167
  return acc + `${model}\n`;
@@ -4034,19 +4174,20 @@ function writeModelsInline(array) {
4034
4174
  async function writeSchema({ path, schema, target, namingConvention, fileExtension, header }) {
4035
4175
  const name = conventionName(schema.name, namingConvention);
4036
4176
  try {
4037
- await fs$1.outputFile(getPath(path, name, fileExtension), getSchema({
4177
+ await writeGeneratedFile(getPath(path, name, fileExtension), getSchema({
4038
4178
  schema,
4039
4179
  target,
4040
4180
  header,
4041
4181
  namingConvention
4042
4182
  }));
4043
4183
  } catch (error) {
4044
- throw new Error(`Oups... 🍻. An Error occurred while writing schema ${name} => ${String(error)}`);
4184
+ throw new Error(`Oups... 🍻. An Error occurred while writing schema ${name} => ${String(error)}`, { cause: error });
4045
4185
  }
4046
4186
  }
4047
4187
  async function writeSchemas({ schemaPath, schemas, target, namingConvention, fileExtension, header, indexFiles }) {
4048
4188
  const schemaGroups = getSchemaGroups(schemaPath, schemas, namingConvention, fileExtension);
4049
- normalizeCanonicalImportPaths(schemas, getCanonicalMap(schemaGroups, schemaPath, namingConvention, fileExtension), schemaPath, namingConvention, fileExtension);
4189
+ const { canonicalPathMap, canonicalNameMap } = getCanonicalMap(schemaGroups, schemaPath, namingConvention, fileExtension);
4190
+ normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension);
4050
4191
  for (const groupSchemas of Object.values(schemaGroups)) {
4051
4192
  if (groupSchemas.length === 1) {
4052
4193
  await writeSchema({
@@ -4069,21 +4210,14 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
4069
4210
  });
4070
4211
  }
4071
4212
  if (indexFiles) {
4072
- const schemaFilePath = path.join(schemaPath, `index${fileExtension}`);
4213
+ const schemaFilePath = nodePath.join(schemaPath, `index${fileExtension}`);
4073
4214
  await fs$1.ensureFile(schemaFilePath);
4074
4215
  const ext = fileExtension.endsWith(".ts") ? fileExtension.slice(0, -3) : fileExtension;
4075
4216
  const conventionNamesSet = new Set(Object.values(schemaGroups).map((group) => conventionName(group[0].name, namingConvention)));
4076
4217
  try {
4077
- const currentExports = [...conventionNamesSet].map((schemaName) => `export * from './${schemaName}${ext}';`).toSorted((a, b) => a.localeCompare(b));
4078
- const existingExports = (await fs$1.readFile(schemaFilePath, "utf8")).match(/export\s+\*\s+from\s+['"][^'"]+['"]/g)?.map((statement) => {
4079
- const match = /export\s+\*\s+from\s+['"]([^'"]+)['"]/.exec(statement);
4080
- if (!match) return;
4081
- return `export * from '${match[1]}';`;
4082
- }).filter(Boolean) ?? [];
4083
- const fileContent = `${header}\n${[...new Set([...existingExports, ...currentExports])].toSorted((a, b) => a.localeCompare(b)).join("\n")}`;
4084
- await fs$1.writeFile(schemaFilePath, fileContent, { encoding: "utf8" });
4218
+ await writeGeneratedFile(schemaFilePath, `${header}\n${[...conventionNamesSet].map((schemaName) => `export * from './${schemaName}${ext}';`).toSorted((a, b) => a.localeCompare(b)).join("\n")}\n`);
4085
4219
  } catch (error) {
4086
- throw new Error(`Oups... 🍻. An Error occurred while writing schema index file ${schemaFilePath} => ${String(error)}`);
4220
+ throw new Error(`Oups... 🍻. An Error occurred while writing schema index file ${schemaFilePath} => ${String(error)}`, { cause: error });
4087
4221
  }
4088
4222
  }
4089
4223
  }
@@ -4092,7 +4226,7 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
4092
4226
  //#region src/writers/generate-imports-for-builder.ts
4093
4227
  function generateImportsForBuilder(output, imports, relativeSchemasPath) {
4094
4228
  const isZodSchemaOutput = isObject(output.schemas) && output.schemas.type === "zod";
4095
- let schemaImports = [];
4229
+ let schemaImports;
4096
4230
  if (output.indexFiles) schemaImports = isZodSchemaOutput ? [{
4097
4231
  exports: imports.filter((i) => !i.importPath),
4098
4232
  dependency: joinSafe(relativeSchemasPath, "index.zod")
@@ -4100,15 +4234,21 @@ function generateImportsForBuilder(output, imports, relativeSchemasPath) {
4100
4234
  exports: imports.filter((i) => !i.importPath),
4101
4235
  dependency: relativeSchemasPath
4102
4236
  }];
4103
- else schemaImports = uniqueBy(imports.filter((i) => !i.importPath), (x) => x.name).map((i) => {
4104
- const name = conventionName(isZodSchemaOutput ? i.name : i.schemaName ?? i.name, output.namingConvention);
4105
- const suffix = isZodSchemaOutput ? ".zod" : "";
4106
- const importExtension = output.fileExtension.replace(/\.ts$/, "") || "";
4107
- return {
4108
- exports: [i],
4109
- dependency: joinSafe(relativeSchemasPath, `${name}${suffix}${importExtension}`)
4110
- };
4111
- });
4237
+ else {
4238
+ const importsByDependency = /* @__PURE__ */ new Map();
4239
+ for (const schemaImport of imports.filter((i) => !i.importPath)) {
4240
+ const normalizedName = conventionName(isZodSchemaOutput ? schemaImport.name : schemaImport.schemaName ?? schemaImport.name, output.namingConvention);
4241
+ const suffix = isZodSchemaOutput ? ".zod" : "";
4242
+ const importExtension = output.fileExtension.replace(/\.ts$/, "") || "";
4243
+ const dependency = joinSafe(relativeSchemasPath, `${normalizedName}${suffix}${importExtension}`);
4244
+ if (!importsByDependency.has(dependency)) importsByDependency.set(dependency, []);
4245
+ importsByDependency.get(dependency)?.push(schemaImport);
4246
+ }
4247
+ schemaImports = [...importsByDependency.entries()].map(([dependency, dependencyImports]) => ({
4248
+ dependency,
4249
+ exports: uniqueBy(dependencyImports, (entry) => `${entry.name}|${entry.alias ?? ""}|${String(entry.values)}|${String(entry.default)}`)
4250
+ }));
4251
+ }
4112
4252
  const otherImports = uniqueBy(imports.filter((i) => !!i.importPath), (x) => x.name + x.importPath).map((i) => {
4113
4253
  return {
4114
4254
  exports: [i],
@@ -4239,14 +4379,24 @@ interface TypedResponse<T> extends Response {
4239
4379
  async function writeSingleMode({ builder, output, projectName, header, needSchema }) {
4240
4380
  try {
4241
4381
  const { path } = getFileInfo(output.target, {
4242
- backupFilename: conventionName(builder.info.title, output.namingConvention),
4382
+ backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
4243
4383
  extension: output.fileExtension
4244
4384
  });
4245
4385
  const { imports, importsMock, implementation, implementationMock, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, fetchReviver } = generateTarget(builder, output);
4246
4386
  let data = header;
4247
4387
  const schemasPath = output.schemas ? getRelativeImportPath(path, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : void 0;
4248
4388
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
4249
- const importsForBuilder = schemasPath ? generateImportsForBuilder(output, imports.filter((imp) => !importsMock.some((impMock) => imp.name === impMock.name)), schemasPath) : [];
4389
+ const normalizedImports = imports.filter((imp) => {
4390
+ const searchWords = [imp.alias, imp.name].filter((part) => Boolean(part?.length)).map((part) => escapeRegExp(part)).join("|");
4391
+ if (!searchWords) return false;
4392
+ return new RegExp(String.raw`\b(${searchWords})\b`, "g").test(implementation);
4393
+ }).map((imp) => ({ ...imp }));
4394
+ for (const mockImport of importsMock) {
4395
+ const matchingImport = normalizedImports.find((imp) => imp.name === mockImport.name && (imp.alias ?? "") === (mockImport.alias ?? ""));
4396
+ if (!matchingImport) continue;
4397
+ if (!!mockImport.values || !!mockImport.isConstant || !!mockImport.default || !!mockImport.namespaceImport || !!mockImport.syntheticDefaultImport) matchingImport.values = true;
4398
+ }
4399
+ const importsForBuilder = schemasPath ? generateImportsForBuilder(output, normalizedImports, schemasPath) : [];
4250
4400
  data += builder.imports({
4251
4401
  client: output.client,
4252
4402
  implementation,
@@ -4261,7 +4411,7 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4261
4411
  output
4262
4412
  });
4263
4413
  if (output.mock) {
4264
- const importsMockForBuilder = schemasPath ? generateImportsForBuilder(output, importsMock, schemasPath) : [];
4414
+ const importsMockForBuilder = schemasPath ? generateImportsForBuilder(output, importsMock.filter((impMock) => !normalizedImports.some((imp) => imp.name === impMock.name && (imp.alias ?? "") === (impMock.alias ?? ""))), schemasPath) : [];
4265
4415
  data += builder.importsMock({
4266
4416
  implementation: implementationMock,
4267
4417
  imports: importsMockForBuilder,
@@ -4294,11 +4444,11 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4294
4444
  data += "\n\n";
4295
4445
  data += implementationMock;
4296
4446
  }
4297
- await fs$1.outputFile(path, data);
4447
+ await writeGeneratedFile(path, data);
4298
4448
  return [path];
4299
4449
  } catch (error) {
4300
4450
  const errorMsg = error instanceof Error ? error.message : "unknown error";
4301
- throw new Error(`Oups... 🍻. An Error occurred while writing file => ${errorMsg}`);
4451
+ throw new Error(`Oups... 🍻. An Error occurred while writing file => ${errorMsg}`, { cause: error });
4302
4452
  }
4303
4453
  }
4304
4454
 
@@ -4307,7 +4457,7 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4307
4457
  async function writeSplitMode({ builder, output, projectName, header, needSchema }) {
4308
4458
  try {
4309
4459
  const { path: targetPath, filename, dirname, extension } = getFileInfo(output.target, {
4310
- backupFilename: conventionName(builder.info.title, output.namingConvention),
4460
+ backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
4311
4461
  extension: output.fileExtension
4312
4462
  });
4313
4463
  const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, fetchReviver } = generateTarget(builder, output);
@@ -4338,11 +4488,8 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4338
4488
  isAllowSyntheticDefaultImports,
4339
4489
  options: isFunction(output.mock) ? void 0 : output.mock
4340
4490
  });
4341
- const schemasPath = output.schemas ? void 0 : path.join(dirname, filename + ".schemas" + extension);
4342
- if (schemasPath && needSchema) {
4343
- const schemasData = header + generateModelsInline(builder.schemas);
4344
- await fs$1.outputFile(schemasPath, schemasData);
4345
- }
4491
+ const schemasPath = output.schemas ? void 0 : nodePath.join(dirname, filename + ".schemas" + extension);
4492
+ if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, header + generateModelsInline(builder.schemas));
4346
4493
  if (mutators) implementationData += generateMutatorImports({
4347
4494
  mutators,
4348
4495
  implementation
@@ -4363,17 +4510,17 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4363
4510
  implementationData += `\n${implementation}`;
4364
4511
  mockData += `\n${implementationMock}`;
4365
4512
  const implementationFilename = filename + (OutputClient.ANGULAR === output.client ? ".service" : "") + extension;
4366
- const implementationPath = path.join(dirname, implementationFilename);
4367
- await fs$1.outputFile(implementationPath, implementationData);
4368
- const mockPath = output.mock ? path.join(dirname, filename + "." + getMockFileExtensionByTypeName(output.mock) + extension) : void 0;
4369
- if (mockPath) await fs$1.outputFile(mockPath, mockData);
4513
+ const implementationPath = nodePath.join(dirname, implementationFilename);
4514
+ await writeGeneratedFile(implementationPath, implementationData);
4515
+ const mockPath = output.mock ? nodePath.join(dirname, filename + "." + getMockFileExtensionByTypeName(output.mock) + extension) : void 0;
4516
+ if (mockPath) await writeGeneratedFile(mockPath, mockData);
4370
4517
  return [
4371
4518
  implementationPath,
4372
4519
  ...schemasPath ? [schemasPath] : [],
4373
4520
  ...mockPath ? [mockPath] : []
4374
4521
  ];
4375
4522
  } catch (error) {
4376
- throw new Error(`Oups... 🍻. An Error occurred while splitting => ${String(error)}`);
4523
+ throw new Error(`Oups... 🍻. An Error occurred while splitting => ${String(error)}`, { cause: error });
4377
4524
  }
4378
4525
  }
4379
4526
 
@@ -4496,13 +4643,13 @@ function generateTargetForTags(builder, options) {
4496
4643
  //#region src/writers/split-tags-mode.ts
4497
4644
  async function writeSplitTagsMode({ builder, output, projectName, header, needSchema }) {
4498
4645
  const { filename, dirname, extension } = getFileInfo(output.target, {
4499
- backupFilename: conventionName(builder.info.title, output.namingConvention),
4646
+ backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
4500
4647
  extension: output.fileExtension
4501
4648
  });
4502
4649
  const target = generateTargetForTags(builder, output);
4503
4650
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
4504
4651
  const mockOption = output.mock && !isFunction(output.mock) ? output.mock : void 0;
4505
- const indexFilePath = mockOption?.indexMockFiles ? path.join(dirname, "index." + getMockFileExtensionByTypeName(mockOption) + extension) : void 0;
4652
+ const indexFilePath = mockOption?.indexMockFiles ? nodePath.join(dirname, "index." + getMockFileExtensionByTypeName(mockOption) + extension) : void 0;
4506
4653
  if (indexFilePath) await fs$1.outputFile(indexFilePath, "");
4507
4654
  const tagEntries = Object.entries(target);
4508
4655
  const generatedFilePathsArray = await Promise.all(tagEntries.map(async ([tag, target]) => {
@@ -4510,7 +4657,7 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4510
4657
  const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, fetchReviver, formUrlEncoded, paramsSerializer } = target;
4511
4658
  let implementationData = header;
4512
4659
  let mockData = header;
4513
- const importerPath = path.join(dirname, tag, tag + extension);
4660
+ const importerPath = nodePath.join(dirname, tag, tag + extension);
4514
4661
  const relativeSchemasPath = output.schemas ? getRelativeImportPath(importerPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "../" + filename + ".schemas";
4515
4662
  const importsForBuilder = generateImportsForBuilder(output, imports, relativeSchemasPath);
4516
4663
  implementationData += builder.imports({
@@ -4535,11 +4682,8 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4535
4682
  isAllowSyntheticDefaultImports,
4536
4683
  options: isFunction(output.mock) ? void 0 : output.mock
4537
4684
  });
4538
- const schemasPath = output.schemas ? void 0 : path.join(dirname, filename + ".schemas" + extension);
4539
- if (schemasPath && needSchema) {
4540
- const schemasData = header + generateModelsInline(builder.schemas);
4541
- await fs$1.outputFile(schemasPath, schemasData);
4542
- }
4685
+ const schemasPath = output.schemas ? void 0 : nodePath.join(dirname, filename + ".schemas" + extension);
4686
+ if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, header + generateModelsInline(builder.schemas));
4543
4687
  if (mutators) implementationData += generateMutatorImports({
4544
4688
  mutators,
4545
4689
  implementation,
@@ -4576,17 +4720,17 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4576
4720
  implementationData += `\n${implementation}`;
4577
4721
  mockData += `\n${implementationMock}`;
4578
4722
  const implementationFilename = tag + (OutputClient.ANGULAR === output.client ? ".service" : "") + extension;
4579
- const implementationPath = path.join(dirname, tag, implementationFilename);
4580
- await fs$1.outputFile(implementationPath, implementationData);
4581
- const mockPath = output.mock ? path.join(dirname, tag, tag + "." + getMockFileExtensionByTypeName(output.mock) + extension) : void 0;
4582
- if (mockPath) await fs$1.outputFile(mockPath, mockData);
4723
+ const implementationPath = nodePath.join(dirname, tag, implementationFilename);
4724
+ await writeGeneratedFile(implementationPath, implementationData);
4725
+ const mockPath = output.mock ? nodePath.join(dirname, tag, tag + "." + getMockFileExtensionByTypeName(output.mock) + extension) : void 0;
4726
+ if (mockPath) await writeGeneratedFile(mockPath, mockData);
4583
4727
  return [
4584
4728
  implementationPath,
4585
4729
  ...schemasPath ? [schemasPath] : [],
4586
4730
  ...mockPath ? [mockPath] : []
4587
4731
  ];
4588
4732
  } catch (error) {
4589
- throw new Error(`Oups... 🍻. An Error occurred while splitting tag ${tag} => ${String(error)}`);
4733
+ throw new Error(`Oups... 🍻. An Error occurred while splitting tag ${tag} => ${String(error)}`, { cause: error });
4590
4734
  }
4591
4735
  }));
4592
4736
  if (indexFilePath && mockOption) {
@@ -4596,14 +4740,14 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4596
4740
  }).join("");
4597
4741
  await fs$1.appendFile(indexFilePath, indexContent);
4598
4742
  }
4599
- return [...indexFilePath ? [indexFilePath] : [], ...generatedFilePathsArray.flat()];
4743
+ return [...new Set([...indexFilePath ? [indexFilePath] : [], ...generatedFilePathsArray.flat()])];
4600
4744
  }
4601
4745
 
4602
4746
  //#endregion
4603
4747
  //#region src/writers/tags-mode.ts
4604
4748
  async function writeTagsMode({ builder, output, projectName, header, needSchema }) {
4605
4749
  const { path: targetPath, filename, dirname, extension } = getFileInfo(output.target, {
4606
- backupFilename: conventionName(builder.info.title, output.namingConvention),
4750
+ backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
4607
4751
  extension: output.fileExtension
4608
4752
  });
4609
4753
  const target = generateTargetForTags(builder, output);
@@ -4613,7 +4757,17 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4613
4757
  const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, formUrlEncoded, fetchReviver, paramsSerializer } = target;
4614
4758
  let data = header;
4615
4759
  const schemasPathRelative = output.schemas ? getRelativeImportPath(targetPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas";
4616
- const importsForBuilder = generateImportsForBuilder(output, imports.filter((imp) => !importsMock.some((impMock) => imp.name === impMock.name)), schemasPathRelative);
4760
+ const normalizedImports = imports.filter((imp) => {
4761
+ const searchWords = [imp.alias, imp.name].filter((part) => Boolean(part?.length)).map((part) => escapeRegExp(part)).join("|");
4762
+ if (!searchWords) return false;
4763
+ return new RegExp(String.raw`\b(${searchWords})\b`, "g").test(implementation);
4764
+ }).map((imp) => ({ ...imp }));
4765
+ for (const mockImport of importsMock) {
4766
+ const matchingImport = normalizedImports.find((imp) => imp.name === mockImport.name && (imp.alias ?? "") === (mockImport.alias ?? ""));
4767
+ if (!matchingImport) continue;
4768
+ if (!!mockImport.values || !!mockImport.isConstant || !!mockImport.default || !!mockImport.namespaceImport || !!mockImport.syntheticDefaultImport) matchingImport.values = true;
4769
+ }
4770
+ const importsForBuilder = generateImportsForBuilder(output, normalizedImports, schemasPathRelative);
4617
4771
  data += builder.imports({
4618
4772
  client: output.client,
4619
4773
  implementation,
@@ -4628,7 +4782,7 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4628
4782
  output
4629
4783
  });
4630
4784
  if (output.mock) {
4631
- const importsMockForBuilder = generateImportsForBuilder(output, importsMock, schemasPathRelative);
4785
+ const importsMockForBuilder = generateImportsForBuilder(output, importsMock.filter((impMock) => !normalizedImports.some((imp) => imp.name === impMock.name && (imp.alias ?? "") === (impMock.alias ?? ""))), schemasPathRelative);
4632
4786
  data += builder.importsMock({
4633
4787
  implementation: implementationMock,
4634
4788
  imports: importsMockForBuilder,
@@ -4638,11 +4792,8 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4638
4792
  options: isFunction(output.mock) ? void 0 : output.mock
4639
4793
  });
4640
4794
  }
4641
- const schemasPath = output.schemas ? void 0 : path.join(dirname, filename + ".schemas" + extension);
4642
- if (schemasPath && needSchema) {
4643
- const schemasData = header + generateModelsInline(builder.schemas);
4644
- await fs$1.outputFile(schemasPath, schemasData);
4645
- }
4795
+ const schemasPath = output.schemas ? void 0 : nodePath.join(dirname, filename + ".schemas" + extension);
4796
+ if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, header + generateModelsInline(builder.schemas));
4646
4797
  if (mutators) data += generateMutatorImports({
4647
4798
  mutators,
4648
4799
  implementation
@@ -4666,15 +4817,15 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4666
4817
  data += "\n\n";
4667
4818
  data += implementationMock;
4668
4819
  }
4669
- const implementationPath = path.join(dirname, `${kebab(tag)}${extension}`);
4670
- await fs$1.outputFile(implementationPath, data);
4820
+ const implementationPath = nodePath.join(dirname, `${kebab(tag)}${extension}`);
4821
+ await writeGeneratedFile(implementationPath, data);
4671
4822
  return [implementationPath, ...schemasPath ? [schemasPath] : []];
4672
4823
  } catch (error) {
4673
- throw new Error(`Oups... 🍻. An Error occurred while writing tag ${tag} => ${String(error)}`);
4824
+ throw new Error(`Oups... 🍻. An Error occurred while writing tag ${tag} => ${String(error)}`, { cause: error });
4674
4825
  }
4675
4826
  }))).flat();
4676
4827
  }
4677
4828
 
4678
4829
  //#endregion
4679
- export { BODY_TYPE_NAME, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, _filteredVerbs, addDependency, asyncReduce, camel, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, filterByContentType, fixCrossDirectoryImports, fixRegularSchemaImports, generalJSTypes, generalJSTypesWithArray, generateAxiosOptions, generateBodyMutatorConfig, generateBodyOptions, generateComponentDefinition, generateDependencyImports, generateFormDataAndUrlEncodedFunction, generateImports, generateModelInline, generateModelsInline, generateMutator, generateMutatorConfig, generateMutatorImports, generateMutatorRequestOptions, generateOptions, generateParameterDefinition, generateQueryParamsAxiosConfig, generateSchemasDefinition, generateTarget, generateTargetForTags, generateVerbImports, generateVerbOptions, generateVerbsOptions, getAngularFilteredParamsCallExpression, getAngularFilteredParamsExpression, getAngularFilteredParamsHelperBody, getArray, getBody, getCombinedEnumValue, getDefaultContentType, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, getEnumUnionFromSchema, getExtension, getFileInfo, getFormDataFieldFileType, getFullRoute, getIsBodyVerb, getKey, getMockFileExtensionByTypeName, getNumberWord, getObject, getOperationId, getOrvalGeneratedTypes, getParameters, getParams, getParamsInPath, getPropertySafe, getProps, getQueryParams, getRefInfo, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getSuccessResponseType, getTypedResponse, isBinaryContentType, isBoolean, isDirectory, isFunction, isModule, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, isVerbose, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, logVerbose, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resolveDiscriminators, resolveExampleRefs, resolveInstalledVersion, resolveInstalledVersions, resolveObject, resolveRef, resolveValue, sanitize, setVerbose, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
4830
+ export { BODY_TYPE_NAME, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, _filteredVerbs, addDependency, asyncReduce, camel, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, escapeRegExp, filterByContentType, fixCrossDirectoryImports, fixRegularSchemaImports, generalJSTypes, generalJSTypesWithArray, generateAxiosOptions, generateBodyMutatorConfig, generateBodyOptions, generateComponentDefinition, generateDependencyImports, generateFormDataAndUrlEncodedFunction, generateImports, generateModelInline, generateModelsInline, generateMutator, generateMutatorConfig, generateMutatorImports, generateMutatorRequestOptions, generateOptions, generateParameterDefinition, generateQueryParamsAxiosConfig, generateSchemasDefinition, generateTarget, generateTargetForTags, generateVerbImports, generateVerbOptions, generateVerbsOptions, getAngularFilteredParamsCallExpression, getAngularFilteredParamsExpression, getAngularFilteredParamsHelperBody, getArray, getBody, getCombinedEnumValue, getDefaultContentType, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, getEnumUnionFromSchema, getExtension, getFileInfo, getFormDataFieldFileType, getFullRoute, getIsBodyVerb, getKey, getMockFileExtensionByTypeName, getNumberWord, getObject, getOperationId, getOrvalGeneratedTypes, getParameters, getParams, getParamsInPath, getPropertySafe, getProps, getQueryParams, getRefInfo, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getSuccessResponseType, getTypedResponse, isBinaryContentType, isBoolean, isDirectory, isFunction, isModule, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, isVerbose, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, logVerbose, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resolveDiscriminators, resolveExampleRefs, resolveInstalledVersion, resolveInstalledVersions, resolveObject, resolveRef, resolveValue, sanitize, setVerbose, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
4680
4831
  //# sourceMappingURL=index.mjs.map