orval 8.13.0 → 8.15.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-CVTlXJY3.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, buildDynamicScope, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateMutator, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getImportExtension, getMockFileExtensionByTypeName, getRefInfo, getRoute, isBoolean, isComponentRef, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, resolveValue, splitSchemasByType, upath, writeGeneratedFile, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode } from "@orval/core";
|
|
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.15.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) },
|
|
@@ -1218,6 +1247,7 @@ function normalizeHooks(hooks) {
|
|
|
1218
1247
|
function normalizeHonoOptions(hono = {}, workspace) {
|
|
1219
1248
|
return {
|
|
1220
1249
|
...hono.handlers ? { handlers: path.resolve(workspace, hono.handlers) } : {},
|
|
1250
|
+
handlerGenerationStrategy: hono.handlerGenerationStrategy ?? "smart",
|
|
1221
1251
|
compositeRoute: hono.compositeRoute ? path.resolve(workspace, hono.compositeRoute) : "",
|
|
1222
1252
|
validator: hono.validator ?? true,
|
|
1223
1253
|
validatorOutputPath: hono.validatorOutputPath ? path.resolve(workspace, hono.validatorOutputPath) : ""
|
|
@@ -1258,6 +1288,10 @@ function normalizeQueryOptions(queryOptions = {}, outputWorkspace, globalOptions
|
|
|
1258
1288
|
...queryOptions.mutationOptions ? { mutationOptions: normalizeMutator(outputWorkspace, queryOptions.mutationOptions) } : {},
|
|
1259
1289
|
...isNullish(globalOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: globalOptions.shouldExportQueryKey },
|
|
1260
1290
|
...isNullish(queryOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: queryOptions.shouldExportQueryKey },
|
|
1291
|
+
...isNullish(globalOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: globalOptions.shouldFilterQueryKey },
|
|
1292
|
+
...isNullish(queryOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: queryOptions.shouldFilterQueryKey },
|
|
1293
|
+
...isNullish(globalOptions.queryKeyFilter) ? {} : { queryKeyFilter: globalOptions.queryKeyFilter },
|
|
1294
|
+
...isNullish(queryOptions.queryKeyFilter) ? {} : { queryKeyFilter: queryOptions.queryKeyFilter },
|
|
1261
1295
|
...isNullish(globalOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: globalOptions.shouldExportHttpClient },
|
|
1262
1296
|
...isNullish(queryOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: queryOptions.shouldExportHttpClient },
|
|
1263
1297
|
...isNullish(globalOptions.shouldExportMutatorHooks) ? {} : { shouldExportMutatorHooks: globalOptions.shouldExportMutatorHooks },
|
|
@@ -1325,30 +1359,29 @@ async function startWatcher(watchOptions, watchFn, defaultTarget = ".") {
|
|
|
1325
1359
|
}
|
|
1326
1360
|
//#endregion
|
|
1327
1361
|
//#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
1362
|
/**
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1363
|
+
* Resolve the export identifier for a `#/components/schemas/X` ref. We reuse
|
|
1364
|
+
* `@orval/core`'s `getRefInfo(...).name` (`pascal` + sanitize + component
|
|
1365
|
+
* suffix) so reusable zod schema exports match the operation wrappers and the
|
|
1366
|
+
* TS model types exactly. `namingConvention` deliberately does NOT influence
|
|
1367
|
+
* the identifier — it governs file names only, consistent with the rest of
|
|
1368
|
+
* orval. The same call powers the generator's `namedRef` emission, so the
|
|
1369
|
+
* definition name and every reference stay in sync.
|
|
1335
1370
|
*/
|
|
1336
|
-
const resolveSchemaName = (ref,
|
|
1337
|
-
const JS_IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1371
|
+
const resolveSchemaName = (ref, context) => getRefInfo(ref, context).name;
|
|
1338
1372
|
/**
|
|
1339
|
-
* Resolve names for a set of refs, throwing on conflicts
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1342
|
-
*
|
|
1373
|
+
* Resolve names for a set of refs, throwing on conflicts (two distinct refs
|
|
1374
|
+
* collapsing to the same identifier). The mapping is the single source of
|
|
1375
|
+
* truth for cross-schema references — the generator, the orchestrator's graph,
|
|
1376
|
+
* and the sentinel rewriter all consult it.
|
|
1343
1377
|
*/
|
|
1344
|
-
const resolveSchemaNames = (refs,
|
|
1378
|
+
const resolveSchemaNames = (refs, context) => {
|
|
1345
1379
|
const resolved = /* @__PURE__ */ new Map();
|
|
1346
1380
|
const reverse = /* @__PURE__ */ new Map();
|
|
1347
1381
|
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.`);
|
|
1382
|
+
const name = resolveSchemaName(ref, context);
|
|
1350
1383
|
const previous = reverse.get(name);
|
|
1351
|
-
if (previous !== void 0 && previous !== ref) throw new Error(`[orval/zod] generateReusableSchemas: refs ${previous} and ${ref} both
|
|
1384
|
+
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
1385
|
resolved.set(ref, name);
|
|
1353
1386
|
reverse.set(name, ref);
|
|
1354
1387
|
}
|
|
@@ -1366,19 +1399,30 @@ const generateReusableSchemaSet = (refs, context, options) => {
|
|
|
1366
1399
|
const nameToRef = /* @__PURE__ */ new Map();
|
|
1367
1400
|
for (const schemaName of Object.keys(componentSchemas)) {
|
|
1368
1401
|
const ref = `#/components/schemas/${schemaName}`;
|
|
1369
|
-
nameToRef.set(resolveSchemaName(ref, context
|
|
1402
|
+
nameToRef.set(resolveSchemaName(ref, context), ref);
|
|
1370
1403
|
}
|
|
1371
1404
|
const queue = [...refs];
|
|
1372
1405
|
const seen = new Set(refs);
|
|
1373
1406
|
const entries = [];
|
|
1374
1407
|
for (const ref of queue) {
|
|
1375
|
-
const
|
|
1408
|
+
const schemaName = ref.slice(21);
|
|
1409
|
+
const schema = componentSchemas[schemaName];
|
|
1376
1410
|
if (!schema) continue;
|
|
1377
|
-
const name = resolveSchemaName(ref, context
|
|
1378
|
-
const
|
|
1411
|
+
const name = resolveSchemaName(ref, context);
|
|
1412
|
+
const scopedContext = {
|
|
1413
|
+
...context,
|
|
1414
|
+
dynamicScope: buildDynamicScope(schemaName, schema, context)
|
|
1415
|
+
};
|
|
1416
|
+
const parsed = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(schema, scopedContext, name, options.strict, options.isZodV4, {
|
|
1379
1417
|
required: true,
|
|
1380
|
-
useReusableSchemas: true
|
|
1381
|
-
|
|
1418
|
+
useReusableSchemas: true,
|
|
1419
|
+
emitMeta: options.generateMeta
|
|
1420
|
+
}), scopedContext, options.coerce ?? false, options.strict, options.isZodV4, void 0, options.paramsMutator ? {
|
|
1421
|
+
mutator: options.paramsMutator,
|
|
1422
|
+
operationId: "",
|
|
1423
|
+
location: "schema",
|
|
1424
|
+
schemaName: name
|
|
1425
|
+
} : void 0);
|
|
1382
1426
|
entries.push({
|
|
1383
1427
|
ref,
|
|
1384
1428
|
name,
|
|
@@ -1445,11 +1489,27 @@ const tarjan = (graph) => {
|
|
|
1445
1489
|
};
|
|
1446
1490
|
const SENTINEL_PATTERN = /__REF_([A-Za-z_$][A-Za-z0-9_$]*)__/g;
|
|
1447
1491
|
/**
|
|
1492
|
+
* Replace every `__REF_<name>__` sentinel with the bare identifier. Use this
|
|
1493
|
+
* for schemas that sit at the top of the dependency graph (operation params,
|
|
1494
|
+
* bodies, responses) — they can never participate in a cycle with the
|
|
1495
|
+
* component schemas they reference, so every ref is a direct (non-lazy) one.
|
|
1496
|
+
*/
|
|
1497
|
+
const rewriteSentinelsToDirect = (zod) => zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => refName);
|
|
1498
|
+
/**
|
|
1448
1499
|
* Replace every `__REF_<name>__` sentinel with either the bare identifier or
|
|
1449
1500
|
* `zod.lazy(() => <name>)` based on whether the edge closes a cycle, then
|
|
1450
1501
|
* reorder entries so that every non-lazy reference is emitted AFTER its
|
|
1451
1502
|
* target. This avoids TDZ errors at module load.
|
|
1452
1503
|
*
|
|
1504
|
+
* Entries that sit in a cycle (SCC of size > 1, or a self-loop) are flagged
|
|
1505
|
+
* `isRecursive`. Their generated `const` reads its own binding inside the
|
|
1506
|
+
* initializer (through the `zod.lazy` wrapper), which TypeScript rejects with
|
|
1507
|
+
* TS7022 ("'X' implicitly has type 'any' ... referenced directly or indirectly
|
|
1508
|
+
* in its own initializer") unless the `const` carries an explicit type
|
|
1509
|
+
* annotation. The writer (`write-zod-specs`) supplies that annotation —
|
|
1510
|
+
* `const X: zod.ZodType<X>` — backed by a generated TS type, which both
|
|
1511
|
+
* silences TS7022 and preserves full `z.infer` typing through the recursion.
|
|
1512
|
+
*
|
|
1453
1513
|
* Both the lazy classification and the emit order come from a single Tarjan
|
|
1454
1514
|
* run, guaranteeing they agree: a non-lazy edge u→v means v is visited (and
|
|
1455
1515
|
* popped) before u in DFS, so v appears earlier in the SCC array → emitted
|
|
@@ -1459,13 +1519,17 @@ const rewriteReusableSchemas = (entries) => {
|
|
|
1459
1519
|
const graph = new Map(entries.map((e) => [e.name, new Set(e.usedRefs)]));
|
|
1460
1520
|
for (const e of entries) for (const ref of e.usedRefs) if (!graph.has(ref)) graph.set(ref, /* @__PURE__ */ new Set());
|
|
1461
1521
|
const { sccs, lazyEdges } = tarjan(graph);
|
|
1522
|
+
const recursiveNames = /* @__PURE__ */ new Set();
|
|
1523
|
+
for (const scc of sccs) if (scc.length > 1) for (const name of scc) recursiveNames.add(name);
|
|
1524
|
+
else if (lazyEdges.has(edgeKey(scc[0], scc[0]))) recursiveNames.add(scc[0]);
|
|
1462
1525
|
const rewritten = new Map(entries.map((entry) => {
|
|
1463
1526
|
const newZod = entry.zod.replaceAll(SENTINEL_PATTERN, (_match, refName) => {
|
|
1464
1527
|
return lazyEdges.has(edgeKey(entry.name, refName)) ? `zod.lazy(() => ${refName})` : refName;
|
|
1465
1528
|
});
|
|
1466
1529
|
return [entry.name, {
|
|
1467
1530
|
...entry,
|
|
1468
|
-
zod: newZod
|
|
1531
|
+
zod: newZod,
|
|
1532
|
+
isRecursive: recursiveNames.has(entry.name)
|
|
1469
1533
|
}];
|
|
1470
1534
|
}));
|
|
1471
1535
|
const out = [];
|
|
@@ -1477,18 +1541,99 @@ const rewriteReusableSchemas = (entries) => {
|
|
|
1477
1541
|
};
|
|
1478
1542
|
//#endregion
|
|
1479
1543
|
//#region src/write-zod-specs.ts
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1544
|
+
/**
|
|
1545
|
+
* Render the `import { ... } from '...'` line for a resolved
|
|
1546
|
+
* `GeneratorMutator`. Mirrors the format produced by
|
|
1547
|
+
* `generateMutatorImports` in `@orval/core` but inlined to avoid pulling in
|
|
1548
|
+
* its full surface area for a single statement.
|
|
1549
|
+
*/
|
|
1550
|
+
function buildMutatorImportStatement(mutator) {
|
|
1551
|
+
return `import ${mutator.default ? mutator.name : `{ ${mutator.name} }`} from '${mutator.path}';`;
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Whole-word substring check for a resolved mutator alias inside generated
|
|
1555
|
+
* code. Plain `String.includes` would false-positive when the user names the
|
|
1556
|
+
* mutator something like `min` against `.min(1)`.
|
|
1557
|
+
*/
|
|
1558
|
+
function bodyReferencesMutator(body, mutator) {
|
|
1559
|
+
return new RegExp(String.raw`\b${mutator.name}\b`).test(body);
|
|
1560
|
+
}
|
|
1561
|
+
function generateZodSchemaFileContent(header, schemas, includeZodImport = true) {
|
|
1562
|
+
const refImports = [...new Set(schemas.flatMap((s) => s.importStatements ?? []))].toSorted();
|
|
1563
|
+
const importBlock = [...includeZodImport ? [`import { z as zod } from 'zod';`] : [], ...refImports].join("\n");
|
|
1564
|
+
const schemaContent = schemas.map(({ schemaName, consts, zodExpression }) => {
|
|
1484
1565
|
return `${consts ? `${consts}\n` : ""}export const ${schemaName} = ${zodExpression}
|
|
1485
1566
|
|
|
1486
1567
|
export type ${schemaName} = zod.input<typeof ${schemaName}>;
|
|
1487
1568
|
export type ${schemaName}Output = zod.output<typeof ${schemaName}>;`;
|
|
1488
|
-
}).join("\n\n")
|
|
1489
|
-
`;
|
|
1569
|
+
}).join("\n\n");
|
|
1570
|
+
return `${header}${importBlock ? `${importBlock}\n\n` : ""}${schemaContent}\n`;
|
|
1571
|
+
}
|
|
1572
|
+
function renderReusableSchemaEntry(entry, context) {
|
|
1573
|
+
const consts = entry.consts ? `${entry.consts}\n\n` : "";
|
|
1574
|
+
if (entry.isRecursive) {
|
|
1575
|
+
const rawName = isComponentRef(entry.ref) ? getRefInfo(entry.ref, context).originalName : void 0;
|
|
1576
|
+
const schema = rawName ? context.spec.components?.schemas?.[rawName] : void 0;
|
|
1577
|
+
const resolved = schema ? resolveValue({
|
|
1578
|
+
schema,
|
|
1579
|
+
name: entry.name,
|
|
1580
|
+
context
|
|
1581
|
+
}) : void 0;
|
|
1582
|
+
const typeBody = resolved ? resolved.value : "unknown";
|
|
1583
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1584
|
+
const extraImports = [];
|
|
1585
|
+
for (const imp of resolved?.imports ?? []) {
|
|
1586
|
+
if (!imp.name || imp.name === entry.name) continue;
|
|
1587
|
+
const bindingKey = imp.alias ?? imp.name;
|
|
1588
|
+
if (seen.has(bindingKey)) continue;
|
|
1589
|
+
seen.add(bindingKey);
|
|
1590
|
+
extraImports.push({
|
|
1591
|
+
name: imp.name,
|
|
1592
|
+
...imp.alias ? { alias: imp.alias } : {}
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
return {
|
|
1596
|
+
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}>;`,
|
|
1597
|
+
extraImports
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
return {
|
|
1601
|
+
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}>;`,
|
|
1602
|
+
extraImports: []
|
|
1603
|
+
};
|
|
1490
1604
|
}
|
|
1491
1605
|
const isValidSchemaIdentifier = (name) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
1606
|
+
/**
|
|
1607
|
+
* Build the sibling-file `import { … } from './…'` block for one reusable
|
|
1608
|
+
* schema file. Two sources feed in:
|
|
1609
|
+
* - `usedRefs` — names from the zod runtime expression. Sourced from the
|
|
1610
|
+
* sentinel parser, so always unaliased.
|
|
1611
|
+
* - `extraImports` — names the recursive TS body needs that the zod runtime
|
|
1612
|
+
* collapsed (`propertyNames` $refs, etc.). May carry `alias`.
|
|
1613
|
+
* Keyed by export name (`name`) so an aliased `extraImports` entry overrides
|
|
1614
|
+
* a bare `usedRefs` entry — the recursive TS body uses the local binding, so
|
|
1615
|
+
* the aliased form has to win for the file to compile. Self-refs and
|
|
1616
|
+
* non-component identifiers are filtered out.
|
|
1617
|
+
*
|
|
1618
|
+
* Exported for unit-test coverage of the alias-propagation path; no
|
|
1619
|
+
* `resolveValue` producer surfaces aliases here today, so the integration
|
|
1620
|
+
* tests can't exercise it.
|
|
1621
|
+
*/
|
|
1622
|
+
function buildSiblingImports({ usedRefs, extraImports, entryName, componentNames, namingConvention, importExt }) {
|
|
1623
|
+
const importsByName = /* @__PURE__ */ new Map();
|
|
1624
|
+
for (const name of usedRefs) {
|
|
1625
|
+
if (name === entryName) continue;
|
|
1626
|
+
importsByName.set(name, { name });
|
|
1627
|
+
}
|
|
1628
|
+
for (const imp of extraImports) {
|
|
1629
|
+
if (imp.name === entryName || !componentNames.has(imp.name)) continue;
|
|
1630
|
+
importsByName.set(imp.name, imp);
|
|
1631
|
+
}
|
|
1632
|
+
return [...importsByName.values()].toSorted((a, b) => a.name.localeCompare(b.name)).map(({ name, alias }) => {
|
|
1633
|
+
const importedFile = conventionName(name, namingConvention);
|
|
1634
|
+
return `import { ${alias ? `${name} as ${alias}` : name} } from './${importedFile}${importExt}';`;
|
|
1635
|
+
}).join("\n");
|
|
1636
|
+
}
|
|
1492
1637
|
const isPrimitiveSchemaName = (name) => [
|
|
1493
1638
|
"string",
|
|
1494
1639
|
"number",
|
|
@@ -1529,8 +1674,8 @@ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNam
|
|
|
1529
1674
|
const uniqueExports = [...new Set(allExports.split("\n"))].filter((line) => line.trim()).toSorted().join("\n");
|
|
1530
1675
|
await fs.outputFile(indexPath, `${header}\n${uniqueExports}\n`);
|
|
1531
1676
|
}
|
|
1532
|
-
function generateZodSchemasInline(builder, output) {
|
|
1533
|
-
if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output);
|
|
1677
|
+
function generateZodSchemasInline(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
|
|
1678
|
+
if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output, includeZodImport, paramsMutator, includeParamsImport);
|
|
1534
1679
|
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
1535
1680
|
if (schemasWithOpenApiDef.length === 0) return "";
|
|
1536
1681
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
@@ -1545,7 +1690,10 @@ function generateZodSchemasInline(builder, output) {
|
|
|
1545
1690
|
workspace: "",
|
|
1546
1691
|
output
|
|
1547
1692
|
};
|
|
1548
|
-
const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
|
|
1693
|
+
const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
|
|
1694
|
+
required: true,
|
|
1695
|
+
emitMeta: output.override.zod.generateMeta
|
|
1696
|
+
}), context, coerce, strict, isZodV4);
|
|
1549
1697
|
schemas.push({
|
|
1550
1698
|
schemaName: name,
|
|
1551
1699
|
consts: parsedZodDefinition.consts,
|
|
@@ -1553,11 +1701,9 @@ function generateZodSchemasInline(builder, output) {
|
|
|
1553
1701
|
});
|
|
1554
1702
|
}
|
|
1555
1703
|
if (schemas.length === 0) return "";
|
|
1556
|
-
return generateZodSchemaFileContent("", schemas);
|
|
1704
|
+
return generateZodSchemaFileContent("", schemas, includeZodImport);
|
|
1557
1705
|
}
|
|
1558
|
-
function generateZodSchemasInlineReusable(builder, output) {
|
|
1559
|
-
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
1560
|
-
if (schemasWithOpenApiDef.length === 0) return "";
|
|
1706
|
+
function generateZodSchemasInlineReusable(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
|
|
1561
1707
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
1562
1708
|
const strict = output.override.zod.strict.body;
|
|
1563
1709
|
const coerce = output.override.zod.coerce.body;
|
|
@@ -1567,19 +1713,24 @@ function generateZodSchemasInlineReusable(builder, output) {
|
|
|
1567
1713
|
workspace: "",
|
|
1568
1714
|
output
|
|
1569
1715
|
};
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
|
|
1716
|
+
const componentSchemas = builder.spec.components?.schemas ?? {};
|
|
1717
|
+
const refs = Object.keys(componentSchemas).map((schemaName) => `#/components/schemas/${schemaName}`);
|
|
1718
|
+
if (refs.length === 0) return "";
|
|
1719
|
+
resolveSchemaNames(refs, context);
|
|
1720
|
+
const body = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
|
|
1573
1721
|
strict,
|
|
1574
1722
|
isZodV4,
|
|
1575
|
-
coerce
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
}).join("\n\n")
|
|
1723
|
+
coerce,
|
|
1724
|
+
generateMeta: output.override.zod.generateMeta,
|
|
1725
|
+
paramsMutator
|
|
1726
|
+
})).map((entry) => renderReusableSchemaEntry(entry, context).content).join("\n\n");
|
|
1727
|
+
const zodImport = includeZodImport ? `import { z as zod } from 'zod';\n` : "";
|
|
1728
|
+
const paramsImport = paramsMutator && includeParamsImport && bodyReferencesMutator(body, paramsMutator) ? `${buildMutatorImportStatement(paramsMutator)}\n` : "";
|
|
1729
|
+
return `${zodImport || paramsImport ? `${zodImport}${paramsImport}\n` : ""}${body}\n`;
|
|
1579
1730
|
}
|
|
1580
|
-
async function writeZodSchemas(builder, schemasPath, fileExtension, header, output) {
|
|
1731
|
+
async function writeZodSchemas(builder, schemasPath, fileExtension, header, output, paramsMutator) {
|
|
1581
1732
|
if (output.override.zod.generateReusableSchemas) {
|
|
1582
|
-
await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output);
|
|
1733
|
+
await writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator);
|
|
1583
1734
|
return;
|
|
1584
1735
|
}
|
|
1585
1736
|
const schemasWithOpenApiDef = builder.schemas.filter((s) => s.schema);
|
|
@@ -1598,7 +1749,10 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
|
|
|
1598
1749
|
workspace: "",
|
|
1599
1750
|
output
|
|
1600
1751
|
};
|
|
1601
|
-
const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
|
|
1752
|
+
const parsedZodDefinition = parseZodValidationSchemaDefinition(generateZodValidationSchemaDefinition(dereference(schemaObject, context), context, name, strict, isZodV4, {
|
|
1753
|
+
required: true,
|
|
1754
|
+
emitMeta: output.override.zod.generateMeta
|
|
1755
|
+
}), context, coerce, strict, isZodV4);
|
|
1602
1756
|
schemasToWrite.push({
|
|
1603
1757
|
schemaName: name,
|
|
1604
1758
|
filePath,
|
|
@@ -1613,7 +1767,7 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
|
|
|
1613
1767
|
}
|
|
1614
1768
|
if (output.indexFiles) await writeZodSchemaIndex(schemasPath, fileExtension, header, groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName), output.namingConvention, false);
|
|
1615
1769
|
}
|
|
1616
|
-
async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output) {
|
|
1770
|
+
async function writeZodSchemasReusable(builder, schemasPath, fileExtension, header, output, paramsMutator) {
|
|
1617
1771
|
const isZodV4 = !!output.packageJson && isZodVersionV4(output.packageJson);
|
|
1618
1772
|
const strict = output.override.zod.strict.body;
|
|
1619
1773
|
const coerce = output.override.zod.coerce.body;
|
|
@@ -1623,25 +1777,33 @@ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, head
|
|
|
1623
1777
|
workspace: "",
|
|
1624
1778
|
output
|
|
1625
1779
|
};
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
});
|
|
1630
|
-
resolveSchemaNames(refs, output.namingConvention);
|
|
1780
|
+
const componentSchemas = builder.spec.components?.schemas ?? {};
|
|
1781
|
+
const refs = Object.keys(componentSchemas).map((schemaName) => `#/components/schemas/${schemaName}`);
|
|
1782
|
+
resolveSchemaNames(refs, context);
|
|
1631
1783
|
const rewritten = rewriteReusableSchemas(generateReusableSchemaSet(refs, context, {
|
|
1632
1784
|
strict,
|
|
1633
1785
|
isZodV4,
|
|
1634
|
-
coerce
|
|
1786
|
+
coerce,
|
|
1787
|
+
generateMeta: output.override.zod.generateMeta,
|
|
1788
|
+
paramsMutator
|
|
1635
1789
|
}));
|
|
1790
|
+
const componentNames = new Set(Object.keys(builder.spec.components?.schemas ?? {}).map((schemaName) => resolveSchemaName(`#/components/schemas/${schemaName}`, context)));
|
|
1791
|
+
const paramsMutatorImport = paramsMutator ? buildMutatorImportStatement(paramsMutator) : void 0;
|
|
1636
1792
|
for (const entry of rewritten) {
|
|
1637
1793
|
const fileName = conventionName(entry.name, output.namingConvention);
|
|
1638
1794
|
const filePath = path.join(schemasPath, `${fileName}${fileExtension}`);
|
|
1639
1795
|
const importExt = fileExtension.replace(/\.ts$/, "");
|
|
1640
|
-
const
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1796
|
+
const rendered = renderReusableSchemaEntry(entry, context);
|
|
1797
|
+
const refImports = buildSiblingImports({
|
|
1798
|
+
usedRefs: entry.usedRefs,
|
|
1799
|
+
extraImports: rendered.extraImports,
|
|
1800
|
+
entryName: entry.name,
|
|
1801
|
+
componentNames,
|
|
1802
|
+
namingConvention: output.namingConvention,
|
|
1803
|
+
importExt
|
|
1804
|
+
});
|
|
1805
|
+
const imports = [...!!paramsMutator && bodyReferencesMutator(entry.zod, paramsMutator) && paramsMutatorImport ? [paramsMutatorImport] : [], ...refImports ? [refImports] : []].join("\n");
|
|
1806
|
+
const fileContent = `${header}import { z as zod } from 'zod';\n` + (imports ? `${imports}\n\n` : "\n") + `${rendered.content}\n`;
|
|
1645
1807
|
await fs.outputFile(filePath, fileContent);
|
|
1646
1808
|
}
|
|
1647
1809
|
if (output.indexFiles && rewritten.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, rewritten.map((e) => e.name), output.namingConvention, true);
|
|
@@ -1713,11 +1875,21 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
1713
1875
|
required: true,
|
|
1714
1876
|
useReusableSchemas
|
|
1715
1877
|
}), zodContext, coerce, strict, isZodV4);
|
|
1878
|
+
let zodExpression = parsedZodDefinition.zod;
|
|
1879
|
+
let importStatements;
|
|
1880
|
+
if (useReusableSchemas && parsedZodDefinition.usedRefs.size > 0) {
|
|
1881
|
+
zodExpression = rewriteSentinelsToDirect(zodExpression);
|
|
1882
|
+
const importExt = fileExtension.replace(/\.ts$/, "");
|
|
1883
|
+
importStatements = [...parsedZodDefinition.usedRefs].filter((refName) => refName !== name).toSorted().map((refName) => {
|
|
1884
|
+
return `import { ${refName} } from './${conventionName(refName, output.namingConvention)}${importExt}';`;
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1716
1887
|
schemasToWrite.push({
|
|
1717
1888
|
schemaName: name,
|
|
1718
1889
|
filePath,
|
|
1719
1890
|
consts: parsedZodDefinition.consts,
|
|
1720
|
-
zodExpression
|
|
1891
|
+
zodExpression,
|
|
1892
|
+
importStatements
|
|
1721
1893
|
});
|
|
1722
1894
|
}
|
|
1723
1895
|
const groupedSchemasToWrite = groupSchemasByFilePath(schemasToWrite);
|
|
@@ -1837,6 +2009,17 @@ async function writeFakerSchemaMocks(builder, options, header) {
|
|
|
1837
2009
|
await writeGeneratedFile(filePath, content);
|
|
1838
2010
|
return filePath;
|
|
1839
2011
|
}
|
|
2012
|
+
function isSchemaValidatorClient(client) {
|
|
2013
|
+
return client === "zod" || client === "effect";
|
|
2014
|
+
}
|
|
2015
|
+
function shouldGenerateZodSchemasInline(output, hasOperations) {
|
|
2016
|
+
if (output.client !== "zod" || output.schemas) return false;
|
|
2017
|
+
if (output.override.zod.generateReusableSchemas) return true;
|
|
2018
|
+
return !hasOperations;
|
|
2019
|
+
}
|
|
2020
|
+
function shouldGenerateSchemas(output, hasOperations) {
|
|
2021
|
+
return !output.schemas && !isSchemaValidatorClient(output.client) || shouldGenerateZodSchemasInline(output, hasOperations);
|
|
2022
|
+
}
|
|
1840
2023
|
async function writeSpecs(builder, workspace, options, projectName) {
|
|
1841
2024
|
const { info, schemas, target } = builder;
|
|
1842
2025
|
const { output } = options;
|
|
@@ -1846,7 +2029,13 @@ async function writeSpecs(builder, workspace, options, projectName) {
|
|
|
1846
2029
|
const schemasPath = isString(output.schemas) ? output.schemas : output.schemas.path;
|
|
1847
2030
|
if (!isString(output.schemas) && output.schemas.type === "zod" || isString(output.schemas) && output.client === "zod" && output.override.zod.generateReusableSchemas) {
|
|
1848
2031
|
const fileExtension = output.schemaFileExtension;
|
|
1849
|
-
await writeZodSchemas(builder, schemasPath, fileExtension, header, output
|
|
2032
|
+
await writeZodSchemas(builder, schemasPath, fileExtension, header, output, output.override.zod.params ? await generateMutator({
|
|
2033
|
+
output: path.join(schemasPath, `__params__${fileExtension}`),
|
|
2034
|
+
mutator: output.override.zod.params,
|
|
2035
|
+
name: "zodParams",
|
|
2036
|
+
workspace,
|
|
2037
|
+
tsconfig: output.tsconfig
|
|
2038
|
+
}) : void 0);
|
|
1850
2039
|
await writeZodSchemasFromVerbs(builder.verbOptions, schemasPath, fileExtension, header, output, {
|
|
1851
2040
|
spec: builder.spec,
|
|
1852
2041
|
target: builder.target,
|
|
@@ -1903,17 +2092,26 @@ async function writeSpecs(builder, workspace, options, projectName) {
|
|
|
1903
2092
|
let implementationPaths = [];
|
|
1904
2093
|
if (output.target) {
|
|
1905
2094
|
const writeMode = getWriteMode(output.mode);
|
|
1906
|
-
const isZodClient = output.client === "zod";
|
|
1907
2095
|
const hasOperations = Object.keys(builder.operations).length > 0;
|
|
1908
|
-
const needZodSchemasInline =
|
|
2096
|
+
const needZodSchemasInline = shouldGenerateZodSchemasInline(output, hasOperations);
|
|
2097
|
+
const includeZodImport = !Object.values(builder.operations).some((operation) => /\bzod\b/.test(operation.implementation));
|
|
2098
|
+
const inlineSchemasParamsMutator = needZodSchemasInline && output.override.zod.params ? await generateMutator({
|
|
2099
|
+
output: output.target,
|
|
2100
|
+
mutator: output.override.zod.params,
|
|
2101
|
+
name: "zodParams",
|
|
2102
|
+
workspace,
|
|
2103
|
+
tsconfig: output.tsconfig
|
|
2104
|
+
}) : void 0;
|
|
2105
|
+
const isSchemasInSeparateFile = output.mode !== OutputMode.SINGLE;
|
|
2106
|
+
const includeParamsImport = !hasOperations || isSchemasInSeparateFile;
|
|
1909
2107
|
implementationPaths = await writeMode({
|
|
1910
2108
|
builder,
|
|
1911
2109
|
workspace,
|
|
1912
2110
|
output,
|
|
1913
2111
|
projectName,
|
|
1914
2112
|
header,
|
|
1915
|
-
needSchema:
|
|
1916
|
-
generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output) : void 0
|
|
2113
|
+
needSchema: shouldGenerateSchemas(output, hasOperations),
|
|
2114
|
+
generateSchemasInline: needZodSchemasInline ? () => generateZodSchemasInline(builder, output, includeZodImport, inlineSchemasParamsMutator, includeParamsImport) : void 0
|
|
1917
2115
|
});
|
|
1918
2116
|
}
|
|
1919
2117
|
if (output.workspace) {
|
|
@@ -2072,4 +2270,4 @@ async function loadConfigFile(configFilePath) {
|
|
|
2072
2270
|
//#endregion
|
|
2073
2271
|
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
2272
|
|
|
2075
|
-
//# sourceMappingURL=config-
|
|
2273
|
+
//# sourceMappingURL=config-CVTlXJY3.mjs.map
|