orval 8.13.0 → 8.14.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-yIkhToq8.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,5 +1,5 @@
|
|
|
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, 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";
|
|
2
|
+
import { FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SupportedFormatter, asyncReduce, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateMutator, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getImportExtension, getMockFileExtensionByTypeName, getRefInfo, getRoute, isBoolean, isComponentRef, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, resolveValue, splitSchemasByType, upath, writeGeneratedFile, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
|
|
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";
|
|
@@ -8,6 +8,7 @@ import * as mock from "@orval/mock";
|
|
|
8
8
|
import { generateFakerForSchemas, generateMockImports, getDefaultMockOptionsForType } from "@orval/mock";
|
|
9
9
|
import angular from "@orval/angular";
|
|
10
10
|
import axios from "@orval/axios";
|
|
11
|
+
import effect from "@orval/effect";
|
|
11
12
|
import fetchClient from "@orval/fetch";
|
|
12
13
|
import hono from "@orval/hono";
|
|
13
14
|
import mcp from "@orval/mcp";
|
|
@@ -28,7 +29,7 @@ import { createJiti } from "jiti";
|
|
|
28
29
|
//#region package.json
|
|
29
30
|
var name = "orval";
|
|
30
31
|
var description = "A swagger client generator for typescript";
|
|
31
|
-
var version = "8.
|
|
32
|
+
var version = "8.14.0";
|
|
32
33
|
//#endregion
|
|
33
34
|
//#region src/client.ts
|
|
34
35
|
const DEFAULT_CLIENT = OutputClient.AXIOS;
|
|
@@ -61,6 +62,7 @@ const getGeneratorClient = (outputClient, output) => {
|
|
|
61
62
|
})(),
|
|
62
63
|
swr: swr()(),
|
|
63
64
|
zod: zod()(),
|
|
65
|
+
effect: effect()(),
|
|
64
66
|
hono: hono()(),
|
|
65
67
|
fetch: fetchClient()(),
|
|
66
68
|
mcp: mcp()()
|
|
@@ -854,6 +856,26 @@ function normalizeSchemasOption(schemas, workspace) {
|
|
|
854
856
|
type: schemas.type
|
|
855
857
|
};
|
|
856
858
|
}
|
|
859
|
+
function normalizeEffectOptions(effect) {
|
|
860
|
+
return {
|
|
861
|
+
strict: {
|
|
862
|
+
param: effect?.strict?.param ?? false,
|
|
863
|
+
query: effect?.strict?.query ?? false,
|
|
864
|
+
header: effect?.strict?.header ?? false,
|
|
865
|
+
body: effect?.strict?.body ?? false,
|
|
866
|
+
response: effect?.strict?.response ?? false
|
|
867
|
+
},
|
|
868
|
+
generate: {
|
|
869
|
+
param: effect?.generate?.param ?? true,
|
|
870
|
+
query: effect?.generate?.query ?? true,
|
|
871
|
+
header: effect?.generate?.header ?? true,
|
|
872
|
+
body: effect?.generate?.body ?? true,
|
|
873
|
+
response: effect?.generate?.response ?? true
|
|
874
|
+
},
|
|
875
|
+
generateEachHttpStatus: effect?.generateEachHttpStatus ?? false,
|
|
876
|
+
useBrandedTypes: effect?.useBrandedTypes ?? false
|
|
877
|
+
};
|
|
878
|
+
}
|
|
857
879
|
async function normalizeOptions(optionsExport, workspace = process.cwd(), globalOptions = {}) {
|
|
858
880
|
const options = await (isFunction(optionsExport) ? optionsExport() : optionsExport);
|
|
859
881
|
if (!options.input) throw new Error(styleText("red", `Config requires an input.`));
|
|
@@ -908,6 +930,7 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
|
|
|
908
930
|
shouldExportMutatorHooks: true,
|
|
909
931
|
shouldExportHttpClient: true,
|
|
910
932
|
shouldExportQueryKey: true,
|
|
933
|
+
shouldFilterQueryKey: false,
|
|
911
934
|
shouldSplitQueryKey: false,
|
|
912
935
|
...normalizeQueryOptions(outputOptions.override?.query, workspace)
|
|
913
936
|
};
|
|
@@ -1013,12 +1036,15 @@ async function normalizeOptions(optionsExport, workspace = process.cwd(), global
|
|
|
1013
1036
|
...outputOptions.override?.zod?.preprocess?.body ? { body: normalizeMutator(workspace, outputOptions.override.zod.preprocess.body) } : {},
|
|
1014
1037
|
...outputOptions.override?.zod?.preprocess?.response ? { response: normalizeMutator(workspace, outputOptions.override.zod.preprocess.response) } : {}
|
|
1015
1038
|
},
|
|
1039
|
+
...outputOptions.override?.zod?.params ? { params: normalizeMutator(workspace, outputOptions.override.zod.params) } : {},
|
|
1016
1040
|
generateEachHttpStatus: outputOptions.override?.zod?.generateEachHttpStatus ?? false,
|
|
1017
1041
|
useBrandedTypes: outputOptions.override?.zod?.useBrandedTypes ?? false,
|
|
1018
1042
|
generateReusableSchemas: outputOptions.override?.zod?.generateReusableSchemas ?? false,
|
|
1043
|
+
generateMeta: outputOptions.override?.zod?.generateMeta ?? false,
|
|
1019
1044
|
dateTimeOptions: outputOptions.override?.zod?.dateTimeOptions ?? { offset: true },
|
|
1020
1045
|
timeOptions: outputOptions.override?.zod?.timeOptions ?? {}
|
|
1021
1046
|
},
|
|
1047
|
+
effect: normalizeEffectOptions(outputOptions.override?.effect),
|
|
1022
1048
|
swr: {
|
|
1023
1049
|
generateErrorTypes: false,
|
|
1024
1050
|
...outputOptions.override?.swr
|
|
@@ -1144,7 +1170,7 @@ function normalizePath(path$1, workspace) {
|
|
|
1144
1170
|
return path.resolve(workspace, path$1);
|
|
1145
1171
|
}
|
|
1146
1172
|
function normalizeOperationsAndTags(operationsOrTags, workspace, global) {
|
|
1147
|
-
return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, paramsFilter, query, angular, zod, ...rest }]) => {
|
|
1173
|
+
return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, paramsFilter, query, angular, zod, effect, ...rest }]) => {
|
|
1148
1174
|
return [key, {
|
|
1149
1175
|
...rest,
|
|
1150
1176
|
...angular ? { angular: {
|
|
@@ -1183,12 +1209,15 @@ function normalizeOperationsAndTags(operationsOrTags, workspace, global) {
|
|
|
1183
1209
|
...zod.preprocess?.body ? { body: normalizeMutator(workspace, zod.preprocess.body) } : {},
|
|
1184
1210
|
...zod.preprocess?.response ? { response: normalizeMutator(workspace, zod.preprocess.response) } : {}
|
|
1185
1211
|
},
|
|
1212
|
+
...zod.params ? { params: normalizeMutator(workspace, zod.params) } : {},
|
|
1186
1213
|
generateEachHttpStatus: zod.generateEachHttpStatus ?? false,
|
|
1187
1214
|
useBrandedTypes: zod.useBrandedTypes ?? false,
|
|
1188
1215
|
generateReusableSchemas: zod.generateReusableSchemas ?? false,
|
|
1216
|
+
generateMeta: zod.generateMeta ?? false,
|
|
1189
1217
|
dateTimeOptions: zod.dateTimeOptions ?? { offset: true },
|
|
1190
1218
|
timeOptions: zod.timeOptions ?? {}
|
|
1191
1219
|
} } : {},
|
|
1220
|
+
...effect ? { effect: normalizeEffectOptions(effect) } : {},
|
|
1192
1221
|
...transformer ? { transformer: normalizePath(transformer, workspace) } : {},
|
|
1193
1222
|
...mutator ? { mutator: normalizeMutator(workspace, mutator) } : {},
|
|
1194
1223
|
...formData === void 0 ? {} : { formData: createFormData(workspace, formData) },
|
|
@@ -1258,6 +1287,8 @@ function normalizeQueryOptions(queryOptions = {}, outputWorkspace, globalOptions
|
|
|
1258
1287
|
...queryOptions.mutationOptions ? { mutationOptions: normalizeMutator(outputWorkspace, queryOptions.mutationOptions) } : {},
|
|
1259
1288
|
...isNullish(globalOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: globalOptions.shouldExportQueryKey },
|
|
1260
1289
|
...isNullish(queryOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: queryOptions.shouldExportQueryKey },
|
|
1290
|
+
...isNullish(globalOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: globalOptions.shouldFilterQueryKey },
|
|
1291
|
+
...isNullish(queryOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: queryOptions.shouldFilterQueryKey },
|
|
1261
1292
|
...isNullish(globalOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: globalOptions.shouldExportHttpClient },
|
|
1262
1293
|
...isNullish(queryOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: queryOptions.shouldExportHttpClient },
|
|
1263
1294
|
...isNullish(globalOptions.shouldExportMutatorHooks) ? {} : { shouldExportMutatorHooks: globalOptions.shouldExportMutatorHooks },
|
|
@@ -1325,30 +1356,29 @@ async function startWatcher(watchOptions, watchFn, defaultTarget = ".") {
|
|
|
1325
1356
|
}
|
|
1326
1357
|
//#endregion
|
|
1327
1358
|
//#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
1359
|
/**
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1360
|
+
* Resolve the export identifier for a `#/components/schemas/X` ref. We reuse
|
|
1361
|
+
* `@orval/core`'s `getRefInfo(...).name` (`pascal` + sanitize + component
|
|
1362
|
+
* suffix) so reusable zod schema exports match the operation wrappers and the
|
|
1363
|
+
* TS model types exactly. `namingConvention` deliberately does NOT influence
|
|
1364
|
+
* the identifier — it governs file names only, consistent with the rest of
|
|
1365
|
+
* orval. The same call powers the generator's `namedRef` emission, so the
|
|
1366
|
+
* definition name and every reference stay in sync.
|
|
1335
1367
|
*/
|
|
1336
|
-
const resolveSchemaName = (ref,
|
|
1337
|
-
const JS_IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1368
|
+
const resolveSchemaName = (ref, context) => getRefInfo(ref, context).name;
|
|
1338
1369
|
/**
|
|
1339
|
-
* Resolve names for a set of refs, throwing on conflicts
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1342
|
-
*
|
|
1370
|
+
* Resolve names for a set of refs, throwing on conflicts (two distinct refs
|
|
1371
|
+
* collapsing to the same identifier). The mapping is the single source of
|
|
1372
|
+
* truth for cross-schema references — the generator, the orchestrator's graph,
|
|
1373
|
+
* and the sentinel rewriter all consult it.
|
|
1343
1374
|
*/
|
|
1344
|
-
const resolveSchemaNames = (refs,
|
|
1375
|
+
const resolveSchemaNames = (refs, context) => {
|
|
1345
1376
|
const resolved = /* @__PURE__ */ new Map();
|
|
1346
1377
|
const reverse = /* @__PURE__ */ new Map();
|
|
1347
1378
|
for (const ref of refs) {
|
|
1348
|
-
const name = resolveSchemaName(ref,
|
|
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.`);
|
|
1379
|
+
const name = resolveSchemaName(ref, context);
|
|
1350
1380
|
const previous = reverse.get(name);
|
|
1351
|
-
if (previous !== void 0 && previous !== ref) throw new Error(`[orval/zod] generateReusableSchemas: refs ${previous} and ${ref} both
|
|
1381
|
+
if (previous !== void 0 && previous !== ref) throw new Error(`[orval/zod] generateReusableSchemas: refs ${previous} and ${ref} both resolve to the export name "${name}". Rename one in the OpenAPI source.`);
|
|
1352
1382
|
resolved.set(ref, name);
|
|
1353
1383
|
reverse.set(name, ref);
|
|
1354
1384
|
}
|
|
@@ -1366,7 +1396,7 @@ const generateReusableSchemaSet = (refs, context, options) => {
|
|
|
1366
1396
|
const nameToRef = /* @__PURE__ */ new Map();
|
|
1367
1397
|
for (const schemaName of Object.keys(componentSchemas)) {
|
|
1368
1398
|
const ref = `#/components/schemas/${schemaName}`;
|
|
1369
|
-
nameToRef.set(resolveSchemaName(ref, context
|
|
1399
|
+
nameToRef.set(resolveSchemaName(ref, context), ref);
|
|
1370
1400
|
}
|
|
1371
1401
|
const queue = [...refs];
|
|
1372
1402
|
const seen = new Set(refs);
|
|
@@ -1374,11 +1404,17 @@ const generateReusableSchemaSet = (refs, context, options) => {
|
|
|
1374
1404
|
for (const ref of queue) {
|
|
1375
1405
|
const schema = componentSchemas[ref.slice(21)];
|
|
1376
1406
|
if (!schema) continue;
|
|
1377
|
-
const name = resolveSchemaName(ref, context
|
|
1407
|
+
const name = resolveSchemaName(ref, context);
|
|
1378
1408
|
const parsed = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(schema, context, name, options.strict, options.isZodV4, {
|
|
1379
1409
|
required: true,
|
|
1380
|
-
useReusableSchemas: true
|
|
1381
|
-
|
|
1410
|
+
useReusableSchemas: true,
|
|
1411
|
+
emitMeta: options.generateMeta
|
|
1412
|
+
}), context, options.coerce ?? false, options.strict, options.isZodV4, void 0, options.paramsMutator ? {
|
|
1413
|
+
mutator: options.paramsMutator,
|
|
1414
|
+
operationId: "",
|
|
1415
|
+
location: "schema",
|
|
1416
|
+
schemaName: name
|
|
1417
|
+
} : void 0);
|
|
1382
1418
|
entries.push({
|
|
1383
1419
|
ref,
|
|
1384
1420
|
name,
|
|
@@ -1445,11 +1481,27 @@ const tarjan = (graph) => {
|
|
|
1445
1481
|
};
|
|
1446
1482
|
const SENTINEL_PATTERN = /__REF_([A-Za-z_$][A-Za-z0-9_$]*)__/g;
|
|
1447
1483
|
/**
|
|
1484
|
+
* Replace every `__REF_<name>__` sentinel with the bare identifier. Use this
|
|
1485
|
+
* for schemas that sit at the top of the dependency graph (operation params,
|
|
1486
|
+
* bodies, responses) — they can never participate in a cycle with the
|
|
1487
|
+
* component schemas they reference, so every ref is a direct (non-lazy) one.
|
|
1488
|
+
*/
|
|
1489
|
+
const rewriteSentinelsToDirect = (zod) => zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => refName);
|
|
1490
|
+
/**
|
|
1448
1491
|
* Replace every `__REF_<name>__` sentinel with either the bare identifier or
|
|
1449
1492
|
* `zod.lazy(() => <name>)` based on whether the edge closes a cycle, then
|
|
1450
1493
|
* reorder entries so that every non-lazy reference is emitted AFTER its
|
|
1451
1494
|
* target. This avoids TDZ errors at module load.
|
|
1452
1495
|
*
|
|
1496
|
+
* Entries that sit in a cycle (SCC of size > 1, or a self-loop) are flagged
|
|
1497
|
+
* `isRecursive`. Their generated `const` reads its own binding inside the
|
|
1498
|
+
* initializer (through the `zod.lazy` wrapper), which TypeScript rejects with
|
|
1499
|
+
* TS7022 ("'X' implicitly has type 'any' ... referenced directly or indirectly
|
|
1500
|
+
* in its own initializer") unless the `const` carries an explicit type
|
|
1501
|
+
* annotation. The writer (`write-zod-specs`) supplies that annotation —
|
|
1502
|
+
* `const X: zod.ZodType<X>` — backed by a generated TS type, which both
|
|
1503
|
+
* silences TS7022 and preserves full `z.infer` typing through the recursion.
|
|
1504
|
+
*
|
|
1453
1505
|
* Both the lazy classification and the emit order come from a single Tarjan
|
|
1454
1506
|
* run, guaranteeing they agree: a non-lazy edge u→v means v is visited (and
|
|
1455
1507
|
* popped) before u in DFS, so v appears earlier in the SCC array → emitted
|
|
@@ -1459,13 +1511,17 @@ const rewriteReusableSchemas = (entries) => {
|
|
|
1459
1511
|
const graph = new Map(entries.map((e) => [e.name, new Set(e.usedRefs)]));
|
|
1460
1512
|
for (const e of entries) for (const ref of e.usedRefs) if (!graph.has(ref)) graph.set(ref, /* @__PURE__ */ new Set());
|
|
1461
1513
|
const { sccs, lazyEdges } = tarjan(graph);
|
|
1514
|
+
const recursiveNames = /* @__PURE__ */ new Set();
|
|
1515
|
+
for (const scc of sccs) if (scc.length > 1) for (const name of scc) recursiveNames.add(name);
|
|
1516
|
+
else if (lazyEdges.has(edgeKey(scc[0], scc[0]))) recursiveNames.add(scc[0]);
|
|
1462
1517
|
const rewritten = new Map(entries.map((entry) => {
|
|
1463
1518
|
const newZod = entry.zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => {
|
|
1464
1519
|
return lazyEdges.has(edgeKey(entry.name, refName)) ? `zod.lazy(() => ${refName})` : refName;
|
|
1465
1520
|
});
|
|
1466
1521
|
return [entry.name, {
|
|
1467
1522
|
...entry,
|
|
1468
|
-
zod: newZod
|
|
1523
|
+
zod: newZod,
|
|
1524
|
+
isRecursive: recursiveNames.has(entry.name)
|
|
1469
1525
|
}];
|
|
1470
1526
|
}));
|
|
1471
1527
|
const out = [];
|
|
@@ -1477,18 +1533,99 @@ const rewriteReusableSchemas = (entries) => {
|
|
|
1477
1533
|
};
|
|
1478
1534
|
//#endregion
|
|
1479
1535
|
//#region src/write-zod-specs.ts
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1536
|
+
/**
|
|
1537
|
+
* Render the `import { ... } from '...'` line for a resolved
|
|
1538
|
+
* `GeneratorMutator`. Mirrors the format produced by
|
|
1539
|
+
* `generateMutatorImports` in `@orval/core` but inlined to avoid pulling in
|
|
1540
|
+
* its full surface area for a single statement.
|
|
1541
|
+
*/
|
|
1542
|
+
function buildMutatorImportStatement(mutator) {
|
|
1543
|
+
return `import ${mutator.default ? mutator.name : `{ ${mutator.name} }`} from '${mutator.path}';`;
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Whole-word substring check for a resolved mutator alias inside generated
|
|
1547
|
+
* code. Plain `String.includes` would false-positive when the user names the
|
|
1548
|
+
* mutator something like `min` against `.min(1)`.
|
|
1549
|
+
*/
|
|
1550
|
+
function bodyReferencesMutator(body, mutator) {
|
|
1551
|
+
return new RegExp(String.raw`\b${mutator.name}\b`).test(body);
|
|
1552
|
+
}
|
|
1553
|
+
function generateZodSchemaFileContent(header, schemas, includeZodImport = true) {
|
|
1554
|
+
const refImports = [...new Set(schemas.flatMap((s) => s.importStatements ?? []))].toSorted();
|
|
1555
|
+
const importBlock = [...includeZodImport ? [`import { z as zod } from 'zod';`] : [], ...refImports].join("\n");
|
|
1556
|
+
const schemaContent = schemas.map(({ schemaName, consts, zodExpression }) => {
|
|
1484
1557
|
return `${consts ? `${consts}\n` : ""}export const ${schemaName} = ${zodExpression}
|
|
1485
1558
|
|
|
1486
1559
|
export type ${schemaName} = zod.input<typeof ${schemaName}>;
|
|
1487
1560
|
export type ${schemaName}Output = zod.output<typeof ${schemaName}>;`;
|
|
1488
|
-
}).join("\n\n")
|
|
1489
|
-
`;
|
|
1561
|
+
}).join("\n\n");
|
|
1562
|
+
return `${header}${importBlock ? `${importBlock}\n\n` : ""}${schemaContent}\n`;
|
|
1563
|
+
}
|
|
1564
|
+
function renderReusableSchemaEntry(entry, context) {
|
|
1565
|
+
const consts = entry.consts ? `${entry.consts}\n\n` : "";
|
|
1566
|
+
if (entry.isRecursive) {
|
|
1567
|
+
const rawName = isComponentRef(entry.ref) ? getRefInfo(entry.ref, context).originalName : void 0;
|
|
1568
|
+
const schema = rawName ? context.spec.components?.schemas?.[rawName] : void 0;
|
|
1569
|
+
const resolved = schema ? resolveValue({
|
|
1570
|
+
schema,
|
|
1571
|
+
name: entry.name,
|
|
1572
|
+
context
|
|
1573
|
+
}) : void 0;
|
|
1574
|
+
const typeBody = resolved ? resolved.value : "unknown";
|
|
1575
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1576
|
+
const extraImports = [];
|
|
1577
|
+
for (const imp of resolved?.imports ?? []) {
|
|
1578
|
+
if (!imp.name || imp.name === entry.name) continue;
|
|
1579
|
+
const bindingKey = imp.alias ?? imp.name;
|
|
1580
|
+
if (seen.has(bindingKey)) continue;
|
|
1581
|
+
seen.add(bindingKey);
|
|
1582
|
+
extraImports.push({
|
|
1583
|
+
name: imp.name,
|
|
1584
|
+
...imp.alias ? { alias: imp.alias } : {}
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
return {
|
|
1588
|
+
content: `${consts}export type ${entry.name} = ${typeBody};\n\nexport const ${entry.name}: zod.ZodType<${entry.name}> = ${entry.zod};\n\nexport type ${entry.name}Output = zod.output<typeof ${entry.name}>;`,
|
|
1589
|
+
extraImports
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
return {
|
|
1593
|
+
content: `${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}>;`,
|
|
1594
|
+
extraImports: []
|
|
1595
|
+
};
|
|
1490
1596
|
}
|
|
1491
1597
|
const isValidSchemaIdentifier = (name) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
1598
|
+
/**
|
|
1599
|
+
* Build the sibling-file `import { … } from './…'` block for one reusable
|
|
1600
|
+
* schema file. Two sources feed in:
|
|
1601
|
+
* - `usedRefs` — names from the zod runtime expression. Sourced from the
|
|
1602
|
+
* sentinel parser, so always unaliased.
|
|
1603
|
+
* - `extraImports` — names the recursive TS body needs that the zod runtime
|
|
1604
|
+
* collapsed (`propertyNames` $refs, etc.). May carry `alias`.
|
|
1605
|
+
* Keyed by export name (`name`) so an aliased `extraImports` entry overrides
|
|
1606
|
+
* a bare `usedRefs` entry — the recursive TS body uses the local binding, so
|
|
1607
|
+
* the aliased form has to win for the file to compile. Self-refs and
|
|
1608
|
+
* non-component identifiers are filtered out.
|
|
1609
|
+
*
|
|
1610
|
+
* Exported for unit-test coverage of the alias-propagation path; no
|
|
1611
|
+
* `resolveValue` producer surfaces aliases here today, so the integration
|
|
1612
|
+
* tests can't exercise it.
|
|
1613
|
+
*/
|
|
1614
|
+
function buildSiblingImports({ usedRefs, extraImports, entryName, componentNames, namingConvention, importExt }) {
|
|
1615
|
+
const importsByName = /* @__PURE__ */ new Map();
|
|
1616
|
+
for (const name of usedRefs) {
|
|
1617
|
+
if (name === entryName) continue;
|
|
1618
|
+
importsByName.set(name, { name });
|
|
1619
|
+
}
|
|
1620
|
+
for (const imp of extraImports) {
|
|
1621
|
+
if (imp.name === entryName || !componentNames.has(imp.name)) continue;
|
|
1622
|
+
importsByName.set(imp.name, imp);
|
|
1623
|
+
}
|
|
1624
|
+
return [...importsByName.values()].toSorted((a, b) => a.name.localeCompare(b.name)).map(({ name, alias }) => {
|
|
1625
|
+
const importedFile = conventionName(name, namingConvention);
|
|
1626
|
+
return `import { ${alias ? `${name} as ${alias}` : name} } from './${importedFile}${importExt}';`;
|
|
1627
|
+
}).join("\n");
|
|
1628
|
+
}
|
|
1492
1629
|
const isPrimitiveSchemaName = (name) => [
|
|
1493
1630
|
"string",
|
|
1494
1631
|
"number",
|
|
@@ -1529,8 +1666,8 @@ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNam
|
|
|
1529
1666
|
const uniqueExports = [...new Set(allExports.split("\n"))].filter((line) => line.trim()).toSorted().join("\n");
|
|
1530
1667
|
await fs.outputFile(indexPath, `${header}\n${uniqueExports}\n`);
|
|
1531
1668
|
}
|
|
1532
|
-
function generateZodSchemasInline(builder, output) {
|
|
1533
|
-
if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output);
|
|
1669
|
+
function generateZodSchemasInline(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
|
|
1670
|
+
if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output, includeZodImport, paramsMutator, includeParamsImport);
|
|
1534
1671
|
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
1535
1672
|
if (schemasWithOpenApiDef.length === 0) return "";
|
|
1536
1673
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
@@ -1545,7 +1682,10 @@ function generateZodSchemasInline(builder, output) {
|
|
|
1545
1682
|
workspace: "",
|
|
1546
1683
|
output
|
|
1547
1684
|
};
|
|
1548
|
-
const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
|
|
1685
|
+
const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
|
|
1686
|
+
required: true,
|
|
1687
|
+
emitMeta: output.override.zod.generateMeta
|
|
1688
|
+
}), context, coerce, strict, isZodV4);
|
|
1549
1689
|
schemas.push({
|
|
1550
1690
|
schemaName: name,
|
|
1551
1691
|
consts: parsedZodDefinition.consts,
|
|
@@ -1553,11 +1693,9 @@ function generateZodSchemasInline(builder, output) {
|
|
|
1553
1693
|
});
|
|
1554
1694
|
}
|
|
1555
1695
|
if (schemas.length === 0) return "";
|
|
1556
|
-
return generateZodSchemaFileContent("", schemas);
|
|
1696
|
+
return generateZodSchemaFileContent("", schemas, includeZodImport);
|
|
1557
1697
|
}
|
|
1558
|
-
function generateZodSchemasInlineReusable(builder, output) {
|
|
1559
|
-
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
1560
|
-
if (schemasWithOpenApiDef.length === 0) return "";
|
|
1698
|
+
function generateZodSchemasInlineReusable(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
|
|
1561
1699
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
1562
1700
|
const strict = output.override.zod.strict.body;
|
|
1563
1701
|
const coerce = output.override.zod.coerce.body;
|
|
@@ -1567,19 +1705,24 @@ function generateZodSchemasInlineReusable(builder, output) {
|
|
|
1567
1705
|
workspace: "",
|
|
1568
1706
|
output
|
|
1569
1707
|
};
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
|
|
1708
|
+
const componentSchemas = builder.spec.components?.schemas ?? {};
|
|
1709
|
+
const refs = Object.keys(componentSchemas).map((schemaName) => `#/components/schemas/${schemaName}`);
|
|
1710
|
+
if (refs.length === 0) return "";
|
|
1711
|
+
resolveSchemaNames(refs, context);
|
|
1712
|
+
const body = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
|
|
1573
1713
|
strict,
|
|
1574
1714
|
isZodV4,
|
|
1575
|
-
coerce
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
}).join("\n\n")
|
|
1715
|
+
coerce,
|
|
1716
|
+
generateMeta: output.override.zod.generateMeta,
|
|
1717
|
+
paramsMutator
|
|
1718
|
+
})).map((entry) => renderReusableSchemaEntry(entry, context).content).join("\n\n");
|
|
1719
|
+
const zodImport = includeZodImport ? `import { z as zod } from 'zod';\n` : "";
|
|
1720
|
+
const paramsImport = paramsMutator && includeParamsImport && bodyReferencesMutator(body, paramsMutator) ? `${buildMutatorImportStatement(paramsMutator)}\n` : "";
|
|
1721
|
+
return `${zodImport || paramsImport ? `${zodImport}${paramsImport}\n` : ""}${body}\n`;
|
|
1579
1722
|
}
|
|
1580
|
-
async function writeZodSchemas(builder, schemasPath, fileExtension, header, output) {
|
|
1723
|
+
async function writeZodSchemas(builder, schemasPath, fileExtension, header, output, paramsMutator) {
|
|
1581
1724
|
if (output.override.zod.generateReusableSchemas) {
|
|
1582
|
-
await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output);
|
|
1725
|
+
await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator);
|
|
1583
1726
|
return;
|
|
1584
1727
|
}
|
|
1585
1728
|
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
@@ -1598,7 +1741,10 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
|
|
|
1598
1741
|
workspace: "",
|
|
1599
1742
|
output
|
|
1600
1743
|
};
|
|
1601
|
-
const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
|
|
1744
|
+
const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
|
|
1745
|
+
required: true,
|
|
1746
|
+
emitMeta: output.override.zod.generateMeta
|
|
1747
|
+
}), context, coerce, strict, isZodV4);
|
|
1602
1748
|
schemasToWrite.push({
|
|
1603
1749
|
schemaName: name,
|
|
1604
1750
|
filePath,
|
|
@@ -1613,7 +1759,7 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
|
|
|
1613
1759
|
}
|
|
1614
1760
|
if (output.indexFiles) await writeZodSchemaIndex(schemasPath, fileExtension, header, groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName), output.namingConvention, false);
|
|
1615
1761
|
}
|
|
1616
|
-
async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output) {
|
|
1762
|
+
async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator) {
|
|
1617
1763
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
1618
1764
|
const strict = output.override.zod.strict.body;
|
|
1619
1765
|
const coerce = output.override.zod.coerce.body;
|
|
@@ -1623,25 +1769,33 @@ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, head
|
|
|
1623
1769
|
workspace: "",
|
|
1624
1770
|
output
|
|
1625
1771
|
};
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
});
|
|
1630
|
-
resolveSchemaNames(refs, output.namingConvention);
|
|
1772
|
+
const componentSchemas = builder.spec.components?.schemas ?? {};
|
|
1773
|
+
const refs = Object.keys(componentSchemas).map((schemaName) => `#/components/schemas/${schemaName}`);
|
|
1774
|
+
resolveSchemaNames(refs, context);
|
|
1631
1775
|
const rewritten = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
|
|
1632
1776
|
strict,
|
|
1633
1777
|
isZodV4,
|
|
1634
|
-
coerce
|
|
1778
|
+
coerce,
|
|
1779
|
+
generateMeta: output.override.zod.generateMeta,
|
|
1780
|
+
paramsMutator
|
|
1635
1781
|
}));
|
|
1782
|
+
const componentNames = new Set(Object.keys(builder.spec.components?.schemas ?? {}).map((schemaName) => resolveSchemaName(`#/components/schemas/${schemaName}`, context)));
|
|
1783
|
+
const paramsMutatorImport = paramsMutator ? buildMutatorImportStatement(paramsMutator) : void 0;
|
|
1636
1784
|
for (const entry of rewritten) {
|
|
1637
1785
|
const fileName = conventionName(entry.name, output.namingConvention);
|
|
1638
1786
|
const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
|
|
1639
1787
|
const importExt = fileExtension.replace(/\.ts$/, "");
|
|
1640
|
-
const
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1788
|
+
const rendered = renderReusableSchemaEntry(entry, context);
|
|
1789
|
+
const refImports = buildSiblingImports({
|
|
1790
|
+
usedRefs: entry.usedRefs,
|
|
1791
|
+
extraImports: rendered.extraImports,
|
|
1792
|
+
entryName: entry.name,
|
|
1793
|
+
componentNames,
|
|
1794
|
+
namingConvention: output.namingConvention,
|
|
1795
|
+
importExt
|
|
1796
|
+
});
|
|
1797
|
+
const imports = [...!!paramsMutator && bodyReferencesMutator(entry.zod, paramsMutator) && paramsMutatorImport ? [paramsMutatorImport] : [], ...refImports ? [refImports] : []].join("\n");
|
|
1798
|
+
const fileContent = `${header}import { z as zod } from 'zod';\n` + (imports ? `${imports}\n\n` : "\n") + `${rendered.content}\n`;
|
|
1645
1799
|
await fs.outputFile(filePath, fileContent);
|
|
1646
1800
|
}
|
|
1647
1801
|
if (output.indexFiles && rewritten.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, rewritten.map((e) => e.name), output.namingConvention, true);
|
|
@@ -1713,11 +1867,21 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
1713
1867
|
required: true,
|
|
1714
1868
|
useReusableSchemas
|
|
1715
1869
|
}), zodContext, coerce, strict, isZodV4);
|
|
1870
|
+
let zodExpression = parsedZodDefinition.zod;
|
|
1871
|
+
let importStatements;
|
|
1872
|
+
if (useReusableSchemas && parsedZodDefinition.usedRefs.size > 0) {
|
|
1873
|
+
zodExpression = rewriteSentinelsToDirect(zodExpression);
|
|
1874
|
+
const importExt = fileExtension.replace(/\.ts$/, "");
|
|
1875
|
+
importStatements = [...parsedZodDefinition.usedRefs].filter((refName) => refName !== name).toSorted().map((refName) => {
|
|
1876
|
+
return `import { ${refName} } from './${conventionName(refName, output.namingConvention)}${importExt}';`;
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1716
1879
|
schemasToWrite.push({
|
|
1717
1880
|
schemaName: name,
|
|
1718
1881
|
filePath,
|
|
1719
1882
|
consts: parsedZodDefinition.consts,
|
|
1720
|
-
zodExpression
|
|
1883
|
+
zodExpression,
|
|
1884
|
+
importStatements
|
|
1721
1885
|
});
|
|
1722
1886
|
}
|
|
1723
1887
|
const groupedSchemasToWrite = groupSchemasByFilePath(schemasToWrite);
|
|
@@ -1837,6 +2001,17 @@ async function writeFakerSchemaMocks(builder, options, header) {
|
|
|
1837
2001
|
await writeGeneratedFile(filePath, content);
|
|
1838
2002
|
return filePath;
|
|
1839
2003
|
}
|
|
2004
|
+
function isSchemaValidatorClient(client) {
|
|
2005
|
+
return client === "zod" || client === "effect";
|
|
2006
|
+
}
|
|
2007
|
+
function shouldGenerateZodSchemasInline(output, hasOperations) {
|
|
2008
|
+
if (output.client !== "zod" || output.schemas) return false;
|
|
2009
|
+
if (output.override.zod.generateReusableSchemas) return true;
|
|
2010
|
+
return !hasOperations;
|
|
2011
|
+
}
|
|
2012
|
+
function shouldGenerateSchemas(output, hasOperations) {
|
|
2013
|
+
return !output.schemas && !isSchemaValidatorClient(output.client) || shouldGenerateZodSchemasInline(output, hasOperations);
|
|
2014
|
+
}
|
|
1840
2015
|
async function writeSpecs(builder, workspace, options, projectName) {
|
|
1841
2016
|
const { info, schemas, target } = builder;
|
|
1842
2017
|
const { output } = options;
|
|
@@ -1846,7 +2021,13 @@ async function writeSpecs(builder, workspace, options, projectName) {
|
|
|
1846
2021
|
const schemasPath = isString(output.schemas) ? output.schemas : output.schemas.path;
|
|
1847
2022
|
if (!isString(output.schemas) && output.schemas.type === "zod" || isString(output.schemas) && output.client === "zod" && output.override.zod.generateReusableSchemas) {
|
|
1848
2023
|
const fileExtension = output.schemaFileExtension;
|
|
1849
|
-
await writeZodSchemas(builder, schemasPath, fileExtension, header, output
|
|
2024
|
+
await writeZodSchemas(builder, schemasPath, fileExtension, header, output, output.override.zod.params ? await generateMutator({
|
|
2025
|
+
output: path.join(schemasPath, `__params__${fileExtension}`),
|
|
2026
|
+
mutator: output.override.zod.params,
|
|
2027
|
+
name: "zodParams",
|
|
2028
|
+
workspace,
|
|
2029
|
+
tsconfig: output.tsconfig
|
|
2030
|
+
}) : void 0);
|
|
1850
2031
|
await writeZodSchemasFromVerbs(builder.verbOptions, schemasPath, fileExtension, header, output, {
|
|
1851
2032
|
spec: builder.spec,
|
|
1852
2033
|
target: builder.target,
|
|
@@ -1903,17 +2084,26 @@ async function writeSpecs(builder, workspace, options, projectName) {
|
|
|
1903
2084
|
let implementationPaths = [];
|
|
1904
2085
|
if (output.target) {
|
|
1905
2086
|
const writeMode = getWriteMode(output.mode);
|
|
1906
|
-
const isZodClient = output.client === "zod";
|
|
1907
2087
|
const hasOperations = Object.keys(builder.operations).length > 0;
|
|
1908
|
-
const needZodSchemasInline =
|
|
2088
|
+
const needZodSchemasInline = shouldGenerateZodSchemasInline(output, hasOperations);
|
|
2089
|
+
const includeZodImport = !Object.values(builder.operations).some((operation) => /\bzod\b/.test(operation.implementation));
|
|
2090
|
+
const inlineSchemasParamsMutator = needZodSchemasInline && output.override.zod.params ? await generateMutator({
|
|
2091
|
+
output: output.target,
|
|
2092
|
+
mutator: output.override.zod.params,
|
|
2093
|
+
name: "zodParams",
|
|
2094
|
+
workspace,
|
|
2095
|
+
tsconfig: output.tsconfig
|
|
2096
|
+
}) : void 0;
|
|
2097
|
+
const isSchemasInSeparateFile = output.mode !== OutputMode.SINGLE;
|
|
2098
|
+
const includeParamsImport = !hasOperations || isSchemasInSeparateFile;
|
|
1909
2099
|
implementationPaths = await writeMode({
|
|
1910
2100
|
builder,
|
|
1911
2101
|
workspace,
|
|
1912
2102
|
output,
|
|
1913
2103
|
projectName,
|
|
1914
2104
|
header,
|
|
1915
|
-
needSchema:
|
|
1916
|
-
generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output) : void 0
|
|
2105
|
+
needSchema: shouldGenerateSchemas(output, hasOperations),
|
|
2106
|
+
generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output, includeZodImport, inlineSchemasParamsMutator, includeParamsImport) : void 0
|
|
1917
2107
|
});
|
|
1918
2108
|
}
|
|
1919
2109
|
if (output.workspace) {
|
|
@@ -2072,4 +2262,4 @@ async function loadConfigFile(configFilePath) {
|
|
|
2072
2262
|
//#endregion
|
|
2073
2263
|
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 };
|
|
2074
2264
|
|
|
2075
|
-
//# sourceMappingURL=config-
|
|
2265
|
+
//# sourceMappingURL=config-yIkhToq8.mjs.map
|