@orval/core 8.12.3 → 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/index.d.mts +212 -3
- package/dist/index.mjs +938 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -48,6 +48,7 @@ const OutputClient = {
|
|
|
48
48
|
VUE_QUERY: "vue-query",
|
|
49
49
|
SWR: "swr",
|
|
50
50
|
ZOD: "zod",
|
|
51
|
+
EFFECT: "effect",
|
|
51
52
|
HONO: "hono",
|
|
52
53
|
FETCH: "fetch",
|
|
53
54
|
MCP: "mcp"
|
|
@@ -143,6 +144,16 @@ const TEMPLATE_TAG_REGEX = /\${(.+?)}/g;
|
|
|
143
144
|
function isReference(obj) {
|
|
144
145
|
return !isNullish$1(obj) && Object.hasOwn(obj, "$ref");
|
|
145
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Discriminator helper for {@link OpenApiDynamicReferenceObject}.
|
|
149
|
+
*
|
|
150
|
+
* Returns `true` when `obj` has a `$dynamicRef` string property,
|
|
151
|
+
* indicating it is an OpenAPI 3.1 dynamic reference rather than a
|
|
152
|
+
* static `$ref`.
|
|
153
|
+
*/
|
|
154
|
+
function isDynamicReference(obj) {
|
|
155
|
+
return !isNullish$1(obj) && Object.hasOwn(obj, "$dynamicRef") && typeof obj.$dynamicRef === "string";
|
|
156
|
+
}
|
|
146
157
|
function isDirectory(pathValue) {
|
|
147
158
|
return !nodePath.extname(pathValue);
|
|
148
159
|
}
|
|
@@ -741,13 +752,21 @@ function count(str = "", key) {
|
|
|
741
752
|
var path_exports = /* @__PURE__ */ __exportAll({
|
|
742
753
|
getRelativeImportPath: () => getRelativeImportPath,
|
|
743
754
|
getSchemaFileName: () => getSchemaFileName,
|
|
755
|
+
isAbsolute: () => isAbsolute,
|
|
744
756
|
join: () => join,
|
|
745
757
|
joinSafe: () => joinSafe,
|
|
746
758
|
normalizeSafe: () => normalizeSafe,
|
|
747
759
|
relativeSafe: () => relativeSafe,
|
|
760
|
+
resolve: () => resolve,
|
|
748
761
|
separator: () => "/",
|
|
749
762
|
toUnix: () => toUnix
|
|
750
763
|
});
|
|
764
|
+
function isAbsolute(value) {
|
|
765
|
+
return nodePath.isAbsolute(value);
|
|
766
|
+
}
|
|
767
|
+
function resolve(...args) {
|
|
768
|
+
return toUnix(nodePath.resolve(...args));
|
|
769
|
+
}
|
|
751
770
|
function toUnix(value) {
|
|
752
771
|
value = value.replaceAll("\\", "/");
|
|
753
772
|
value = value.replaceAll(/(?<!^)\/+/g, "/");
|
|
@@ -1357,6 +1376,16 @@ function getRefInfo($ref, context) {
|
|
|
1357
1376
|
refPaths
|
|
1358
1377
|
};
|
|
1359
1378
|
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Extracts the anchor name from a fragment-only `$dynamicRef` (e.g. `#category` → `category`).
|
|
1381
|
+
*
|
|
1382
|
+
* Returns `undefined` for external-document `$dynamicRef` values (e.g. `other.json#anchor`)
|
|
1383
|
+
* which are not supported.
|
|
1384
|
+
*/
|
|
1385
|
+
function getDynamicAnchorName(dynamicRef) {
|
|
1386
|
+
if (!dynamicRef.startsWith("#") || dynamicRef.length <= 1) return;
|
|
1387
|
+
return dynamicRef.slice(1);
|
|
1388
|
+
}
|
|
1360
1389
|
//#endregion
|
|
1361
1390
|
//#region src/getters/imports.ts
|
|
1362
1391
|
function getAliasedImports({ name, resolvedValue, context }) {
|
|
@@ -1514,7 +1543,9 @@ function combineSchemas({ name, schema, separator, context, nullable, formDataCo
|
|
|
1514
1543
|
const originalProps = resolvedValue.originalSchema.properties;
|
|
1515
1544
|
if (resolvedValue.type === "object" && originalProps) resolvedData.allProperties.push(...Object.keys(originalProps));
|
|
1516
1545
|
}
|
|
1517
|
-
|
|
1546
|
+
const isAllEnums = resolvedData.isEnum.every(Boolean);
|
|
1547
|
+
const isNullableEnumComposition = (separator === "anyOf" || separator === "oneOf") && !isAllEnums && resolvedData.isEnum.some(Boolean) && resolvedData.isEnum.every((isEnum, index) => isEnum && !resolvedData.isRef[index] || resolvedData.types[index] === "null");
|
|
1548
|
+
if (isAllEnums && name && items.length > 1 && context.output.override.enumGenerationType !== EnumGeneration.UNION) {
|
|
1518
1549
|
const { value: combinedEnumValue, valueImports, hasNull } = getCombinedEnumValue(resolvedData.values.map((value, index) => ({
|
|
1519
1550
|
value,
|
|
1520
1551
|
isRef: resolvedData.isRef[index],
|
|
@@ -1575,7 +1606,7 @@ function combineSchemas({ name, schema, separator, context, nullable, formDataCo
|
|
|
1575
1606
|
imports: resolvedValue ? [...resolvedData.imports, ...resolvedValue.imports] : resolvedData.imports,
|
|
1576
1607
|
schemas: resolvedValue ? [...resolvedData.schemas, ...resolvedValue.schemas] : resolvedData.schemas,
|
|
1577
1608
|
dependencies: resolvedValue ? [...resolvedData.dependencies, ...resolvedValue.dependencies] : resolvedData.dependencies,
|
|
1578
|
-
isEnum:
|
|
1609
|
+
isEnum: isNullableEnumComposition,
|
|
1579
1610
|
type: "object",
|
|
1580
1611
|
isRef: false,
|
|
1581
1612
|
hasReadonlyProps: resolvedData.hasReadonlyProps || (resolvedValue?.hasReadonlyProps ?? false),
|
|
@@ -2041,16 +2072,33 @@ function getScalar({ item, name, context, formDataContext }) {
|
|
|
2041
2072
|
examples: resolveExampleRefs(schemaExamples, context)
|
|
2042
2073
|
};
|
|
2043
2074
|
}
|
|
2044
|
-
case "null":
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2075
|
+
case "null": {
|
|
2076
|
+
const itemAllOf = item.allOf;
|
|
2077
|
+
const itemOneOf = item.oneOf;
|
|
2078
|
+
const itemAnyOf = item.anyOf;
|
|
2079
|
+
let separator;
|
|
2080
|
+
if (itemAllOf?.length) separator = "allOf";
|
|
2081
|
+
else if (itemOneOf?.length) separator = "oneOf";
|
|
2082
|
+
else if (itemAnyOf?.length) separator = "anyOf";
|
|
2083
|
+
if (separator) return combineSchemas({
|
|
2084
|
+
schema: Object.fromEntries(Object.entries(item).filter(([key]) => key !== "type")),
|
|
2085
|
+
name,
|
|
2086
|
+
separator,
|
|
2087
|
+
context,
|
|
2088
|
+
nullable: nullable || " | null",
|
|
2089
|
+
formDataContext
|
|
2090
|
+
});
|
|
2091
|
+
return {
|
|
2092
|
+
value: "null",
|
|
2093
|
+
isEnum: false,
|
|
2094
|
+
type: "null",
|
|
2095
|
+
imports: [],
|
|
2096
|
+
schemas: [],
|
|
2097
|
+
isRef: false,
|
|
2098
|
+
hasReadonlyProps: schemaReadOnly ?? false,
|
|
2099
|
+
dependencies: []
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2054
2102
|
default: {
|
|
2055
2103
|
if (isArray(itemType)) return combineSchemas({
|
|
2056
2104
|
schema: { anyOf: itemType.map((type) => Object.assign({}, item, { type })) },
|
|
@@ -2088,6 +2136,28 @@ function getScalar({ item, name, context, formDataContext }) {
|
|
|
2088
2136
|
}
|
|
2089
2137
|
//#endregion
|
|
2090
2138
|
//#region src/resolvers/ref.ts
|
|
2139
|
+
/** Convert a `$dynamicAnchor` name to a valid TypeScript generic parameter identifier. */
|
|
2140
|
+
function dynamicAnchorToParamName(anchor) {
|
|
2141
|
+
return sanitize(anchor, {
|
|
2142
|
+
underscore: "_",
|
|
2143
|
+
whitespace: "_",
|
|
2144
|
+
dash: "_",
|
|
2145
|
+
es5keyword: true,
|
|
2146
|
+
es5IdentifierName: true
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
function dynamicAnchorsToUniqueParamNames(anchors) {
|
|
2150
|
+
const result = /* @__PURE__ */ new Map();
|
|
2151
|
+
const usedNames = /* @__PURE__ */ new Map();
|
|
2152
|
+
for (const anchor of anchors) {
|
|
2153
|
+
const base = dynamicAnchorToParamName(anchor);
|
|
2154
|
+
const count = usedNames.get(base) ?? 0;
|
|
2155
|
+
usedNames.set(base, count + 1);
|
|
2156
|
+
const paramName = count === 0 ? base : `${base}${count + 1}`;
|
|
2157
|
+
result.set(anchor, paramName);
|
|
2158
|
+
}
|
|
2159
|
+
return result;
|
|
2160
|
+
}
|
|
2091
2161
|
const REF_NOT_FOUND_PREFIX = "Oops... 🍻. Ref not found";
|
|
2092
2162
|
/**
|
|
2093
2163
|
* Recursively resolves a `$ref` in an OpenAPI document, following
|
|
@@ -2137,15 +2207,100 @@ function resolveRef(schema, context, imports = []) {
|
|
|
2137
2207
|
schemaName: originalName
|
|
2138
2208
|
}]);
|
|
2139
2209
|
}
|
|
2210
|
+
/** Check whether a schema reference has at least one `$defs` entry with both `$dynamicAnchor` and `$ref`. */
|
|
2211
|
+
function isBoundAlias(schema) {
|
|
2212
|
+
const defs = schema.$defs;
|
|
2213
|
+
if (!defs || typeof defs !== "object") return false;
|
|
2214
|
+
for (const defSchema of Object.values(defs)) {
|
|
2215
|
+
if (!defSchema || typeof defSchema !== "object") continue;
|
|
2216
|
+
const rec = defSchema;
|
|
2217
|
+
if (typeof rec.$dynamicAnchor === "string" && typeof rec.$ref === "string") return true;
|
|
2218
|
+
}
|
|
2219
|
+
return false;
|
|
2220
|
+
}
|
|
2140
2221
|
/**
|
|
2141
|
-
*
|
|
2142
|
-
*
|
|
2143
|
-
* Preserves OpenAPI 3.0 `nullable` and 3.1 type-array (`["object", "null"]`)
|
|
2144
|
-
* hints from the referencing schema onto the resolved target.
|
|
2145
|
-
*
|
|
2146
|
-
* @see https://spec.openapis.org/oas/v3.0.3#fixed-fields-18 (nullable)
|
|
2147
|
-
* @see https://spec.openapis.org/oas/v3.1.0#schema-object (type as array)
|
|
2222
|
+
* Extract bound-alias information from a schema that references a generic template
|
|
2223
|
+
* and binds `$dynamicAnchor` entries to concrete types via `$defs`.
|
|
2148
2224
|
*/
|
|
2225
|
+
function extractBoundAliasInfo(schema, context) {
|
|
2226
|
+
let bindingElement;
|
|
2227
|
+
let extraSchemas;
|
|
2228
|
+
if (isReference(schema) && isBoundAlias(schema)) bindingElement = schema;
|
|
2229
|
+
else {
|
|
2230
|
+
const allOf = schema.allOf;
|
|
2231
|
+
if (Array.isArray(allOf)) for (let i = 0; i < allOf.length; i++) {
|
|
2232
|
+
const element = allOf[i];
|
|
2233
|
+
if (isReference(element) && isBoundAlias(element)) {
|
|
2234
|
+
bindingElement = element;
|
|
2235
|
+
extraSchemas = allOf.filter((_, j) => j !== i);
|
|
2236
|
+
break;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
if (!bindingElement) return void 0;
|
|
2241
|
+
const defs = bindingElement.$defs;
|
|
2242
|
+
if (!defs || typeof defs !== "object") return void 0;
|
|
2243
|
+
const bindingByAnchor = /* @__PURE__ */ new Map();
|
|
2244
|
+
for (const defSchema of Object.values(defs)) {
|
|
2245
|
+
if (!defSchema || typeof defSchema !== "object") continue;
|
|
2246
|
+
const rec = defSchema;
|
|
2247
|
+
if (rec.$dynamicAnchor === void 0) continue;
|
|
2248
|
+
const ref = rec.$ref;
|
|
2249
|
+
if (!ref || !isComponentRef(ref)) continue;
|
|
2250
|
+
const anchor = rec.$dynamicAnchor;
|
|
2251
|
+
const { name, originalName } = getRefInfo(ref, context);
|
|
2252
|
+
bindingByAnchor.set(anchor, {
|
|
2253
|
+
typeName: name,
|
|
2254
|
+
originalName
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
if (bindingByAnchor.size === 0) return void 0;
|
|
2258
|
+
const refPath = bindingElement.$ref;
|
|
2259
|
+
if (typeof refPath !== "string") return void 0;
|
|
2260
|
+
const { name: genericName, refPaths: templateRefPaths } = getRefInfo(refPath, context);
|
|
2261
|
+
const templateDefs = (templateRefPaths ? prop(context.spec, ...templateRefPaths) : void 0)?.$defs;
|
|
2262
|
+
const typeArgs = [];
|
|
2263
|
+
const genericParams = [];
|
|
2264
|
+
const imports = [];
|
|
2265
|
+
if (templateDefs && typeof templateDefs === "object") {
|
|
2266
|
+
const templateAnchors = [];
|
|
2267
|
+
for (const defSchema of Object.values(templateDefs)) {
|
|
2268
|
+
if (!defSchema || typeof defSchema !== "object") continue;
|
|
2269
|
+
const rec = defSchema;
|
|
2270
|
+
if (rec.$dynamicAnchor === void 0 || rec.$ref !== void 0) continue;
|
|
2271
|
+
templateAnchors.push(rec.$dynamicAnchor);
|
|
2272
|
+
}
|
|
2273
|
+
const uniqueNames = dynamicAnchorsToUniqueParamNames(templateAnchors);
|
|
2274
|
+
for (const anchor of templateAnchors) {
|
|
2275
|
+
const binding = bindingByAnchor.get(anchor);
|
|
2276
|
+
if (binding) {
|
|
2277
|
+
typeArgs.push(binding.typeName);
|
|
2278
|
+
imports.push({
|
|
2279
|
+
name: binding.typeName,
|
|
2280
|
+
schemaName: binding.originalName
|
|
2281
|
+
});
|
|
2282
|
+
} else {
|
|
2283
|
+
const paramName = uniqueNames.get(anchor) ?? dynamicAnchorToParamName(anchor);
|
|
2284
|
+
typeArgs.push(paramName);
|
|
2285
|
+
genericParams.push(paramName);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
if (typeArgs.length === 0) for (const { typeName, originalName } of bindingByAnchor.values()) {
|
|
2290
|
+
typeArgs.push(typeName);
|
|
2291
|
+
imports.push({
|
|
2292
|
+
name: typeName,
|
|
2293
|
+
schemaName: originalName
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
2296
|
+
return {
|
|
2297
|
+
genericName,
|
|
2298
|
+
genericParams,
|
|
2299
|
+
typeArgs,
|
|
2300
|
+
imports,
|
|
2301
|
+
extraSchemas
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2149
2304
|
function getSchema$1(schema, context) {
|
|
2150
2305
|
if (!schema.$ref) throw new Error(`${REF_NOT_FOUND_PREFIX}: missing $ref`);
|
|
2151
2306
|
const refInfo = getRefInfo(schema.$ref, context);
|
|
@@ -2172,6 +2327,108 @@ function getSchema$1(schema, context) {
|
|
|
2172
2327
|
refInfo
|
|
2173
2328
|
};
|
|
2174
2329
|
}
|
|
2330
|
+
function encodeJsonPointerSegment(segment) {
|
|
2331
|
+
return segment.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Build the dynamic scope for a schema: maps `$dynamicAnchor` names to concrete
|
|
2335
|
+
* type entries for self-referential resolution, `$defs` bindings, and sibling anchors.
|
|
2336
|
+
*/
|
|
2337
|
+
function buildDynamicScope(schemaName, schema, context) {
|
|
2338
|
+
const scope = {};
|
|
2339
|
+
const getSchemaScopeEntry = (name) => {
|
|
2340
|
+
const refInfo = getRefInfo(`#/components/schemas/${encodeJsonPointerSegment(name)}`, context);
|
|
2341
|
+
return {
|
|
2342
|
+
name: refInfo.name,
|
|
2343
|
+
schemaName: refInfo.originalName
|
|
2344
|
+
};
|
|
2345
|
+
};
|
|
2346
|
+
const schemaRecord = schema;
|
|
2347
|
+
if (typeof schemaRecord.$dynamicAnchor === "string") scope[schemaRecord.$dynamicAnchor] = getSchemaScopeEntry(schemaName);
|
|
2348
|
+
const defs = schemaRecord.$defs;
|
|
2349
|
+
if (defs && typeof defs === "object") {
|
|
2350
|
+
const unboundAnchors = [];
|
|
2351
|
+
for (const [, defSchema] of Object.entries(defs)) {
|
|
2352
|
+
if (!defSchema || typeof defSchema !== "object") continue;
|
|
2353
|
+
const defRecord = defSchema;
|
|
2354
|
+
if (typeof defRecord.$dynamicAnchor === "string") {
|
|
2355
|
+
const anchorName = defRecord.$dynamicAnchor;
|
|
2356
|
+
const refInDef = defSchema.$ref;
|
|
2357
|
+
if (refInDef?.startsWith("#/components/schemas/")) {
|
|
2358
|
+
const { name, originalName } = getRefInfo(refInDef, context);
|
|
2359
|
+
scope[anchorName] = {
|
|
2360
|
+
name,
|
|
2361
|
+
schemaName: originalName
|
|
2362
|
+
};
|
|
2363
|
+
} else if (!refInDef) unboundAnchors.push(anchorName);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
if (unboundAnchors.length > 0) {
|
|
2367
|
+
const uniqueNames = dynamicAnchorsToUniqueParamNames(unboundAnchors);
|
|
2368
|
+
for (const anchor of unboundAnchors) {
|
|
2369
|
+
const paramName = uniqueNames.get(anchor);
|
|
2370
|
+
if (paramName === void 0) continue;
|
|
2371
|
+
scope[anchor] = {
|
|
2372
|
+
name: paramName,
|
|
2373
|
+
schemaName: paramName,
|
|
2374
|
+
isParameter: true
|
|
2375
|
+
};
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
return scope;
|
|
2380
|
+
}
|
|
2381
|
+
/**
|
|
2382
|
+
* Resolve a `$dynamicRef` anchor to its concrete type using the current dynamic scope.
|
|
2383
|
+
* Returns `{ schema: {}, resolvedTypeName: 'unknown' }` when no scope override exists.
|
|
2384
|
+
*/
|
|
2385
|
+
function resolveDynamicRef(anchorName, context, imports = []) {
|
|
2386
|
+
let scopeEntry = (context.dynamicScope ?? {})[anchorName];
|
|
2387
|
+
if (!scopeEntry) {
|
|
2388
|
+
const schemas = context.spec.components?.schemas;
|
|
2389
|
+
if (schemas && typeof schemas === "object") {
|
|
2390
|
+
const matches = [];
|
|
2391
|
+
for (const [schemaName, schemaObj] of Object.entries(schemas)) {
|
|
2392
|
+
if (!schemaObj || typeof schemaObj !== "object") continue;
|
|
2393
|
+
if (schemaObj.$dynamicAnchor === anchorName) matches.push(schemaName);
|
|
2394
|
+
}
|
|
2395
|
+
const match = matches.length === 1 ? matches[0] : matches.find((m) => m === anchorName);
|
|
2396
|
+
if (match) {
|
|
2397
|
+
const refInfo = getRefInfo(`#/components/schemas/${encodeJsonPointerSegment(match)}`, context);
|
|
2398
|
+
scopeEntry = {
|
|
2399
|
+
name: refInfo.name,
|
|
2400
|
+
schemaName: refInfo.originalName
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
if (!scopeEntry) return {
|
|
2406
|
+
schema: {},
|
|
2407
|
+
imports,
|
|
2408
|
+
resolvedTypeName: "unknown"
|
|
2409
|
+
};
|
|
2410
|
+
if (scopeEntry.isParameter) return {
|
|
2411
|
+
schema: {},
|
|
2412
|
+
imports,
|
|
2413
|
+
resolvedTypeName: scopeEntry.name
|
|
2414
|
+
};
|
|
2415
|
+
const resolvedTypeName = scopeEntry.name;
|
|
2416
|
+
const schemaRef = `#/components/schemas/${encodeJsonPointerSegment(scopeEntry.schemaName)}`;
|
|
2417
|
+
try {
|
|
2418
|
+
const { schema: resolvedSchema, imports: resolvedImports } = resolveRef({ $ref: schemaRef }, context, imports);
|
|
2419
|
+
return {
|
|
2420
|
+
schema: resolvedSchema,
|
|
2421
|
+
imports: resolvedImports,
|
|
2422
|
+
resolvedTypeName
|
|
2423
|
+
};
|
|
2424
|
+
} catch {
|
|
2425
|
+
return {
|
|
2426
|
+
schema: {},
|
|
2427
|
+
imports,
|
|
2428
|
+
resolvedTypeName: "unknown"
|
|
2429
|
+
};
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2175
2432
|
/** Recursively resolves `$ref` entries in an examples array or record. */
|
|
2176
2433
|
function resolveExampleRefs(examples, context) {
|
|
2177
2434
|
if (!examples) return;
|
|
@@ -2189,8 +2446,119 @@ function resolveExampleRefs(examples, context) {
|
|
|
2189
2446
|
}
|
|
2190
2447
|
//#endregion
|
|
2191
2448
|
//#region src/resolvers/value.ts
|
|
2449
|
+
const schemaArrayKeys = [
|
|
2450
|
+
"allOf",
|
|
2451
|
+
"anyOf",
|
|
2452
|
+
"oneOf",
|
|
2453
|
+
"prefixItems"
|
|
2454
|
+
];
|
|
2455
|
+
const schemaObjectKeys = [
|
|
2456
|
+
"additionalProperties",
|
|
2457
|
+
"contains",
|
|
2458
|
+
"else",
|
|
2459
|
+
"if",
|
|
2460
|
+
"items",
|
|
2461
|
+
"not",
|
|
2462
|
+
"propertyNames",
|
|
2463
|
+
"then",
|
|
2464
|
+
"unevaluatedItems",
|
|
2465
|
+
"unevaluatedProperties"
|
|
2466
|
+
];
|
|
2467
|
+
const schemaMapKeys = [
|
|
2468
|
+
"$defs",
|
|
2469
|
+
"dependentSchemas",
|
|
2470
|
+
"patternProperties",
|
|
2471
|
+
"properties"
|
|
2472
|
+
];
|
|
2473
|
+
/**
|
|
2474
|
+
* Recursively walks a schema value and returns `true` if any nested
|
|
2475
|
+
* `$dynamicRef` resolves — via the current `context.dynamicScope` — to a
|
|
2476
|
+
* schema *other* than `refName`.
|
|
2477
|
+
*
|
|
2478
|
+
* Used by `resolveValue` to decide whether a `$ref`'d schema must be
|
|
2479
|
+
* instantiated with its bound type arguments rather than referenced by name.
|
|
2480
|
+
*
|
|
2481
|
+
* @param value - The schema node (or sub-node) to inspect.
|
|
2482
|
+
* @param context - Current resolution context, including the dynamic scope.
|
|
2483
|
+
* @param refName - The resolved name of the enclosing `$ref` schema; dynamic
|
|
2484
|
+
* refs that resolve to this same name are considered
|
|
2485
|
+
* self-references and do not count as "scope-affected".
|
|
2486
|
+
* @param seen - Cycle-guard; tracks already-visited objects.
|
|
2487
|
+
*/
|
|
2488
|
+
function hasScopeAffectedDynamicRef(value, context, refName, seen = /* @__PURE__ */ new WeakSet()) {
|
|
2489
|
+
if (!value || typeof value !== "object") return false;
|
|
2490
|
+
if (!context.dynamicScope || Object.keys(context.dynamicScope).length === 0) return false;
|
|
2491
|
+
if (seen.has(value)) return false;
|
|
2492
|
+
seen.add(value);
|
|
2493
|
+
if (isDynamicReference(value) && value.$dynamicRef.startsWith("#")) {
|
|
2494
|
+
const anchorName = getDynamicAnchorName(value.$dynamicRef);
|
|
2495
|
+
if (anchorName) {
|
|
2496
|
+
const scopeEntry = context.dynamicScope[anchorName];
|
|
2497
|
+
if (scopeEntry && scopeEntry.name !== refName) return true;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
const schema = value;
|
|
2501
|
+
for (const key of schemaArrayKeys) {
|
|
2502
|
+
const items = schema[key];
|
|
2503
|
+
if (Array.isArray(items) && items.some((item) => hasScopeAffectedDynamicRef(item, context, refName, seen))) return true;
|
|
2504
|
+
}
|
|
2505
|
+
for (const key of schemaObjectKeys) if (hasScopeAffectedDynamicRef(schema[key], context, refName, seen)) return true;
|
|
2506
|
+
for (const key of schemaMapKeys) {
|
|
2507
|
+
const schemaMap = schema[key];
|
|
2508
|
+
if (schemaMap && typeof schemaMap === "object" && Object.values(schemaMap).some((item) => hasScopeAffectedDynamicRef(item, context, refName, seen))) return true;
|
|
2509
|
+
}
|
|
2510
|
+
return false;
|
|
2511
|
+
}
|
|
2512
|
+
function makeUnknownValue(originalSchema) {
|
|
2513
|
+
return {
|
|
2514
|
+
value: "unknown",
|
|
2515
|
+
imports: [],
|
|
2516
|
+
type: "unknown",
|
|
2517
|
+
isEnum: false,
|
|
2518
|
+
schemas: [],
|
|
2519
|
+
isRef: false,
|
|
2520
|
+
hasReadonlyProps: false,
|
|
2521
|
+
originalSchema,
|
|
2522
|
+
dependencies: []
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
/**
|
|
2526
|
+
* Resolves an OpenAPI schema or reference object to a {@link ResolverValue}
|
|
2527
|
+
* that carries the TypeScript type string, required imports, and metadata.
|
|
2528
|
+
*
|
|
2529
|
+
* Handles all schema forms in priority order:
|
|
2530
|
+
* 1. **Bound generic alias** — a `$ref` with `$defs` overrides; emits an
|
|
2531
|
+
* instantiated generic expression such as `Paginated<User>`.
|
|
2532
|
+
* 2. **Component `$ref`** — a named `$ref` pointing to `#/components/…`;
|
|
2533
|
+
* emits the schema name as a reference import.
|
|
2534
|
+
* 3. **Non-component `$ref`** — an anonymous or path-level ref; inlines the
|
|
2535
|
+
* resolved schema via {@link getScalar} (cycle-safe).
|
|
2536
|
+
* 4. **`$dynamicRef`** — resolved via the active dynamic scope; falls back to
|
|
2537
|
+
* `unknown` when the anchor is absent or the ref is a bare `#`.
|
|
2538
|
+
* 5. **Plain schema** — delegates to {@link getScalar} for all other cases
|
|
2539
|
+
* (primitives, objects, arrays, enums, …).
|
|
2540
|
+
*/
|
|
2192
2541
|
function resolveValue({ schema, name, context, formDataContext }) {
|
|
2193
2542
|
if (isReference(schema)) {
|
|
2543
|
+
const alias = extractBoundAliasInfo(schema, context);
|
|
2544
|
+
if (alias) {
|
|
2545
|
+
const value = `${alias.genericName}<${alias.typeArgs.join(", ")}>`;
|
|
2546
|
+
const allImports = [{
|
|
2547
|
+
name: alias.genericName,
|
|
2548
|
+
schemaName: alias.genericName
|
|
2549
|
+
}, ...alias.imports];
|
|
2550
|
+
return {
|
|
2551
|
+
value,
|
|
2552
|
+
imports: allImports,
|
|
2553
|
+
type: "object",
|
|
2554
|
+
schemas: [],
|
|
2555
|
+
isEnum: false,
|
|
2556
|
+
originalSchema: schema,
|
|
2557
|
+
hasReadonlyProps: false,
|
|
2558
|
+
isRef: true,
|
|
2559
|
+
dependencies: allImports.map((i) => i.name)
|
|
2560
|
+
};
|
|
2561
|
+
}
|
|
2194
2562
|
const refValue = schema.$ref;
|
|
2195
2563
|
const { schema: schemaObject, imports } = resolveRef(schema, context);
|
|
2196
2564
|
if (refValue && !isComponentRef(refValue)) {
|
|
@@ -2232,12 +2600,44 @@ function resolveValue({ schema, name, context, formDataContext }) {
|
|
|
2232
2600
|
const resolvedImport = imports[0];
|
|
2233
2601
|
let hasReadonlyProps = false;
|
|
2234
2602
|
const refName = resolvedImport.name;
|
|
2235
|
-
|
|
2603
|
+
let effectiveContext = context;
|
|
2604
|
+
const refAnchor = schemaObject.$dynamicAnchor;
|
|
2605
|
+
if (typeof refAnchor === "string" && context.dynamicScope?.[refAnchor] && context.dynamicScope[refAnchor].name !== refName && !context.dynamicScope[refAnchor].isParameter) {
|
|
2606
|
+
const scopeEntry = context.dynamicScope[refAnchor];
|
|
2607
|
+
const allOf = ((context.spec.components?.schemas)?.[scopeEntry.schemaName])?.allOf;
|
|
2608
|
+
if (!(Array.isArray(allOf) && allOf.some((el) => {
|
|
2609
|
+
if (!el || typeof el !== "object") return false;
|
|
2610
|
+
const rec = el;
|
|
2611
|
+
if (typeof rec.$ref !== "string" || !isComponentRef(rec.$ref)) return false;
|
|
2612
|
+
const { name } = getRefInfo(rec.$ref, context);
|
|
2613
|
+
return name === refName;
|
|
2614
|
+
}))) {
|
|
2615
|
+
const filteredScope = Object.fromEntries(Object.entries(context.dynamicScope).filter(([key]) => key !== refAnchor));
|
|
2616
|
+
effectiveContext = {
|
|
2617
|
+
...context,
|
|
2618
|
+
dynamicScope: filteredScope
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
if (!effectiveContext.parents?.includes(refName) && hasScopeAffectedDynamicRef(schemaObject, effectiveContext, refName)) return {
|
|
2623
|
+
...getScalar({
|
|
2624
|
+
item: schemaObject,
|
|
2625
|
+
name: name ?? refName,
|
|
2626
|
+
context: {
|
|
2627
|
+
...effectiveContext,
|
|
2628
|
+
parents: [...effectiveContext.parents ?? [], refName]
|
|
2629
|
+
},
|
|
2630
|
+
formDataContext
|
|
2631
|
+
}),
|
|
2632
|
+
originalSchema: schemaObject,
|
|
2633
|
+
isRef: false
|
|
2634
|
+
};
|
|
2635
|
+
if (!effectiveContext.parents?.includes(refName)) hasReadonlyProps = getScalar({
|
|
2236
2636
|
item: schemaObject,
|
|
2237
2637
|
name: refName,
|
|
2238
2638
|
context: {
|
|
2239
|
-
...
|
|
2240
|
-
parents: [...
|
|
2639
|
+
...effectiveContext,
|
|
2640
|
+
parents: [...effectiveContext.parents ?? [], refName]
|
|
2241
2641
|
}
|
|
2242
2642
|
}).hasReadonlyProps;
|
|
2243
2643
|
const isAnyOfNullable = schemaObject.anyOf?.some((anyOfItem) => !isReference(anyOfItem) && (anyOfItem.type === "null" || Array.isArray(anyOfItem.type) && anyOfItem.type.includes("null")));
|
|
@@ -2258,6 +2658,25 @@ function resolveValue({ schema, name, context, formDataContext }) {
|
|
|
2258
2658
|
dependencies: [resolvedImport.name]
|
|
2259
2659
|
};
|
|
2260
2660
|
}
|
|
2661
|
+
if (isDynamicReference(schema)) {
|
|
2662
|
+
const dynamicRef = schema.$dynamicRef;
|
|
2663
|
+
if (!dynamicRef.startsWith("#")) return makeUnknownValue(schema);
|
|
2664
|
+
const anchorName = getDynamicAnchorName(dynamicRef);
|
|
2665
|
+
if (!anchorName) return makeUnknownValue(schema);
|
|
2666
|
+
const { imports: resolvedImports, resolvedTypeName } = resolveDynamicRef(anchorName, context);
|
|
2667
|
+
if (resolvedTypeName === "unknown") return makeUnknownValue(schema);
|
|
2668
|
+
return {
|
|
2669
|
+
value: resolvedTypeName,
|
|
2670
|
+
imports: resolvedImports,
|
|
2671
|
+
type: "object",
|
|
2672
|
+
isEnum: false,
|
|
2673
|
+
schemas: [],
|
|
2674
|
+
isRef: true,
|
|
2675
|
+
hasReadonlyProps: false,
|
|
2676
|
+
originalSchema: schema,
|
|
2677
|
+
dependencies: [resolvedTypeName]
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2261
2680
|
return {
|
|
2262
2681
|
...getScalar({
|
|
2263
2682
|
item: schema,
|
|
@@ -2349,7 +2768,8 @@ function resolveObject({ schema, propName, combined = false, context, formDataCo
|
|
|
2349
2768
|
propName,
|
|
2350
2769
|
combined,
|
|
2351
2770
|
projectName: context.projectName ?? context.output.target,
|
|
2352
|
-
formDataContext
|
|
2771
|
+
formDataContext,
|
|
2772
|
+
dynamicScope: context.dynamicScope
|
|
2353
2773
|
});
|
|
2354
2774
|
if (resolveObjectCacheMap.has(hashKey)) return resolveObjectCacheMap.get(hashKey);
|
|
2355
2775
|
const result = resolveObjectOriginal({
|
|
@@ -3018,6 +3438,61 @@ function resolveDiscriminators(schemas, context) {
|
|
|
3018
3438
|
}
|
|
3019
3439
|
}
|
|
3020
3440
|
}
|
|
3441
|
+
for (const [parentName, parentSchema] of Object.entries(transformedSchemas)) {
|
|
3442
|
+
if (isBoolean$1(parentSchema)) continue;
|
|
3443
|
+
if (!parentSchema.oneOf || !parentSchema.discriminator?.mapping) continue;
|
|
3444
|
+
const { mapping, propertyName } = parentSchema.discriminator;
|
|
3445
|
+
if (!propertyName) continue;
|
|
3446
|
+
const parentProperties = parentSchema.properties;
|
|
3447
|
+
const parentRequired = parentSchema.required;
|
|
3448
|
+
const inheritableProps = {};
|
|
3449
|
+
if (parentProperties) {
|
|
3450
|
+
for (const [key, value] of Object.entries(parentProperties)) if (key !== propertyName) inheritableProps[key] = value;
|
|
3451
|
+
}
|
|
3452
|
+
const inheritableRequired = parentRequired?.filter((key) => key !== propertyName);
|
|
3453
|
+
const hasInheritableProps = Object.keys(inheritableProps).length > 0;
|
|
3454
|
+
for (const mappingValue of Object.values(mapping)) {
|
|
3455
|
+
let variantSchema;
|
|
3456
|
+
try {
|
|
3457
|
+
const { originalName } = getRefInfo(mappingValue, context);
|
|
3458
|
+
variantSchema = transformedSchemas[pascal(originalName)] ?? transformedSchemas[originalName];
|
|
3459
|
+
} catch {
|
|
3460
|
+
variantSchema = transformedSchemas[mappingValue];
|
|
3461
|
+
}
|
|
3462
|
+
if (!variantSchema || isBoolean$1(variantSchema)) continue;
|
|
3463
|
+
const variantAllOf = variantSchema.allOf;
|
|
3464
|
+
if (!variantAllOf) continue;
|
|
3465
|
+
const rewritten = [];
|
|
3466
|
+
for (const item of variantAllOf) {
|
|
3467
|
+
if (!isReference(item) || !item.$ref) {
|
|
3468
|
+
rewritten.push(item);
|
|
3469
|
+
continue;
|
|
3470
|
+
}
|
|
3471
|
+
let refOriginalName;
|
|
3472
|
+
try {
|
|
3473
|
+
refOriginalName = getRefInfo(item.$ref, context).originalName;
|
|
3474
|
+
} catch {
|
|
3475
|
+
refOriginalName = void 0;
|
|
3476
|
+
}
|
|
3477
|
+
if (!(refOriginalName === parentName || refOriginalName !== void 0 && pascal(refOriginalName) === pascal(parentName))) {
|
|
3478
|
+
rewritten.push(item);
|
|
3479
|
+
continue;
|
|
3480
|
+
}
|
|
3481
|
+
const inlinedParent = { ...parentSchema };
|
|
3482
|
+
delete inlinedParent.oneOf;
|
|
3483
|
+
delete inlinedParent.discriminator;
|
|
3484
|
+
delete inlinedParent.allOf;
|
|
3485
|
+
delete inlinedParent.anyOf;
|
|
3486
|
+
if (hasInheritableProps) inlinedParent.properties = { ...inheritableProps };
|
|
3487
|
+
else delete inlinedParent.properties;
|
|
3488
|
+
if (inheritableRequired && inheritableRequired.length > 0) inlinedParent.required = [...inheritableRequired];
|
|
3489
|
+
else delete inlinedParent.required;
|
|
3490
|
+
if (Object.keys(inlinedParent).filter((key) => key !== "type").length > 0) rewritten.push(inlinedParent);
|
|
3491
|
+
}
|
|
3492
|
+
if (rewritten.length === 0) delete variantSchema.allOf;
|
|
3493
|
+
else variantSchema.allOf = rewritten;
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3021
3496
|
return transformedSchemas;
|
|
3022
3497
|
}
|
|
3023
3498
|
//#endregion
|
|
@@ -3270,7 +3745,8 @@ function getQueryParamsTypes(queryParams, operationName, context) {
|
|
|
3270
3745
|
};
|
|
3271
3746
|
if (resolvedValue.isEnum && !resolvedValue.isRef) {
|
|
3272
3747
|
const enumName = queryName;
|
|
3273
|
-
const
|
|
3748
|
+
const parameterAsSchema = parameter;
|
|
3749
|
+
const enumValue = getEnum(resolvedValue.value, enumName, getEnumNames(resolvedValue.originalSchema) ?? getEnumNames(parameterAsSchema), context.output.override.enumGenerationType, getEnumDescriptions(resolvedValue.originalSchema) ?? getEnumDescriptions(parameterAsSchema), context.output.override.namingConvention.enum);
|
|
3274
3750
|
return {
|
|
3275
3751
|
name,
|
|
3276
3752
|
required,
|
|
@@ -3312,6 +3788,7 @@ function getQueryParams({ queryParams, operationName, context, suffix = "params"
|
|
|
3312
3788
|
},
|
|
3313
3789
|
deps: schemas,
|
|
3314
3790
|
isOptional: allOptional,
|
|
3791
|
+
paramNames: types.map(({ name }) => name),
|
|
3315
3792
|
requiredNullableKeys,
|
|
3316
3793
|
...nonPrimitiveKeys.length > 0 ? { nonPrimitiveKeys } : {}
|
|
3317
3794
|
};
|
|
@@ -3470,6 +3947,231 @@ function generateComponentDefinition(responses = {}, context, suffix) {
|
|
|
3470
3947
|
return generatorSchemas;
|
|
3471
3948
|
}
|
|
3472
3949
|
//#endregion
|
|
3950
|
+
//#region src/generators/factory.ts
|
|
3951
|
+
const circularRefCache = /* @__PURE__ */ new WeakMap();
|
|
3952
|
+
function getSchemasPath(context) {
|
|
3953
|
+
const { schemas, target } = context.output;
|
|
3954
|
+
if (schemas) return normalizeSafe(isString(schemas) ? schemas : schemas.path);
|
|
3955
|
+
const { dirname, filename } = getFileInfo(target);
|
|
3956
|
+
return joinSafe(dirname, filename + ".schemas");
|
|
3957
|
+
}
|
|
3958
|
+
function getSchemaImportPath(refName, context) {
|
|
3959
|
+
if (context.output.factoryMethods?.mode === "single") return;
|
|
3960
|
+
let outputDir = context.output.factoryMethods?.outputDirectory;
|
|
3961
|
+
let schemasPath = getSchemasPath(context);
|
|
3962
|
+
if (context.output.workspace) {
|
|
3963
|
+
if (outputDir && !isAbsolute(outputDir)) outputDir = resolve(context.output.workspace, outputDir);
|
|
3964
|
+
if (schemasPath && !isAbsolute(schemasPath)) schemasPath = resolve(context.output.workspace, schemasPath);
|
|
3965
|
+
}
|
|
3966
|
+
return joinSafe(outputDir ? relativeSafe(outputDir, schemasPath) : "./", conventionName(refName, context.output.namingConvention));
|
|
3967
|
+
}
|
|
3968
|
+
function isReference$1(schema) {
|
|
3969
|
+
return "$ref" in schema;
|
|
3970
|
+
}
|
|
3971
|
+
function getResolvedRef(schema, context) {
|
|
3972
|
+
return resolveRef(schema, context);
|
|
3973
|
+
}
|
|
3974
|
+
function getProperties(schema) {
|
|
3975
|
+
return schema.properties ?? {};
|
|
3976
|
+
}
|
|
3977
|
+
function getItems(schema) {
|
|
3978
|
+
return schema.items;
|
|
3979
|
+
}
|
|
3980
|
+
function getAdditionalProperties(schema) {
|
|
3981
|
+
return schema.additionalProperties;
|
|
3982
|
+
}
|
|
3983
|
+
function getSchemas(schemas) {
|
|
3984
|
+
return schemas;
|
|
3985
|
+
}
|
|
3986
|
+
function getExtendedProps(schema) {
|
|
3987
|
+
const extended = schema;
|
|
3988
|
+
return {
|
|
3989
|
+
constValue: extended.const,
|
|
3990
|
+
prefixItems: extended.prefixItems,
|
|
3991
|
+
minItems: extended.minItems
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
function generateFactory(schema, name, context) {
|
|
3995
|
+
if (!canGenerateSchema(schema) || !context.output.factoryMethods) return void 0;
|
|
3996
|
+
const { functionNamePrefix, mode } = context.output.factoryMethods;
|
|
3997
|
+
const factoryName = `${functionNamePrefix}${pascal(name)}`;
|
|
3998
|
+
const imports = [];
|
|
3999
|
+
const payload = buildPayload(schema, context, [name], imports);
|
|
4000
|
+
if (mode !== "single") {
|
|
4001
|
+
const schemaImportPath = getSchemaImportPath(name, context);
|
|
4002
|
+
imports.push({
|
|
4003
|
+
name,
|
|
4004
|
+
importPath: schemaImportPath
|
|
4005
|
+
});
|
|
4006
|
+
}
|
|
4007
|
+
return {
|
|
4008
|
+
model: `export function ${factoryName}(): ${name} {\n return ${payload};\n}\n`,
|
|
4009
|
+
imports
|
|
4010
|
+
};
|
|
4011
|
+
}
|
|
4012
|
+
function canGenerateSchema(schema) {
|
|
4013
|
+
return schema.type === "object" || schema.type === "array" || !!schema.properties || !!schema.allOf || !!schema.oneOf || !!schema.anyOf || !!schema.items || !!schema.enum;
|
|
4014
|
+
}
|
|
4015
|
+
function hasCircularReference(target, sourceName, context, visited = /* @__PURE__ */ new Set()) {
|
|
4016
|
+
if (isReference$1(target)) {
|
|
4017
|
+
const { imports, schema } = getResolvedRef(target, context);
|
|
4018
|
+
const refName = imports[0]?.name;
|
|
4019
|
+
if (refName === sourceName) return true;
|
|
4020
|
+
if (refName && visited.has(refName)) return false;
|
|
4021
|
+
if (refName) visited.add(refName);
|
|
4022
|
+
let cache = circularRefCache.get(context);
|
|
4023
|
+
if (!cache) {
|
|
4024
|
+
cache = /* @__PURE__ */ new Map();
|
|
4025
|
+
circularRefCache.set(context, cache);
|
|
4026
|
+
}
|
|
4027
|
+
const cacheKey = refName ? `${sourceName}::${refName}` : void 0;
|
|
4028
|
+
if (cacheKey) {
|
|
4029
|
+
const cached = cache.get(cacheKey);
|
|
4030
|
+
if (cached !== void 0) return cached;
|
|
4031
|
+
}
|
|
4032
|
+
const result = hasCircularReference(schema, sourceName, context, visited);
|
|
4033
|
+
if (cacheKey) cache.set(cacheKey, result);
|
|
4034
|
+
return result;
|
|
4035
|
+
}
|
|
4036
|
+
const check = (schemas) => schemas?.some((s) => hasCircularReference(s, sourceName, context, visited)) ?? false;
|
|
4037
|
+
const items = getItems(target);
|
|
4038
|
+
const additionalProperties = getAdditionalProperties(target);
|
|
4039
|
+
return check(getSchemas(target.allOf)) || check(getSchemas(target.oneOf)) || check(getSchemas(target.anyOf)) || Object.values(getProperties(target)).some((s) => hasCircularReference(s, sourceName, context, visited)) || !!items && hasCircularReference(items, sourceName, context, visited) || typeof additionalProperties === "object" && hasCircularReference(additionalProperties, sourceName, context, visited);
|
|
4040
|
+
}
|
|
4041
|
+
function buildPayload(target, context, parents, imports) {
|
|
4042
|
+
if (isReference$1(target)) return buildRefPayload(target, context, parents, imports);
|
|
4043
|
+
const schema = target;
|
|
4044
|
+
if (schema.allOf) return buildAllOfPayload(getSchemas(schema.allOf) ?? [], context, parents, imports);
|
|
4045
|
+
if (schema.oneOf) return buildFirstOfPayload(getSchemas(schema.oneOf) ?? [], context, parents, imports);
|
|
4046
|
+
if (schema.anyOf) return buildFirstOfPayload(getSchemas(schema.anyOf) ?? [], context, parents, imports);
|
|
4047
|
+
const { constValue } = getExtendedProps(schema);
|
|
4048
|
+
if (constValue !== void 0) return formatValue(constValue);
|
|
4049
|
+
if (schema.default !== void 0) return buildDefaultPayload(schema, context);
|
|
4050
|
+
const schemaType = inferSchemaType(schema);
|
|
4051
|
+
if (schemaType === "object" || schema.properties) return buildObjectPayload(schema, context, parents, imports);
|
|
4052
|
+
if (schemaType === "array") return buildArrayPayload(schema, context, parents, imports);
|
|
4053
|
+
return buildPrimitivePayload(schema, schemaType, context);
|
|
4054
|
+
}
|
|
4055
|
+
function buildRefPayload(schema, context, parents, imports) {
|
|
4056
|
+
const { schema: resolved, imports: refImports } = getResolvedRef(schema, context);
|
|
4057
|
+
const refName = refImports[0]?.name;
|
|
4058
|
+
if (!refName) return "{}";
|
|
4059
|
+
if (parents.includes(refName) || hasCircularReference(resolved, parents[0], context)) {
|
|
4060
|
+
imports.push({
|
|
4061
|
+
name: refName,
|
|
4062
|
+
importPath: getSchemaImportPath(refName, context)
|
|
4063
|
+
});
|
|
4064
|
+
return `{} as ${refName}`;
|
|
4065
|
+
}
|
|
4066
|
+
const { functionNamePrefix = "create", mode = "single" } = context.output.factoryMethods ?? {};
|
|
4067
|
+
const refFactoryName = `${functionNamePrefix}${pascal(refName)}`;
|
|
4068
|
+
if (mode !== "single-split") {
|
|
4069
|
+
const importPath = resolveImportPath(mode, refName, context);
|
|
4070
|
+
imports.push({
|
|
4071
|
+
name: refFactoryName,
|
|
4072
|
+
importPath,
|
|
4073
|
+
isConstant: true
|
|
4074
|
+
});
|
|
4075
|
+
}
|
|
4076
|
+
imports.push({
|
|
4077
|
+
name: refName,
|
|
4078
|
+
importPath: getSchemaImportPath(refName, context)
|
|
4079
|
+
});
|
|
4080
|
+
return `${refFactoryName}()`;
|
|
4081
|
+
}
|
|
4082
|
+
function resolveImportPath(mode, refName, context) {
|
|
4083
|
+
const baseName = conventionName(refName, context.output.namingConvention);
|
|
4084
|
+
switch (mode) {
|
|
4085
|
+
case "split": return `./${baseName}.factory`;
|
|
4086
|
+
case "single-split": return `./${conventionName("factoryMethods", context.output.namingConvention)}`;
|
|
4087
|
+
case "single": return `./${baseName}`;
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
function buildAllOfPayload(allOf, context, parents, imports) {
|
|
4091
|
+
const payloads = allOf.map((s) => buildPayload(s, context, parents, imports));
|
|
4092
|
+
return payloads.length > 0 ? `Object.assign({}, ${payloads.join(", ")})` : "{}";
|
|
4093
|
+
}
|
|
4094
|
+
function buildFirstOfPayload(schemas, context, parents, imports) {
|
|
4095
|
+
const first = schemas[0];
|
|
4096
|
+
return first ? buildPayload(first, context, parents, imports) : "{}";
|
|
4097
|
+
}
|
|
4098
|
+
function buildObjectPayload(schema, context, parents, imports) {
|
|
4099
|
+
const { includeOptionalProperty = false } = context.output.factoryMethods ?? {};
|
|
4100
|
+
const props = getProperties(schema);
|
|
4101
|
+
const requiredProps = schema.required ?? [];
|
|
4102
|
+
const entries = Object.entries(props);
|
|
4103
|
+
if (context.output.propertySortOrder === PropertySortOrder.ALPHABETICAL) entries.sort(([a], [b]) => a.localeCompare(b));
|
|
4104
|
+
const includeOptional = includeOptionalProperty;
|
|
4105
|
+
const lines = [];
|
|
4106
|
+
for (const [key, prop] of entries) {
|
|
4107
|
+
const isRequired = requiredProps.includes(key);
|
|
4108
|
+
const resolved = isReference$1(prop) ? getResolvedRef(prop, context).schema : prop;
|
|
4109
|
+
const isReadOnly = !!prop.readOnly || !!resolved.readOnly;
|
|
4110
|
+
const isWriteOnly = !!prop.writeOnly || !!resolved.writeOnly;
|
|
4111
|
+
if (!isRequired) {
|
|
4112
|
+
if (isReadOnly) continue;
|
|
4113
|
+
if (!isWriteOnly && !includeOptional) continue;
|
|
4114
|
+
}
|
|
4115
|
+
const payload = buildPayload(prop, context, parents, imports);
|
|
4116
|
+
const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
4117
|
+
lines.push(`${safeKey}: ${payload}`);
|
|
4118
|
+
}
|
|
4119
|
+
return `{\n ${lines.join(",\n ")}\n }`;
|
|
4120
|
+
}
|
|
4121
|
+
function buildArrayPayload(schema, context, parents, imports) {
|
|
4122
|
+
const { prefixItems, minItems } = getExtendedProps(schema);
|
|
4123
|
+
const items = getItems(schema);
|
|
4124
|
+
if (prefixItems && prefixItems.length > 0) return `[${prefixItems.map((item) => buildPayload(item, context, parents, imports)).join(", ")}]`;
|
|
4125
|
+
if (minItems && items) {
|
|
4126
|
+
const MAX_MIN_ITEMS = 50;
|
|
4127
|
+
if (minItems > MAX_MIN_ITEMS) logWarning(`Warning: minItems is ${minItems}, capping at ${MAX_MIN_ITEMS} to prevent massive payload.`);
|
|
4128
|
+
const count = Math.min(minItems, MAX_MIN_ITEMS);
|
|
4129
|
+
const itemPayload = buildPayload(items, context, parents, imports);
|
|
4130
|
+
return `[${Array.from({ length: count }).fill(itemPayload).join(", ")}]`;
|
|
4131
|
+
}
|
|
4132
|
+
return "[]";
|
|
4133
|
+
}
|
|
4134
|
+
function inferSchemaType(schema) {
|
|
4135
|
+
let type = schema.type;
|
|
4136
|
+
if (Array.isArray(type)) {
|
|
4137
|
+
const nonNull = type.filter((t) => t !== "null");
|
|
4138
|
+
type = nonNull.length > 0 ? nonNull[0] : "null";
|
|
4139
|
+
}
|
|
4140
|
+
if (!type && schema.items) return "array";
|
|
4141
|
+
if (!type && schema.enum) {
|
|
4142
|
+
const first = schema.enum[0];
|
|
4143
|
+
if (typeof first === "number") return "number";
|
|
4144
|
+
if (typeof first === "boolean") return "boolean";
|
|
4145
|
+
return "string";
|
|
4146
|
+
}
|
|
4147
|
+
return type;
|
|
4148
|
+
}
|
|
4149
|
+
function buildDefaultPayload(schema, context) {
|
|
4150
|
+
if (context.output.override.useDates && typeof schema.default === "string" && (schema.format === "date" || schema.format === "date-time")) return `new Date('${schema.default}')`;
|
|
4151
|
+
return formatValue(schema.default);
|
|
4152
|
+
}
|
|
4153
|
+
function buildPrimitivePayload(schema, schemaType, context) {
|
|
4154
|
+
if (schemaType === "null") return "null";
|
|
4155
|
+
const enumValues = schema.enum;
|
|
4156
|
+
if (schemaType === "boolean") return enumValues && enumValues.length > 0 ? String(enumValues[0]) : "false";
|
|
4157
|
+
if (schemaType === "number" || schemaType === "integer") return enumValues && enumValues.length > 0 ? String(enumValues[0]) : "0";
|
|
4158
|
+
if (schemaType === "string") {
|
|
4159
|
+
if (enumValues && enumValues.length > 0) {
|
|
4160
|
+
const first = enumValues[0];
|
|
4161
|
+
return typeof first === "string" ? JSON.stringify(first) : String(first);
|
|
4162
|
+
}
|
|
4163
|
+
if (schema.format === "date" || schema.format === "date-time") return context.output.override.useDates ? "new Date(0)" : `'${(/* @__PURE__ */ new Date(0)).toISOString()}'`;
|
|
4164
|
+
return "''";
|
|
4165
|
+
}
|
|
4166
|
+
return "undefined as unknown";
|
|
4167
|
+
}
|
|
4168
|
+
function formatValue(val) {
|
|
4169
|
+
if (val === null) return "null";
|
|
4170
|
+
if (typeof val === "string") return JSON.stringify(val);
|
|
4171
|
+
if (typeof val === "object") return JSON.stringify(val);
|
|
4172
|
+
return String(val);
|
|
4173
|
+
}
|
|
4174
|
+
//#endregion
|
|
3473
4175
|
//#region src/generators/imports.ts
|
|
3474
4176
|
function generateImports({ imports, namingConvention = NamingConvention.CAMEL_CASE, importExtension = "" }) {
|
|
3475
4177
|
if (imports.length === 0) return "";
|
|
@@ -3721,6 +4423,9 @@ function parseFile(file, name) {
|
|
|
3721
4423
|
return;
|
|
3722
4424
|
}
|
|
3723
4425
|
}
|
|
4426
|
+
function standardMutatorInfo() {
|
|
4427
|
+
return { numberOfParams: 1 };
|
|
4428
|
+
}
|
|
3724
4429
|
function parseFunction(ast, funcName) {
|
|
3725
4430
|
const node = ast.body.find((childNode) => {
|
|
3726
4431
|
if (childNode.type === "VariableDeclaration") return childNode.declarations.find((d) => d.id.type === "Identifier" && d.id.name === funcName);
|
|
@@ -3745,6 +4450,7 @@ function parseFunction(ast, funcName) {
|
|
|
3745
4450
|
const declaration = "declarations" in node ? node.declarations.find((d) => d.id.type === "Identifier" && d.id.name === funcName) : void 0;
|
|
3746
4451
|
if (declaration?.init) {
|
|
3747
4452
|
if ("name" in declaration.init) return parseFunction(ast, declaration.init.name);
|
|
4453
|
+
if (declaration.init.type === "CallExpression") return standardMutatorInfo();
|
|
3748
4454
|
if ("body" in declaration.init && "params" in declaration.init && declaration.init.body.type === "ArrowFunctionExpression") return {
|
|
3749
4455
|
numberOfParams: declaration.init.params.length,
|
|
3750
4456
|
returnNumberOfParams: declaration.init.body.params.length
|
|
@@ -4160,7 +4866,7 @@ function generateParameterDefinition(parameters = {}, context, suffix) {
|
|
|
4160
4866
|
* @param name interface name
|
|
4161
4867
|
* @param schema
|
|
4162
4868
|
*/
|
|
4163
|
-
function generateInterface({ name, schema, context }) {
|
|
4869
|
+
function generateInterface({ name, schema, context, genericParams }) {
|
|
4164
4870
|
const scalar = getScalar({
|
|
4165
4871
|
item: schema,
|
|
4166
4872
|
name,
|
|
@@ -4168,6 +4874,7 @@ function generateInterface({ name, schema, context }) {
|
|
|
4168
4874
|
});
|
|
4169
4875
|
const isEmptyObject = scalar.value === "{}";
|
|
4170
4876
|
const shouldUseTypeAlias = context.output.override.useTypeOverInterfaces ?? scalar.useTypeAlias;
|
|
4877
|
+
const genericSuffix = genericParams && genericParams.length > 0 ? `<${genericParams.join(", ")}>` : "";
|
|
4171
4878
|
let model = "";
|
|
4172
4879
|
model += jsDoc(schema);
|
|
4173
4880
|
if (isEmptyObject) model += "// eslint-disable-next-line @typescript-eslint/no-empty-interface\n";
|
|
@@ -4175,12 +4882,12 @@ function generateInterface({ name, schema, context }) {
|
|
|
4175
4882
|
const properties = schema.properties;
|
|
4176
4883
|
if (properties && Object.values(properties).length > 0 && Object.values(properties).every((item) => "const" in item)) {
|
|
4177
4884
|
const mappedScalarValue = scalar.value.replaceAll(";", ",").replaceAll("?:", ":");
|
|
4178
|
-
model += `export const ${name}Value = ${mappedScalarValue} as const;\nexport type ${name} = typeof ${name}Value;\n`;
|
|
4885
|
+
model += `export const ${name}Value = ${mappedScalarValue} as const;\nexport type ${name}${genericSuffix} = typeof ${name}Value;\n`;
|
|
4179
4886
|
} else {
|
|
4180
4887
|
const blankInterfaceValue = scalar.value === "unknown" ? "{}" : scalar.value;
|
|
4181
|
-
model += `export interface ${name} ${blankInterfaceValue}\n`;
|
|
4888
|
+
model += `export interface ${name}${genericSuffix} ${blankInterfaceValue}\n`;
|
|
4182
4889
|
}
|
|
4183
|
-
} else model += `export type ${name} = ${scalar.value};\n`;
|
|
4890
|
+
} else model += `export type ${name}${genericSuffix} = ${scalar.value};\n`;
|
|
4184
4891
|
const externalModulesImportsOnly = scalar.imports.filter((importName) => importName.alias ? importName.alias !== name : importName.name !== name);
|
|
4185
4892
|
return [...scalar.schemas, {
|
|
4186
4893
|
name,
|
|
@@ -4214,6 +4921,17 @@ function generateSchemasDefinition(schemas = {}, context, suffix, filters) {
|
|
|
4214
4921
|
const normalizedName = conventionName(schema.name, context.output.namingConvention);
|
|
4215
4922
|
if (!seenNames.has(normalizedName)) {
|
|
4216
4923
|
seenNames.add(normalizedName);
|
|
4924
|
+
if (context.output.factoryMethods && schema.schema) {
|
|
4925
|
+
const factoryData = generateFactory(schema.schema, schema.name, context);
|
|
4926
|
+
if (factoryData) if (context.output.factoryMethods.mode === "single") {
|
|
4927
|
+
schema.model += `\n${factoryData.model}`;
|
|
4928
|
+
for (const imp of factoryData.imports) if (!schema.imports.some((existing) => existing.name === imp.name)) schema.imports.push(imp);
|
|
4929
|
+
} else {
|
|
4930
|
+
schema.factory = factoryData.model;
|
|
4931
|
+
schema.factoryImports = factoryData.imports;
|
|
4932
|
+
schema.factoryMode = context.output.factoryMethods.mode;
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4217
4935
|
deduplicatedModels.push(schema);
|
|
4218
4936
|
}
|
|
4219
4937
|
}
|
|
@@ -4258,6 +4976,21 @@ function shouldCreateInterface(schema) {
|
|
|
4258
4976
|
const isNullable = isArray(schema.type) && schema.type.includes("null");
|
|
4259
4977
|
return (!schema.type || schema.type === "object") && !schema.allOf && !schema.oneOf && !schema.anyOf && isDereferenced(schema) && !schema.enum && !isNullable;
|
|
4260
4978
|
}
|
|
4979
|
+
function collectGenericParams(schema) {
|
|
4980
|
+
const defs = schema.$defs;
|
|
4981
|
+
if (!defs || typeof defs !== "object") return [];
|
|
4982
|
+
const anchors = [];
|
|
4983
|
+
for (const defSchema of Object.values(defs)) {
|
|
4984
|
+
if (!defSchema || typeof defSchema !== "object") continue;
|
|
4985
|
+
const rec = defSchema;
|
|
4986
|
+
if (rec.$dynamicAnchor !== void 0 && rec.$ref === void 0) anchors.push(rec.$dynamicAnchor);
|
|
4987
|
+
}
|
|
4988
|
+
const uniqueNames = dynamicAnchorsToUniqueParamNames(anchors);
|
|
4989
|
+
return anchors.map((anchor) => ({
|
|
4990
|
+
anchorName: anchor,
|
|
4991
|
+
paramName: uniqueNames.get(anchor) ?? dynamicAnchorToParamName(anchor)
|
|
4992
|
+
}));
|
|
4993
|
+
}
|
|
4261
4994
|
function generateSchemaDefinitions(schemaName, schema, context, suffix) {
|
|
4262
4995
|
const sanitizedSchemaName = sanitize(`${pascal(schemaName)}${suffix}`, {
|
|
4263
4996
|
underscore: "_",
|
|
@@ -4272,22 +5005,82 @@ function generateSchemaDefinitions(schemaName, schema, context, suffix) {
|
|
|
4272
5005
|
imports: [],
|
|
4273
5006
|
schema
|
|
4274
5007
|
}];
|
|
5008
|
+
const alias = extractBoundAliasInfo(schema, context);
|
|
5009
|
+
if (alias) {
|
|
5010
|
+
const genericParams = alias.genericParams.map((paramName) => ({
|
|
5011
|
+
anchorName: paramName,
|
|
5012
|
+
paramName
|
|
5013
|
+
}));
|
|
5014
|
+
const genericSuffix = genericParams.length > 0 ? `<${genericParams.map((p) => p.paramName).join(", ")}>` : "";
|
|
5015
|
+
const typeArgsStr = alias.typeArgs.join(", ");
|
|
5016
|
+
const genericPart = `${alias.genericName}<${typeArgsStr}>`;
|
|
5017
|
+
const schemaType = schema.type;
|
|
5018
|
+
const nullable = Array.isArray(schemaType) && schemaType.includes("null") || schema.nullable === true ? " | null" : "";
|
|
5019
|
+
let model;
|
|
5020
|
+
const allImports = [{
|
|
5021
|
+
name: alias.genericName,
|
|
5022
|
+
schemaName: alias.genericName
|
|
5023
|
+
}, ...alias.imports];
|
|
5024
|
+
if (alias.extraSchemas && alias.extraSchemas.length > 0) {
|
|
5025
|
+
const aliasScopedContext = {
|
|
5026
|
+
...context,
|
|
5027
|
+
dynamicScope: buildDynamicScope(schemaName, schema, context)
|
|
5028
|
+
};
|
|
5029
|
+
const subSchemas = [];
|
|
5030
|
+
model = `export type ${sanitizedSchemaName}${genericSuffix} = (${[genericPart, ...alias.extraSchemas.map((extraSchema) => {
|
|
5031
|
+
const resolved = resolveValue({
|
|
5032
|
+
schema: extraSchema,
|
|
5033
|
+
name: sanitizedSchemaName,
|
|
5034
|
+
context: aliasScopedContext
|
|
5035
|
+
});
|
|
5036
|
+
for (const imp of resolved.imports) {
|
|
5037
|
+
const impSchemaName = imp.schemaName ?? imp.name;
|
|
5038
|
+
if (!allImports.some((a) => a.name === imp.name && a.schemaName === impSchemaName)) allImports.push({
|
|
5039
|
+
name: imp.name,
|
|
5040
|
+
schemaName: impSchemaName
|
|
5041
|
+
});
|
|
5042
|
+
}
|
|
5043
|
+
for (const sub of resolved.schemas) if (sub.name !== sanitizedSchemaName) subSchemas.push(sub);
|
|
5044
|
+
return resolved.value;
|
|
5045
|
+
})].join(" & ")})${nullable};\n`;
|
|
5046
|
+
return [...subSchemas, {
|
|
5047
|
+
name: sanitizedSchemaName,
|
|
5048
|
+
model,
|
|
5049
|
+
imports: allImports,
|
|
5050
|
+
dependencies: allImports.map((i) => i.name),
|
|
5051
|
+
schema
|
|
5052
|
+
}];
|
|
5053
|
+
} else model = `export type ${sanitizedSchemaName}${genericSuffix} = ${genericPart}${nullable};\n`;
|
|
5054
|
+
return [{
|
|
5055
|
+
name: sanitizedSchemaName,
|
|
5056
|
+
model,
|
|
5057
|
+
imports: allImports,
|
|
5058
|
+
dependencies: allImports.map((i) => i.name),
|
|
5059
|
+
schema
|
|
5060
|
+
}];
|
|
5061
|
+
}
|
|
5062
|
+
const scopedContext = isBoolean(schema) ? context : {
|
|
5063
|
+
...context,
|
|
5064
|
+
dynamicScope: buildDynamicScope(schemaName, schema, context)
|
|
5065
|
+
};
|
|
5066
|
+
const genericParams = collectGenericParams(schema);
|
|
4275
5067
|
if (shouldCreateInterface(schema)) return generateInterface({
|
|
4276
5068
|
name: sanitizedSchemaName,
|
|
4277
5069
|
schema,
|
|
4278
|
-
context
|
|
5070
|
+
context: scopedContext,
|
|
5071
|
+
genericParams: genericParams.length > 0 ? genericParams.map((p) => p.paramName) : void 0
|
|
4279
5072
|
});
|
|
4280
5073
|
const resolvedValue = resolveValue({
|
|
4281
5074
|
schema,
|
|
4282
5075
|
name: sanitizedSchemaName,
|
|
4283
|
-
context
|
|
5076
|
+
context: scopedContext
|
|
4284
5077
|
});
|
|
4285
5078
|
let output = "";
|
|
4286
5079
|
let imports = resolvedValue.imports;
|
|
4287
5080
|
output += jsDoc(schema);
|
|
4288
5081
|
if (resolvedValue.isEnum && !resolvedValue.isRef) output += getEnum(resolvedValue.value, sanitizedSchemaName, getEnumNames(resolvedValue.originalSchema), context.output.override.enumGenerationType, getEnumDescriptions(resolvedValue.originalSchema), context.output.override.namingConvention.enum);
|
|
4289
5082
|
else if (sanitizedSchemaName === resolvedValue.value && resolvedValue.isRef) {
|
|
4290
|
-
const { schema: referredSchema } = resolveRef(schema,
|
|
5083
|
+
const { schema: referredSchema } = resolveRef(schema, scopedContext);
|
|
4291
5084
|
if (!shouldCreateInterface(referredSchema)) {
|
|
4292
5085
|
const imp = resolvedValue.imports.find((imp) => imp.name === sanitizedSchemaName);
|
|
4293
5086
|
if (imp) {
|
|
@@ -4308,7 +5101,8 @@ function generateSchemaDefinitions(schemaName, schema, context, suffix) {
|
|
|
4308
5101
|
resolvedValue.dependencies.push(...schema.dependencies ?? []);
|
|
4309
5102
|
return false;
|
|
4310
5103
|
});
|
|
4311
|
-
|
|
5104
|
+
const genericSuffix = genericParams.length > 0 ? `<${genericParams.map((p) => p.paramName).join(", ")}>` : "";
|
|
5105
|
+
output += `export type ${sanitizedSchemaName}${genericSuffix} = ${resolvedValue.value};\n`;
|
|
4312
5106
|
}
|
|
4313
5107
|
return [...resolvedValue.schemas, {
|
|
4314
5108
|
name: sanitizedSchemaName,
|
|
@@ -4620,33 +5414,54 @@ function getCanonicalMap(schemaGroups, schemaPath, namingConvention, fileExtensi
|
|
|
4620
5414
|
canonicalNameMap
|
|
4621
5415
|
};
|
|
4622
5416
|
}
|
|
4623
|
-
function normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension, tsconfig) {
|
|
5417
|
+
function normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension, tsconfig, factoryOutputDirectory) {
|
|
4624
5418
|
const importExtension = getImportExtension(fileExtension, tsconfig);
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
5419
|
+
const factoryDir = factoryOutputDirectory ?? schemaPath;
|
|
5420
|
+
for (const schema of schemas) {
|
|
5421
|
+
schema.imports = schema.imports.map((imp) => {
|
|
5422
|
+
const canonicalByName = canonicalNameMap.get(imp.name);
|
|
5423
|
+
const resolvedImportKey = resolveImportKey(schemaPath, imp.importPath ?? `./${conventionName(imp.name, namingConvention)}`, fileExtension);
|
|
5424
|
+
const canonicalByPath = canonicalPathMap.get(resolvedImportKey);
|
|
5425
|
+
const canonical = canonicalByName ?? canonicalByPath;
|
|
5426
|
+
if (!canonical?.importPath) return imp;
|
|
5427
|
+
const relative = relativeSafe(schemaPath, canonical.importPath.replaceAll("\\", "/"));
|
|
5428
|
+
const importPath = `${relative.endsWith(fileExtension) ? relative.slice(0, -fileExtension.length) : relative.replace(/\.ts$/, "")}${importExtension}`;
|
|
5429
|
+
return {
|
|
5430
|
+
...imp,
|
|
5431
|
+
importPath
|
|
5432
|
+
};
|
|
5433
|
+
});
|
|
5434
|
+
if (schema.factoryImports) schema.factoryImports = schema.factoryImports.map((imp) => {
|
|
5435
|
+
const canonicalByName = canonicalNameMap.get(imp.name);
|
|
5436
|
+
const resolvedImportKey = resolveImportKey(factoryDir, imp.importPath ?? `./${conventionName(imp.name, namingConvention)}`, fileExtension);
|
|
5437
|
+
const canonicalByPath = canonicalPathMap.get(resolvedImportKey);
|
|
5438
|
+
const canonical = canonicalByName ?? canonicalByPath;
|
|
5439
|
+
if (!canonical?.importPath) return imp;
|
|
5440
|
+
const relative = relativeSafe(factoryDir, canonical.importPath.replaceAll("\\", "/"));
|
|
5441
|
+
const importPath = `${relative.endsWith(fileExtension) ? relative.slice(0, -fileExtension.length) : relative.replace(/\.ts$/, "")}${importExtension}`;
|
|
5442
|
+
return {
|
|
5443
|
+
...imp,
|
|
5444
|
+
importPath
|
|
5445
|
+
};
|
|
5446
|
+
});
|
|
5447
|
+
}
|
|
4638
5448
|
}
|
|
4639
5449
|
function mergeSchemaGroup(schemas) {
|
|
4640
5450
|
const baseSchemaName = schemas[0].name;
|
|
4641
5451
|
const baseSchema = schemas[0].schema;
|
|
4642
5452
|
const mergedImports = [...new Map(schemas.flatMap((schema) => schema.imports).map((imp) => [JSON.stringify(imp), imp])).values()];
|
|
4643
5453
|
const mergedDependencies = [...new Set(schemas.flatMap((schema) => schema.dependencies ?? []))];
|
|
5454
|
+
const mergedFactory = schemas.map((s) => s.factory).filter(Boolean).join("\n");
|
|
5455
|
+
const mergedFactoryImports = [...new Map(schemas.flatMap((schema) => schema.factoryImports ?? []).map((imp) => [JSON.stringify(imp), imp])).values()];
|
|
4644
5456
|
return {
|
|
4645
5457
|
name: baseSchemaName,
|
|
4646
5458
|
schema: baseSchema,
|
|
4647
5459
|
model: schemas.map((schema) => schema.model).join("\n"),
|
|
4648
5460
|
imports: mergedImports,
|
|
4649
|
-
dependencies: mergedDependencies
|
|
5461
|
+
dependencies: mergedDependencies,
|
|
5462
|
+
factory: mergedFactory || void 0,
|
|
5463
|
+
factoryImports: mergedFactoryImports,
|
|
5464
|
+
factoryMode: schemas[0].factoryMode
|
|
4650
5465
|
};
|
|
4651
5466
|
}
|
|
4652
5467
|
function resolveImportKey(schemaPath, importPath, fileExtension) {
|
|
@@ -4688,10 +5503,39 @@ async function writeSchema({ path, schema, target, namingConvention, fileExtensi
|
|
|
4688
5503
|
throw new Error(`Oups... 🍻. An Error occurred while writing schema ${name} => ${String(error)}`, { cause: error });
|
|
4689
5504
|
}
|
|
4690
5505
|
}
|
|
4691
|
-
async function
|
|
5506
|
+
async function emitFactoryForSchema(schema, namingConvention, header, factoryDir, fileExtension, helpers) {
|
|
5507
|
+
if (schema.factory && schema.factoryMode) {
|
|
5508
|
+
const mode = schema.factoryMode;
|
|
5509
|
+
if (mode === "split") {
|
|
5510
|
+
const factoryName = `${conventionName(schema.name, namingConvention)}.factory`;
|
|
5511
|
+
helpers.separateFactoryNames.push(factoryName);
|
|
5512
|
+
const factoryFile = `${header}\n${generateImports({
|
|
5513
|
+
imports: schema.factoryImports ?? [],
|
|
5514
|
+
namingConvention
|
|
5515
|
+
})}\n\n${schema.factory}`;
|
|
5516
|
+
await writeGeneratedFile(getPath(factoryDir, factoryName, fileExtension), factoryFile);
|
|
5517
|
+
} else if (mode === "single-split") {
|
|
5518
|
+
helpers.isCombined.value = true;
|
|
5519
|
+
helpers.combinedFactoryContent.value += `${schema.factory}\n`;
|
|
5520
|
+
helpers.combinedFactoryImports.push(...schema.factoryImports ?? []);
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5523
|
+
}
|
|
5524
|
+
async function writeSchemas({ schemaPath, schemas, target, namingConvention, fileExtension, header, indexFiles, tsconfig, factoryOutputDirectory }) {
|
|
4692
5525
|
const schemaGroups = getSchemaGroups(schemaPath, schemas, namingConvention, fileExtension);
|
|
4693
5526
|
const { canonicalPathMap, canonicalNameMap } = getCanonicalMap(schemaGroups, schemaPath, namingConvention, fileExtension);
|
|
4694
|
-
normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension, tsconfig);
|
|
5527
|
+
normalizeCanonicalImportPaths(schemas, canonicalPathMap, canonicalNameMap, schemaPath, namingConvention, fileExtension, tsconfig, factoryOutputDirectory);
|
|
5528
|
+
const factoryDir = factoryOutputDirectory ?? schemaPath;
|
|
5529
|
+
const combinedFactoryContent = { value: "" };
|
|
5530
|
+
const combinedFactoryImports = [];
|
|
5531
|
+
const isCombined = { value: false };
|
|
5532
|
+
const separateFactoryNames = [];
|
|
5533
|
+
const factoryHelpers = {
|
|
5534
|
+
separateFactoryNames,
|
|
5535
|
+
combinedFactoryContent,
|
|
5536
|
+
combinedFactoryImports,
|
|
5537
|
+
isCombined
|
|
5538
|
+
};
|
|
4695
5539
|
for (const groupSchemas of Object.values(schemaGroups)) {
|
|
4696
5540
|
if (groupSchemas.length === 1) {
|
|
4697
5541
|
await writeSchema({
|
|
@@ -4703,17 +5547,29 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
|
|
|
4703
5547
|
header,
|
|
4704
5548
|
tsconfig
|
|
4705
5549
|
});
|
|
5550
|
+
const singleSchema = groupSchemas[0];
|
|
5551
|
+
await emitFactoryForSchema(singleSchema, namingConvention, header, factoryDir, fileExtension, factoryHelpers);
|
|
4706
5552
|
continue;
|
|
4707
5553
|
}
|
|
5554
|
+
const mergedSchema = mergeSchemaGroup(groupSchemas);
|
|
4708
5555
|
await writeSchema({
|
|
4709
5556
|
path: schemaPath,
|
|
4710
|
-
schema:
|
|
5557
|
+
schema: mergedSchema,
|
|
4711
5558
|
target,
|
|
4712
5559
|
namingConvention,
|
|
4713
5560
|
fileExtension,
|
|
4714
5561
|
header,
|
|
4715
5562
|
tsconfig
|
|
4716
5563
|
});
|
|
5564
|
+
await emitFactoryForSchema(mergedSchema, namingConvention, header, factoryDir, fileExtension, factoryHelpers);
|
|
5565
|
+
}
|
|
5566
|
+
if (isCombined.value) {
|
|
5567
|
+
const factoryFileName = conventionName("factoryMethods", namingConvention);
|
|
5568
|
+
const factoryFile = `${header}\n${generateImports({
|
|
5569
|
+
imports: combinedFactoryImports,
|
|
5570
|
+
namingConvention
|
|
5571
|
+
})}\n\n${combinedFactoryContent.value}`;
|
|
5572
|
+
await writeGeneratedFile(getPath(factoryDir, factoryFileName, fileExtension), factoryFile);
|
|
4717
5573
|
}
|
|
4718
5574
|
if (indexFiles) {
|
|
4719
5575
|
const schemaFilePath = nodePath.join(schemaPath, `index.ts`);
|
|
@@ -4721,7 +5577,25 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
|
|
|
4721
5577
|
const ext = getImportExtension(fileExtension, tsconfig);
|
|
4722
5578
|
const conventionNamesSet = new Set(Object.values(schemaGroups).map((group) => conventionName(group[0].name, namingConvention)));
|
|
4723
5579
|
try {
|
|
4724
|
-
|
|
5580
|
+
const currentExports = [...conventionNamesSet].map((schemaName) => `export * from './${schemaName}${ext}';`);
|
|
5581
|
+
if (factoryOutputDirectory && normalizeSafe(factoryOutputDirectory) !== normalizeSafe(schemaPath) && (isCombined.value || separateFactoryNames.length > 0)) {
|
|
5582
|
+
const factoryIndexFilePath = nodePath.join(factoryOutputDirectory, `index.ts`);
|
|
5583
|
+
await fs$1.ensureFile(factoryIndexFilePath);
|
|
5584
|
+
const factoryExports = [];
|
|
5585
|
+
if (isCombined.value) {
|
|
5586
|
+
const factoryFileName = conventionName("factoryMethods", namingConvention);
|
|
5587
|
+
factoryExports.push(`export * from './${factoryFileName}${ext}';`);
|
|
5588
|
+
}
|
|
5589
|
+
for (const fName of separateFactoryNames) factoryExports.push(`export * from './${fName}${ext}';`);
|
|
5590
|
+
await writeGeneratedFile(factoryIndexFilePath, `${header}\n${factoryExports.join("\n")}\n`);
|
|
5591
|
+
} else {
|
|
5592
|
+
if (isCombined.value) {
|
|
5593
|
+
const factoryFileName = conventionName("factoryMethods", namingConvention);
|
|
5594
|
+
currentExports.push(`export * from './${factoryFileName}${ext}';`);
|
|
5595
|
+
}
|
|
5596
|
+
for (const fName of separateFactoryNames) currentExports.push(`export * from './${fName}${ext}';`);
|
|
5597
|
+
}
|
|
5598
|
+
await writeGeneratedFile(schemaFilePath, `${header}\n${[...new Set(currentExports)].toSorted((a, b) => a.localeCompare(b, "en", { numeric: true })).join("\n")}\n`);
|
|
4725
5599
|
} catch (error) {
|
|
4726
5600
|
throw new Error(`Oups... 🍻. An Error occurred while writing schema index file ${schemaFilePath} => ${String(error)}`, { cause: error });
|
|
4727
5601
|
}
|
|
@@ -4731,6 +5605,13 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
|
|
|
4731
5605
|
//#region src/writers/generate-imports-for-builder.ts
|
|
4732
5606
|
function generateImportsForBuilder(output, imports, relativeSchemasPath) {
|
|
4733
5607
|
const isZodSchemaOutput = isObject(output.schemas) && output.schemas.type === "zod";
|
|
5608
|
+
const schemaFactoryImports = imports.filter((i) => i.schemaFactory);
|
|
5609
|
+
const schemaFactoryImportExtension = getImportExtension(output.fileExtension, output.tsconfig);
|
|
5610
|
+
const schemaFactoryDeps = schemaFactoryImports.length > 0 ? [{
|
|
5611
|
+
exports: uniqueBy(schemaFactoryImports, (entry) => `${entry.name}|${entry.alias ?? ""}`),
|
|
5612
|
+
dependency: joinSafe(relativeSchemasPath, `index.faker${schemaFactoryImportExtension}`)
|
|
5613
|
+
}] : [];
|
|
5614
|
+
imports = imports.filter((i) => !i.schemaFactory);
|
|
4734
5615
|
let schemaImports;
|
|
4735
5616
|
if (output.indexFiles) schemaImports = isZodSchemaOutput ? [{
|
|
4736
5617
|
exports: imports.filter((i) => !i.importPath),
|
|
@@ -4757,7 +5638,11 @@ function generateImportsForBuilder(output, imports, relativeSchemasPath) {
|
|
|
4757
5638
|
dependency: i.importPath
|
|
4758
5639
|
};
|
|
4759
5640
|
});
|
|
4760
|
-
return [
|
|
5641
|
+
return [
|
|
5642
|
+
...schemaImports,
|
|
5643
|
+
...schemaFactoryDeps,
|
|
5644
|
+
...otherImports
|
|
5645
|
+
];
|
|
4761
5646
|
}
|
|
4762
5647
|
//#endregion
|
|
4763
5648
|
//#region src/writers/mock-outputs.ts
|
|
@@ -5475,6 +6360,6 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema,
|
|
|
5475
6360
|
}))).flat();
|
|
5476
6361
|
}
|
|
5477
6362
|
//#endregion
|
|
5478
|
-
export { BODY_TYPE_NAME, DefaultTag, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NAMED_COMPONENT_SECTIONS, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, SupportedFormatter, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, addDependency, asyncReduce, buildAngularParamsFilterExpression, camel, collectReferencedComponents, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, escapeRegExp, filterByContentType, filteredVerbs, fixCrossDirectoryImports, fixRegularSchemaImports, generalJSTypes, generalJSTypesWithArray, generateAxiosOptions, generateBodyMutatorConfig, generateBodyOptions, generateComponentDefinition, generateDependencyImports, generateFormDataAndUrlEncodedFunction, generateImports, generateModelInline, generateModelsInline, generateMutator, generateMutatorConfig, generateMutatorImports, generateMutatorRequestOptions, generateOptions, generateParameterDefinition, generateQueryParamsAxiosConfig, generateSchemasDefinition, generateTarget, generateTargetForTags, generateVerbImports, generateVerbOptions, generateVerbsOptions, getAngularFilteredParamsCallExpression, getAngularFilteredParamsExpression, getAngularFilteredParamsHelperBody, getArray, getBaseUrlRuntimeImports, getBodiesByContentType, getBody, getCombinedEnumValue, getDefaultContentType, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, getEnumUnionFromSchema, getExtension, getFileInfo, getFormDataFieldFileType, getFullRoute, getImportExtension, getIsBodyVerb, getKey, getMockFileExtensionByTypeName, getNumberWord, getObject, getOperationId, getOrvalGeneratedTypes, getParameters, getParams, getParamsInPath, getPropertySafe, getProps, getQueryParams, getRefInfo, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getSuccessResponseType, getTypedResponse, getWarningCount, isBinaryContentType, isBinaryScalarSchema, isBoolean, isComponentRef, isDirectory, isFakerMock, isFunction, isModule, isMswMock, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, isVerbose, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, logVerbose, logWarning, makeRouteSafe, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resetWarnings, resolveDiscriminators, resolveExampleRefs, resolveInstalledVersion, resolveInstalledVersions, resolveObject, resolveRef, resolveValue, sanitize, setVerbose, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, wrapRouteParameters, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
|
|
6363
|
+
export { BODY_TYPE_NAME, DefaultTag, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NAMED_COMPONENT_SECTIONS, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, SupportedFormatter, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, addDependency, asyncReduce, buildAngularParamsFilterExpression, buildDynamicScope, camel, collectReferencedComponents, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicAnchorToParamName, dynamicAnchorsToUniqueParamNames, dynamicImport, escape, escapeRegExp, extractBoundAliasInfo, filterByContentType, filteredVerbs, fixCrossDirectoryImports, fixRegularSchemaImports, generalJSTypes, generalJSTypesWithArray, generateAxiosOptions, generateBodyMutatorConfig, generateBodyOptions, generateComponentDefinition, generateDependencyImports, generateFactory, generateFormDataAndUrlEncodedFunction, generateImports, generateModelInline, generateModelsInline, generateMutator, generateMutatorConfig, generateMutatorImports, generateMutatorRequestOptions, generateOptions, generateParameterDefinition, generateQueryParamsAxiosConfig, generateSchemasDefinition, generateTarget, generateTargetForTags, generateVerbImports, generateVerbOptions, generateVerbsOptions, getAngularFilteredParamsCallExpression, getAngularFilteredParamsExpression, getAngularFilteredParamsHelperBody, getArray, getBaseUrlRuntimeImports, getBodiesByContentType, getBody, getCombinedEnumValue, getDefaultContentType, getDynamicAnchorName, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, getEnumUnionFromSchema, getExtension, getFileInfo, getFormDataFieldFileType, getFullRoute, getImportExtension, getIsBodyVerb, getKey, getMockFileExtensionByTypeName, getNumberWord, getObject, getOperationId, getOrvalGeneratedTypes, getParameters, getParams, getParamsInPath, getPropertySafe, getProps, getQueryParams, getRefInfo, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getSuccessResponseType, getTypedResponse, getWarningCount, isBinaryContentType, isBinaryScalarSchema, isBoolean, isComponentRef, isDirectory, isDynamicReference, isFakerMock, isFunction, isModule, isMswMock, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, isVerbose, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, logVerbose, logWarning, makeRouteSafe, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resetWarnings, resolveDiscriminators, resolveDynamicRef, resolveExampleRefs, resolveInstalledVersion, resolveInstalledVersions, resolveObject, resolveRef, resolveValue, sanitize, setVerbose, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, wrapRouteParameters, writeGeneratedFile, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
|
|
5479
6364
|
|
|
5480
6365
|
//# sourceMappingURL=index.mjs.map
|