orval 8.12.2 → 8.13.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/bin/orval.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { c as description, i as startWatcher, l as name, n as loadConfigFile, r as generateSpec, s as normalizeOptions, t as findConfigFile, u as version } from "../config-
|
|
2
|
+
import { c as description, i as startWatcher, l as name, n as loadConfigFile, r as generateSpec, s as normalizeOptions, t as findConfigFile, u as version } from "../config-Cyybmy9c.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { Option, program } from "@commander-js/extra-typings";
|
|
5
5
|
import { ErrorWithTag, OutputClient, OutputMode, SupportedFormatter, getWarningCount, isString, log, logError, resetWarnings, setVerbose, startMessage } from "@orval/core";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SupportedFormatter, asyncReduce, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getMockFileExtensionByTypeName, getRoute, isBoolean, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, splitSchemasByType, upath, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
|
|
2
|
+
import { FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SupportedFormatter, asyncReduce, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getImportExtension, getMockFileExtensionByTypeName, getRoute, isBoolean, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, splitSchemasByType, upath, writeGeneratedFile, writeSchemas, 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";
|
|
6
6
|
import { isNullish as isNullish$1, pick, unique } from "remeda";
|
|
7
7
|
import * as mock from "@orval/mock";
|
|
8
|
-
import { generateMockImports, getDefaultMockOptionsForType } from "@orval/mock";
|
|
8
|
+
import { generateFakerForSchemas, generateMockImports, getDefaultMockOptionsForType } from "@orval/mock";
|
|
9
9
|
import angular from "@orval/angular";
|
|
10
10
|
import axios from "@orval/axios";
|
|
11
11
|
import fetchClient from "@orval/fetch";
|
|
@@ -28,7 +28,7 @@ import { createJiti } from "jiti";
|
|
|
28
28
|
//#region package.json
|
|
29
29
|
var name = "orval";
|
|
30
30
|
var description = "A swagger client generator for typescript";
|
|
31
|
-
var version = "8.
|
|
31
|
+
var version = "8.13.0";
|
|
32
32
|
//#endregion
|
|
33
33
|
//#region src/client.ts
|
|
34
34
|
const DEFAULT_CLIENT = OutputClient.AXIOS;
|
|
@@ -162,7 +162,10 @@ const generateOperations = (outputClient = DEFAULT_CLIENT, verbsOptions, options
|
|
|
162
162
|
const { client: generatorClient } = getGeneratorClient(outputClient, output);
|
|
163
163
|
const client = await generatorClient(verbOption, options, outputClient, output);
|
|
164
164
|
if (!client.implementation) return acc;
|
|
165
|
-
const mockOutputs = output.mock.generators.
|
|
165
|
+
const mockOutputs = output.mock.generators.filter((entry) => {
|
|
166
|
+
if (!isFunction(entry) && entry.type === OutputMockType.FAKER && entry.operationResponses === false) return false;
|
|
167
|
+
return true;
|
|
168
|
+
}).map((entry) => {
|
|
166
169
|
const generated = invokeMockGenerator(verbOption, options, entry);
|
|
167
170
|
return {
|
|
168
171
|
type: isFunction(entry) ? OutputMockType.MSW : entry.type,
|
|
@@ -891,6 +894,15 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
|
|
|
891
894
|
seenMockTypes.add(entry.type);
|
|
892
895
|
}
|
|
893
896
|
const defaultFileExtension = ".ts";
|
|
897
|
+
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;
|
|
898
|
+
const factoryMethodsConfig = outputOptions.factoryMethods;
|
|
899
|
+
let factoryMethods = void 0;
|
|
900
|
+
if (factoryMethodsConfig) factoryMethods = {
|
|
901
|
+
functionNamePrefix: factoryMethodsConfig.functionNamePrefix ?? "create",
|
|
902
|
+
mode: factoryMethodsConfig.mode ?? "split",
|
|
903
|
+
outputDirectory: factoryMethodsConfig.outputDirectory ? normalizePath(factoryMethodsConfig.outputDirectory, outputWorkspace) : outputOptions.schemas ? normalizePath(isString(outputOptions.schemas) ? outputOptions.schemas : outputOptions.schemas.path, outputWorkspace) : normalizePath(outputWorkspace, outputWorkspace),
|
|
904
|
+
includeOptionalProperty: factoryMethodsConfig.includeOptionalProperty ?? true
|
|
905
|
+
};
|
|
894
906
|
const globalQueryOptions = {
|
|
895
907
|
signal: true,
|
|
896
908
|
shouldExportMutatorHooks: true,
|
|
@@ -913,6 +925,7 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
|
|
|
913
925
|
operationSchemas: outputOptions.operationSchemas ? normalizePath(outputOptions.operationSchemas, outputWorkspace) : void 0,
|
|
914
926
|
namingConvention: outputOptions.namingConvention ?? NamingConvention.CAMEL_CASE,
|
|
915
927
|
fileExtension: outputOptions.fileExtension ?? defaultFileExtension,
|
|
928
|
+
schemaFileExtension: outputOptions.schemaFileExtension ?? outputOptions.fileExtension ?? defaultSchemaFileExtension,
|
|
916
929
|
workspace: outputOptions.workspace ? outputWorkspace : void 0,
|
|
917
930
|
client: outputOptions.client ?? client ?? OutputClient.AXIOS_FUNCTIONS,
|
|
918
931
|
httpClient: outputOptions.httpClient ?? httpClient ?? ((outputOptions.client ?? client) === OutputClient.ANGULAR_QUERY ? OutputHttpClient.ANGULAR : OutputHttpClient.FETCH),
|
|
@@ -927,6 +940,7 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
|
|
|
927
940
|
indexFiles: outputOptions.indexFiles ?? true,
|
|
928
941
|
baseUrl: outputOptions.baseUrl,
|
|
929
942
|
unionAddMissingProperties: outputOptions.unionAddMissingProperties ?? false,
|
|
943
|
+
factoryMethods,
|
|
930
944
|
override: {
|
|
931
945
|
...outputOptions.override,
|
|
932
946
|
mock: {
|
|
@@ -1001,6 +1015,7 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
|
|
|
1001
1015
|
},
|
|
1002
1016
|
generateEachHttpStatus: outputOptions.override?.zod?.generateEachHttpStatus ?? false,
|
|
1003
1017
|
useBrandedTypes: outputOptions.override?.zod?.useBrandedTypes ?? false,
|
|
1018
|
+
generateReusableSchemas: outputOptions.override?.zod?.generateReusableSchemas ?? false,
|
|
1004
1019
|
dateTimeOptions: outputOptions.override?.zod?.dateTimeOptions ?? { offset: true },
|
|
1005
1020
|
timeOptions: outputOptions.override?.zod?.timeOptions ?? {}
|
|
1006
1021
|
},
|
|
@@ -1170,6 +1185,7 @@ function normalizeOperationsAndTags(operationsOrTags, workspace, global) {
|
|
|
1170
1185
|
},
|
|
1171
1186
|
generateEachHttpStatus: zod.generateEachHttpStatus ?? false,
|
|
1172
1187
|
useBrandedTypes: zod.useBrandedTypes ?? false,
|
|
1188
|
+
generateReusableSchemas: zod.generateReusableSchemas ?? false,
|
|
1173
1189
|
dateTimeOptions: zod.dateTimeOptions ?? { offset: true },
|
|
1174
1190
|
timeOptions: zod.timeOptions ?? {}
|
|
1175
1191
|
} } : {},
|
|
@@ -1308,6 +1324,158 @@ async function startWatcher(watchOptions, watchFn, defaultTarget = ".") {
|
|
|
1308
1324
|
});
|
|
1309
1325
|
}
|
|
1310
1326
|
//#endregion
|
|
1327
|
+
//#region src/reusable-schemas.ts
|
|
1328
|
+
const lastRefSegment = (ref) => {
|
|
1329
|
+
const raw = ref.split("/").pop() ?? "";
|
|
1330
|
+
return decodeURIComponent(raw).replaceAll("~1", "/").replaceAll("~0", "~");
|
|
1331
|
+
};
|
|
1332
|
+
/**
|
|
1333
|
+
* Convert a single `#/components/schemas/X` ref into the export name we will
|
|
1334
|
+
* emit for it: the last `$ref` segment with `namingConvention` applied.
|
|
1335
|
+
*/
|
|
1336
|
+
const resolveSchemaName = (ref, namingConvention) => conventionName(lastRefSegment(ref), namingConvention);
|
|
1337
|
+
const JS_IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1338
|
+
/**
|
|
1339
|
+
* Resolve names for a set of refs, throwing on conflicts or on names that
|
|
1340
|
+
* aren't valid JS identifiers (e.g. `kebab-case` produces dashes). The mapping
|
|
1341
|
+
* is the single source of truth for cross-schema references — the generator,
|
|
1342
|
+
* the orchestrator's graph, and the sentinel rewriter all consult it.
|
|
1343
|
+
*/
|
|
1344
|
+
const resolveSchemaNames = (refs, namingConvention) => {
|
|
1345
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
1346
|
+
const reverse = /* @__PURE__ */ new Map();
|
|
1347
|
+
for (const ref of refs) {
|
|
1348
|
+
const name = resolveSchemaName(ref, namingConvention);
|
|
1349
|
+
if (!JS_IDENTIFIER_PATTERN.test(name)) throw new Error(`[orval/zod] generateReusableSchemas: ref ${ref} converts to "${name}" under namingConvention=${namingConvention}, which is not a valid JS identifier. Use camelCase, PascalCase, or snake_case for the project's namingConvention when this flag is enabled.`);
|
|
1350
|
+
const previous = reverse.get(name);
|
|
1351
|
+
if (previous !== void 0 && previous !== ref) throw new Error(`[orval/zod] generateReusableSchemas: refs ${previous} and ${ref} both convert to "${name}" under namingConvention=${namingConvention}. Rename one in the OpenAPI source or change the convention.`);
|
|
1352
|
+
resolved.set(ref, name);
|
|
1353
|
+
reverse.set(name, ref);
|
|
1354
|
+
}
|
|
1355
|
+
return resolved;
|
|
1356
|
+
};
|
|
1357
|
+
/**
|
|
1358
|
+
* For each component-schema ref, run the Zod generator + parser with
|
|
1359
|
+
* `useReusableSchemas: true`. The resulting `zod` strings contain
|
|
1360
|
+
* `__REF_<name>__` sentinels at every site that references another schema;
|
|
1361
|
+
* the SCC step (Task 10) decides whether each sentinel becomes a direct
|
|
1362
|
+
* identifier or a `z.lazy(() => Name)` wrapper.
|
|
1363
|
+
*/
|
|
1364
|
+
const generateReusableSchemaSet = (refs, context, options) => {
|
|
1365
|
+
const componentSchemas = context.spec.components?.schemas ?? {};
|
|
1366
|
+
const nameToRef = /* @__PURE__ */ new Map();
|
|
1367
|
+
for (const schemaName of Object.keys(componentSchemas)) {
|
|
1368
|
+
const ref = `#/components/schemas/${schemaName}`;
|
|
1369
|
+
nameToRef.set(resolveSchemaName(ref, context.output.namingConvention), ref);
|
|
1370
|
+
}
|
|
1371
|
+
const queue = [...refs];
|
|
1372
|
+
const seen = new Set(refs);
|
|
1373
|
+
const entries = [];
|
|
1374
|
+
for (const ref of queue) {
|
|
1375
|
+
const schema = componentSchemas[ref.slice(21)];
|
|
1376
|
+
if (!schema) continue;
|
|
1377
|
+
const name = resolveSchemaName(ref, context.output.namingConvention);
|
|
1378
|
+
const parsed = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(schema, context, name, options.strict, options.isZodV4, {
|
|
1379
|
+
required: true,
|
|
1380
|
+
useReusableSchemas: true
|
|
1381
|
+
}), context, options.coerce ?? false, options.strict, options.isZodV4);
|
|
1382
|
+
entries.push({
|
|
1383
|
+
ref,
|
|
1384
|
+
name,
|
|
1385
|
+
zod: parsed.zod,
|
|
1386
|
+
consts: parsed.consts,
|
|
1387
|
+
usedRefs: parsed.usedRefs
|
|
1388
|
+
});
|
|
1389
|
+
for (const usedName of parsed.usedRefs) {
|
|
1390
|
+
const usedRef = nameToRef.get(usedName);
|
|
1391
|
+
if (usedRef !== void 0 && !seen.has(usedRef)) {
|
|
1392
|
+
seen.add(usedRef);
|
|
1393
|
+
queue.push(usedRef);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
return entries;
|
|
1398
|
+
};
|
|
1399
|
+
const edgeKey = (from, to) => `${from}->${to}`;
|
|
1400
|
+
const tarjan = (graph) => {
|
|
1401
|
+
let index = 0;
|
|
1402
|
+
const stack = [];
|
|
1403
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
1404
|
+
const indices = /* @__PURE__ */ new Map();
|
|
1405
|
+
const lowlinks = /* @__PURE__ */ new Map();
|
|
1406
|
+
const sccs = [];
|
|
1407
|
+
const lazyEdges = /* @__PURE__ */ new Set();
|
|
1408
|
+
const strongconnect = (v) => {
|
|
1409
|
+
indices.set(v, index);
|
|
1410
|
+
lowlinks.set(v, index);
|
|
1411
|
+
index += 1;
|
|
1412
|
+
stack.push(v);
|
|
1413
|
+
onStack.add(v);
|
|
1414
|
+
const neighbors = graph.get(v) ?? /* @__PURE__ */ new Set();
|
|
1415
|
+
for (const w of neighbors) {
|
|
1416
|
+
if (v === w) {
|
|
1417
|
+
lazyEdges.add(edgeKey(v, w));
|
|
1418
|
+
continue;
|
|
1419
|
+
}
|
|
1420
|
+
if (!indices.has(w)) {
|
|
1421
|
+
strongconnect(w);
|
|
1422
|
+
lowlinks.set(v, Math.min(lowlinks.get(v) ?? -1, lowlinks.get(w) ?? -1));
|
|
1423
|
+
} else if (onStack.has(w)) {
|
|
1424
|
+
lazyEdges.add(edgeKey(v, w));
|
|
1425
|
+
lowlinks.set(v, Math.min(lowlinks.get(v) ?? -1, indices.get(w) ?? -1));
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
if (lowlinks.get(v) === indices.get(v)) {
|
|
1429
|
+
const scc = [];
|
|
1430
|
+
let w;
|
|
1431
|
+
do {
|
|
1432
|
+
w = stack.pop();
|
|
1433
|
+
if (w === void 0) break;
|
|
1434
|
+
onStack.delete(w);
|
|
1435
|
+
scc.push(w);
|
|
1436
|
+
} while (w !== v);
|
|
1437
|
+
sccs.push(scc);
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
for (const node of graph.keys()) if (!indices.has(node)) strongconnect(node);
|
|
1441
|
+
return {
|
|
1442
|
+
sccs,
|
|
1443
|
+
lazyEdges
|
|
1444
|
+
};
|
|
1445
|
+
};
|
|
1446
|
+
const SENTINEL_PATTERN = /__REF_([A-Za-z_$][A-Za-z0-9_$]*)__/g;
|
|
1447
|
+
/**
|
|
1448
|
+
* Replace every `__REF_<name>__` sentinel with either the bare identifier or
|
|
1449
|
+
* `zod.lazy(() => <name>)` based on whether the edge closes a cycle, then
|
|
1450
|
+
* reorder entries so that every non-lazy reference is emitted AFTER its
|
|
1451
|
+
* target. This avoids TDZ errors at module load.
|
|
1452
|
+
*
|
|
1453
|
+
* Both the lazy classification and the emit order come from a single Tarjan
|
|
1454
|
+
* run, guaranteeing they agree: a non-lazy edge u→v means v is visited (and
|
|
1455
|
+
* popped) before u in DFS, so v appears earlier in the SCC array → emitted
|
|
1456
|
+
* before u → safe.
|
|
1457
|
+
*/
|
|
1458
|
+
const rewriteReusableSchemas = (entries) => {
|
|
1459
|
+
const graph = new Map(entries.map((e) => [e.name, new Set(e.usedRefs)]));
|
|
1460
|
+
for (const e of entries) for (const ref of e.usedRefs) if (!graph.has(ref)) graph.set(ref, /* @__PURE__ */ new Set());
|
|
1461
|
+
const { sccs, lazyEdges } = tarjan(graph);
|
|
1462
|
+
const rewritten = new Map(entries.map((entry) => {
|
|
1463
|
+
const newZod = entry.zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => {
|
|
1464
|
+
return lazyEdges.has(edgeKey(entry.name, refName)) ? `zod.lazy(() => ${refName})` : refName;
|
|
1465
|
+
});
|
|
1466
|
+
return [entry.name, {
|
|
1467
|
+
...entry,
|
|
1468
|
+
zod: newZod
|
|
1469
|
+
}];
|
|
1470
|
+
}));
|
|
1471
|
+
const out = [];
|
|
1472
|
+
for (const scc of sccs) for (const name of scc) {
|
|
1473
|
+
const entry = rewritten.get(name);
|
|
1474
|
+
if (entry !== void 0) out.push(entry);
|
|
1475
|
+
}
|
|
1476
|
+
return out;
|
|
1477
|
+
};
|
|
1478
|
+
//#endregion
|
|
1311
1479
|
//#region src/write-zod-specs.ts
|
|
1312
1480
|
function generateZodSchemaFileContent(header, schemas) {
|
|
1313
1481
|
return `${header}import { z as zod } from 'zod';
|
|
@@ -1362,6 +1530,7 @@ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNam
|
|
|
1362
1530
|
await fs.outputFile(indexPath, `${header}\n${uniqueExports}\n`);
|
|
1363
1531
|
}
|
|
1364
1532
|
function generateZodSchemasInline(builder, output) {
|
|
1533
|
+
if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output);
|
|
1365
1534
|
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
1366
1535
|
if (schemasWithOpenApiDef.length === 0) return "";
|
|
1367
1536
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
@@ -1386,7 +1555,33 @@ function generateZodSchemasInline(builder, output) {
|
|
|
1386
1555
|
if (schemas.length === 0) return "";
|
|
1387
1556
|
return generateZodSchemaFileContent("", schemas);
|
|
1388
1557
|
}
|
|
1558
|
+
function generateZodSchemasInlineReusable(builder, output) {
|
|
1559
|
+
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
1560
|
+
if (schemasWithOpenApiDef.length === 0) return "";
|
|
1561
|
+
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
1562
|
+
const strict = output.override.zod.strict.body;
|
|
1563
|
+
const coerce = output.override.zod.coerce.body;
|
|
1564
|
+
const context = {
|
|
1565
|
+
spec: builder.spec,
|
|
1566
|
+
target: builder.target,
|
|
1567
|
+
workspace: "",
|
|
1568
|
+
output
|
|
1569
|
+
};
|
|
1570
|
+
const refs = schemasWithOpenApiDef.map(({ name }) => `#/components/schemas/${name}`);
|
|
1571
|
+
resolveSchemaNames(refs, output.namingConvention);
|
|
1572
|
+
return `import { z as zod } from 'zod';\n\n${rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
|
|
1573
|
+
strict,
|
|
1574
|
+
isZodV4,
|
|
1575
|
+
coerce
|
|
1576
|
+
})).map((entry) => {
|
|
1577
|
+
return `${entry.consts ? `${entry.consts}\n\n` : ""}export const ${entry.name} = ${entry.zod};\n\nexport type ${entry.name} = zod.input<typeof ${entry.name}>;\nexport type ${entry.name}Output = zod.output<typeof ${entry.name}>;`;
|
|
1578
|
+
}).join("\n\n")}\n`;
|
|
1579
|
+
}
|
|
1389
1580
|
async function writeZodSchemas(builder, schemasPath, fileExtension, header, output) {
|
|
1581
|
+
if (output.override.zod.generateReusableSchemas) {
|
|
1582
|
+
await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output);
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1390
1585
|
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
1391
1586
|
const schemasToWrite = [];
|
|
1392
1587
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
@@ -1418,6 +1613,39 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
|
|
|
1418
1613
|
}
|
|
1419
1614
|
if (output.indexFiles) await writeZodSchemaIndex(schemasPath, fileExtension, header, groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName), output.namingConvention, false);
|
|
1420
1615
|
}
|
|
1616
|
+
async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output) {
|
|
1617
|
+
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
1618
|
+
const strict = output.override.zod.strict.body;
|
|
1619
|
+
const coerce = output.override.zod.coerce.body;
|
|
1620
|
+
const context = {
|
|
1621
|
+
spec: builder.spec,
|
|
1622
|
+
target: builder.target,
|
|
1623
|
+
workspace: "",
|
|
1624
|
+
output
|
|
1625
|
+
};
|
|
1626
|
+
const refs = builder.schemas.map(({ name }) => `#/components/schemas/${name}`).filter((ref) => {
|
|
1627
|
+
const schemaName = ref.slice(21);
|
|
1628
|
+
return (builder.spec.components?.schemas ?? {})[schemaName] !== void 0;
|
|
1629
|
+
});
|
|
1630
|
+
resolveSchemaNames(refs, output.namingConvention);
|
|
1631
|
+
const rewritten = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
|
|
1632
|
+
strict,
|
|
1633
|
+
isZodV4,
|
|
1634
|
+
coerce
|
|
1635
|
+
}));
|
|
1636
|
+
for (const entry of rewritten) {
|
|
1637
|
+
const fileName = conventionName(entry.name, output.namingConvention);
|
|
1638
|
+
const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
|
|
1639
|
+
const importExt = fileExtension.replace(/\.ts$/, "");
|
|
1640
|
+
const imports = [...entry.usedRefs].filter((r) => r !== entry.name).toSorted().map((r) => {
|
|
1641
|
+
return `import { ${r} } from './${conventionName(r, output.namingConvention)}${importExt}';`;
|
|
1642
|
+
}).join("\n");
|
|
1643
|
+
const consts = entry.consts ? `${entry.consts}\n\n` : "";
|
|
1644
|
+
const fileContent = `${header}import { z as zod } from 'zod';\n` + (imports ? `${imports}\n\n` : "\n") + `${consts}export const ${entry.name} = ${entry.zod};\n\nexport type ${entry.name} = zod.input<typeof ${entry.name}>;\nexport type ${entry.name}Output = zod.output<typeof ${entry.name}>;\n`;
|
|
1645
|
+
await fs.outputFile(filePath, fileContent);
|
|
1646
|
+
}
|
|
1647
|
+
if (output.indexFiles && rewritten.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, rewritten.map((e) => e.name), output.namingConvention, true);
|
|
1648
|
+
}
|
|
1421
1649
|
async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension, header, output, context) {
|
|
1422
1650
|
const zodContext = context;
|
|
1423
1651
|
const verbOptionsArray = Object.values(verbOptions);
|
|
@@ -1425,6 +1653,7 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
1425
1653
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
1426
1654
|
const strict = output.override.zod.strict.body;
|
|
1427
1655
|
const coerce = output.override.zod.coerce.body;
|
|
1656
|
+
const useReusableSchemas = output.override.zod.generateReusableSchemas === true;
|
|
1428
1657
|
const uniqueVerbsSchemas = dedupeSchemasByName(verbOptionsArray.flatMap((verbOption) => {
|
|
1429
1658
|
const operation = verbOption.originalOperation;
|
|
1430
1659
|
const shouldGenerate = {
|
|
@@ -1440,7 +1669,7 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
1440
1669
|
const bodySchema = bodyMedia?.schema;
|
|
1441
1670
|
const bodySchemas = shouldGenerate.body && bodySchema ? [{
|
|
1442
1671
|
name: `${pascal(verbOption.operationName)}Body`,
|
|
1443
|
-
schema: dereference(bodySchema, zodContext),
|
|
1672
|
+
schema: useReusableSchemas ? bodySchema : dereference(bodySchema, zodContext),
|
|
1444
1673
|
bodyContentType,
|
|
1445
1674
|
encoding: bodyMedia?.encoding
|
|
1446
1675
|
}] : [];
|
|
@@ -1450,7 +1679,7 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
1450
1679
|
name: `${pascal(verbOption.operationName)}Params`,
|
|
1451
1680
|
schema: {
|
|
1452
1681
|
type: "object",
|
|
1453
|
-
properties: Object.fromEntries(queryParams.filter((p) => "schema" in p && p.schema).map((p) => [p.name, dereference(p.schema, zodContext)])),
|
|
1682
|
+
properties: Object.fromEntries(queryParams.filter((p) => "schema" in p && p.schema).map((p) => [p.name, useReusableSchemas ? p.schema : dereference(p.schema, zodContext)])),
|
|
1454
1683
|
required: queryParams.filter((p) => p.required).map((p) => p.name).filter((name) => name !== void 0)
|
|
1455
1684
|
}
|
|
1456
1685
|
}] : [];
|
|
@@ -1459,13 +1688,13 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
1459
1688
|
name: `${pascal(verbOption.operationName)}Headers`,
|
|
1460
1689
|
schema: {
|
|
1461
1690
|
type: "object",
|
|
1462
|
-
properties: Object.fromEntries(headerParams.filter((p) => "schema" in p && p.schema).map((p) => [p.name, dereference(p.schema, zodContext)])),
|
|
1691
|
+
properties: Object.fromEntries(headerParams.filter((p) => "schema" in p && p.schema).map((p) => [p.name, useReusableSchemas ? p.schema : dereference(p.schema, zodContext)])),
|
|
1463
1692
|
required: headerParams.filter((p) => p.required).map((p) => p.name).filter((name) => name !== void 0)
|
|
1464
1693
|
}
|
|
1465
1694
|
}] : [];
|
|
1466
1695
|
const responseSchemas = shouldGenerate.response ? [...verbOption.response.types.success, ...verbOption.response.types.errors].filter((responseType) => !!responseType.originalSchema && !responseType.isRef && isValidSchemaIdentifier(responseType.value) && !isPrimitiveSchemaName(responseType.value)).map((responseType) => ({
|
|
1467
1696
|
name: responseType.value,
|
|
1468
|
-
schema: dereference(responseType.originalSchema, zodContext)
|
|
1697
|
+
schema: useReusableSchemas ? responseType.originalSchema : dereference(responseType.originalSchema, zodContext)
|
|
1469
1698
|
})) : [];
|
|
1470
1699
|
return dedupeSchemasByName([
|
|
1471
1700
|
...bodySchemas,
|
|
@@ -1476,10 +1705,14 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
1476
1705
|
}));
|
|
1477
1706
|
const schemasToWrite = [];
|
|
1478
1707
|
for (const entry of uniqueVerbsSchemas) {
|
|
1708
|
+
if (useReusableSchemas && entry.schema && typeof entry.schema.$ref === "string" && Object.keys(entry.schema).length === 1) continue;
|
|
1479
1709
|
const { name, schema } = entry;
|
|
1480
1710
|
const fileName = conventionName(name, output.namingConvention);
|
|
1481
1711
|
const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
|
|
1482
|
-
const parsedZodDefinition = parseZodValidationSchemaDefinition("bodyContentType" in entry && entry.bodyContentType === "multipart/form-data" ? generateFormDataZodSchema(schema, zodContext, name, strict, isZodV4, "encoding" in entry ? entry.encoding : void 0) : generateZodValidationSchemaDefinition(schema, zodContext, name, strict, isZodV4, {
|
|
1712
|
+
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, {
|
|
1713
|
+
required: true,
|
|
1714
|
+
useReusableSchemas
|
|
1715
|
+
}), zodContext, coerce, strict, isZodV4);
|
|
1483
1716
|
schemasToWrite.push({
|
|
1484
1717
|
schemaName: name,
|
|
1485
1718
|
filePath,
|
|
@@ -1545,104 +1778,128 @@ async function addOperationSchemasReExport(schemaPath, operationSchemasPath, hea
|
|
|
1545
1778
|
await fs.outputFile(schemaIndexPath, content);
|
|
1546
1779
|
}
|
|
1547
1780
|
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Emit `<schemas-dir>/index.faker.ts` (or `<output-root>/schemas.faker.ts`
|
|
1783
|
+
* when `output.schemas` is not configured) when a faker generator entry has
|
|
1784
|
+
* `schemas: true`. Each `components/schemas` entry becomes a
|
|
1785
|
+
* `get<SchemaName>Mock(overrides)` factory in the file. Returns the written
|
|
1786
|
+
* file path so callers can include it in formatter / hook runs, or
|
|
1787
|
+
* `undefined` if no file was written.
|
|
1788
|
+
*/
|
|
1789
|
+
async function writeFakerSchemaMocks(builder, options, header) {
|
|
1790
|
+
const { output } = options;
|
|
1791
|
+
const fakerEntry = output.mock.generators.find((g) => !isFunction(g) && g.type === OutputMockType.FAKER && g.schemas === true);
|
|
1792
|
+
if (!fakerEntry) return;
|
|
1793
|
+
const schemasWithDef = builder.schemas.filter((s) => !!s.schema);
|
|
1794
|
+
if (schemasWithDef.length === 0) return;
|
|
1795
|
+
const { implementation, imports } = generateFakerForSchemas(schemasWithDef, {
|
|
1796
|
+
spec: builder.spec,
|
|
1797
|
+
target: builder.target,
|
|
1798
|
+
workspace: "",
|
|
1799
|
+
output
|
|
1800
|
+
}, fakerEntry);
|
|
1801
|
+
if (!implementation.trim()) return;
|
|
1802
|
+
let filePath;
|
|
1803
|
+
let schemaImportPath;
|
|
1804
|
+
const fileExtension = output.fileExtension || ".ts";
|
|
1805
|
+
if (output.schemas) {
|
|
1806
|
+
const schemasDir = isString(output.schemas) ? output.schemas : output.schemas.path;
|
|
1807
|
+
filePath = path.join(schemasDir, `index.faker${fileExtension}`);
|
|
1808
|
+
schemaImportPath = ".";
|
|
1809
|
+
} else {
|
|
1810
|
+
const targetInfo = output.target ? getFileInfo(output.target, { extension: fileExtension }) : void 0;
|
|
1811
|
+
const dir = targetInfo?.dirname ?? process.cwd();
|
|
1812
|
+
filePath = path.join(dir, `schemas.faker${fileExtension}`);
|
|
1813
|
+
schemaImportPath = targetInfo ? `./${targetInfo.filename}${getImportExtension(fileExtension, output.tsconfig)}` : void 0;
|
|
1814
|
+
}
|
|
1815
|
+
const reroutedImports = imports.map((imp) => imp.importPath ? imp : {
|
|
1816
|
+
...imp,
|
|
1817
|
+
importPath: schemaImportPath
|
|
1818
|
+
});
|
|
1819
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1820
|
+
for (const imp of reroutedImports) {
|
|
1821
|
+
const key = imp.importPath ?? "";
|
|
1822
|
+
if (!key) continue;
|
|
1823
|
+
const bucket = grouped.get(key) ?? [];
|
|
1824
|
+
bucket.push(imp);
|
|
1825
|
+
grouped.set(key, bucket);
|
|
1826
|
+
}
|
|
1827
|
+
const content = `${header}${generateDependencyImports(implementation, [{
|
|
1828
|
+
exports: [{
|
|
1829
|
+
name: "faker",
|
|
1830
|
+
values: true
|
|
1831
|
+
}],
|
|
1832
|
+
dependency: fakerEntry.locale ? `@faker-js/faker/locale/${fakerEntry.locale}` : "@faker-js/faker"
|
|
1833
|
+
}, ...[...grouped.entries()].map(([dependency, exports]) => ({
|
|
1834
|
+
exports,
|
|
1835
|
+
dependency
|
|
1836
|
+
}))], void 0, !!output.schemas, false)}\n\n${implementation}`;
|
|
1837
|
+
await writeGeneratedFile(filePath, content);
|
|
1838
|
+
return filePath;
|
|
1839
|
+
}
|
|
1548
1840
|
async function writeSpecs(builder, workspace, options, projectName) {
|
|
1549
1841
|
const { info, schemas, target } = builder;
|
|
1550
1842
|
const { output } = options;
|
|
1551
1843
|
const projectTitle = projectName ?? info.title;
|
|
1552
1844
|
const header = getHeader(output.override.header, info);
|
|
1553
|
-
if (output.schemas)
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
schemaPath,
|
|
1564
|
-
schemas: regularSchemas,
|
|
1565
|
-
target,
|
|
1566
|
-
namingConvention: output.namingConvention,
|
|
1567
|
-
fileExtension,
|
|
1568
|
-
header,
|
|
1569
|
-
indexFiles: output.indexFiles,
|
|
1570
|
-
tsconfig: output.tsconfig
|
|
1845
|
+
if (output.schemas) {
|
|
1846
|
+
const schemasPath = isString(output.schemas) ? output.schemas : output.schemas.path;
|
|
1847
|
+
if (!isString(output.schemas) && output.schemas.type === "zod" || isString(output.schemas) && output.client === "zod" && output.override.zod.generateReusableSchemas) {
|
|
1848
|
+
const fileExtension = output.schemaFileExtension;
|
|
1849
|
+
await writeZodSchemas(builder, schemasPath, fileExtension, header, output);
|
|
1850
|
+
await writeZodSchemasFromVerbs(builder.verbOptions, schemasPath, fileExtension, header, output, {
|
|
1851
|
+
spec: builder.spec,
|
|
1852
|
+
target: builder.target,
|
|
1853
|
+
workspace,
|
|
1854
|
+
output
|
|
1571
1855
|
});
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1856
|
+
} else {
|
|
1857
|
+
const fileExtension = output.fileExtension || ".ts";
|
|
1858
|
+
if (output.operationSchemas) {
|
|
1859
|
+
const { regularSchemas, operationSchemas: opSchemas } = splitSchemasByType(schemas);
|
|
1860
|
+
const regularSchemaNames = new Set(regularSchemas.map((s) => s.name));
|
|
1861
|
+
const operationSchemaNames = new Set(opSchemas.map((s) => s.name));
|
|
1862
|
+
fixCrossDirectoryImports(opSchemas, regularSchemaNames, schemasPath, output.operationSchemas, output.namingConvention, fileExtension, output.tsconfig);
|
|
1863
|
+
fixRegularSchemaImports(regularSchemas, operationSchemaNames, schemasPath, output.operationSchemas, output.namingConvention, fileExtension, output.tsconfig);
|
|
1864
|
+
if (regularSchemas.length > 0) await writeSchemas({
|
|
1865
|
+
schemaPath: schemasPath,
|
|
1866
|
+
schemas: regularSchemas,
|
|
1576
1867
|
target,
|
|
1577
1868
|
namingConvention: output.namingConvention,
|
|
1578
1869
|
fileExtension,
|
|
1579
1870
|
header,
|
|
1580
1871
|
indexFiles: output.indexFiles,
|
|
1581
|
-
tsconfig: output.tsconfig
|
|
1872
|
+
tsconfig: output.tsconfig,
|
|
1873
|
+
factoryOutputDirectory: output.factoryMethods?.outputDirectory
|
|
1582
1874
|
});
|
|
1583
|
-
if (
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
const operationSchemaNames = new Set(opSchemas.map((s) => s.name));
|
|
1601
|
-
fixCrossDirectoryImports(opSchemas, regularSchemaNames, output.schemas.path, output.operationSchemas, output.namingConvention, fileExtension, output.tsconfig);
|
|
1602
|
-
fixRegularSchemaImports(regularSchemas, operationSchemaNames, output.schemas.path, output.operationSchemas, output.namingConvention, fileExtension, output.tsconfig);
|
|
1603
|
-
if (regularSchemas.length > 0) await writeSchemas({
|
|
1604
|
-
schemaPath: output.schemas.path,
|
|
1605
|
-
schemas: regularSchemas,
|
|
1875
|
+
if (opSchemas.length > 0) {
|
|
1876
|
+
await writeSchemas({
|
|
1877
|
+
schemaPath: output.operationSchemas,
|
|
1878
|
+
schemas: opSchemas,
|
|
1879
|
+
target,
|
|
1880
|
+
namingConvention: output.namingConvention,
|
|
1881
|
+
fileExtension,
|
|
1882
|
+
header,
|
|
1883
|
+
indexFiles: output.indexFiles,
|
|
1884
|
+
tsconfig: output.tsconfig,
|
|
1885
|
+
factoryOutputDirectory: output.factoryMethods?.outputDirectory
|
|
1886
|
+
});
|
|
1887
|
+
if (output.indexFiles) await addOperationSchemasReExport(schemasPath, output.operationSchemas, header);
|
|
1888
|
+
}
|
|
1889
|
+
} else await writeSchemas({
|
|
1890
|
+
schemaPath: schemasPath,
|
|
1891
|
+
schemas,
|
|
1606
1892
|
target,
|
|
1607
1893
|
namingConvention: output.namingConvention,
|
|
1608
1894
|
fileExtension,
|
|
1609
1895
|
header,
|
|
1610
1896
|
indexFiles: output.indexFiles,
|
|
1611
|
-
tsconfig: output.tsconfig
|
|
1897
|
+
tsconfig: output.tsconfig,
|
|
1898
|
+
factoryOutputDirectory: output.factoryMethods?.outputDirectory
|
|
1612
1899
|
});
|
|
1613
|
-
|
|
1614
|
-
await writeSchemas({
|
|
1615
|
-
schemaPath: output.operationSchemas,
|
|
1616
|
-
schemas: opSchemas,
|
|
1617
|
-
target,
|
|
1618
|
-
namingConvention: output.namingConvention,
|
|
1619
|
-
fileExtension,
|
|
1620
|
-
header,
|
|
1621
|
-
indexFiles: output.indexFiles,
|
|
1622
|
-
tsconfig: output.tsconfig
|
|
1623
|
-
});
|
|
1624
|
-
if (output.indexFiles) await addOperationSchemasReExport(output.schemas.path, output.operationSchemas, header);
|
|
1625
|
-
}
|
|
1626
|
-
} else await writeSchemas({
|
|
1627
|
-
schemaPath: output.schemas.path,
|
|
1628
|
-
schemas,
|
|
1629
|
-
target,
|
|
1630
|
-
namingConvention: output.namingConvention,
|
|
1631
|
-
fileExtension,
|
|
1632
|
-
header,
|
|
1633
|
-
indexFiles: output.indexFiles,
|
|
1634
|
-
tsconfig: output.tsconfig
|
|
1635
|
-
});
|
|
1636
|
-
} else {
|
|
1637
|
-
const fileExtension = ".zod.ts";
|
|
1638
|
-
await writeZodSchemas(builder, output.schemas.path, fileExtension, header, output);
|
|
1639
|
-
await writeZodSchemasFromVerbs(builder.verbOptions, output.schemas.path, fileExtension, header, output, {
|
|
1640
|
-
spec: builder.spec,
|
|
1641
|
-
target: builder.target,
|
|
1642
|
-
workspace,
|
|
1643
|
-
output
|
|
1644
|
-
});
|
|
1900
|
+
}
|
|
1645
1901
|
}
|
|
1902
|
+
const fakerSchemaPath = await writeFakerSchemaMocks(builder, options, header);
|
|
1646
1903
|
let implementationPaths = [];
|
|
1647
1904
|
if (output.target) {
|
|
1648
1905
|
const writeMode = getWriteMode(output.mode);
|
|
@@ -1684,6 +1941,7 @@ async function writeSpecs(builder, workspace, options, projectName) {
|
|
|
1684
1941
|
}
|
|
1685
1942
|
const paths = [
|
|
1686
1943
|
...output.schemas ? [getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path).dirname] : [],
|
|
1944
|
+
...fakerSchemaPath ? [fakerSchemaPath] : [],
|
|
1687
1945
|
...output.operationSchemas ? [getFileInfo(output.operationSchemas).dirname] : [],
|
|
1688
1946
|
...implementationPaths
|
|
1689
1947
|
];
|
|
@@ -1814,4 +2072,4 @@ async function loadConfigFile(configFilePath) {
|
|
|
1814
2072
|
//#endregion
|
|
1815
2073
|
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 };
|
|
1816
2074
|
|
|
1817
|
-
//# sourceMappingURL=config-
|
|
2075
|
+
//# sourceMappingURL=config-Cyybmy9c.mjs.map
|