@kubb/plugin-ts 5.0.0-beta.99 → 5.0.1
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.cjs +244 -231
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +244 -231
- package/dist/index.js.map +1 -1
- package/package.json +17 -8
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as __name, t as __exportAll } from "./rolldown-runtime-CNktS9qV.js";
|
|
2
2
|
import { parserTs } from "@kubb/parser-ts";
|
|
3
3
|
import { File, jsxRenderer } from "kubb/jsx";
|
|
4
|
-
import { Resolver, ast, createResolver, defineGenerator, definePlugin } from "kubb/kit";
|
|
4
|
+
import { Resolver, ast, createResolver, defineGenerator, definePlugin, isStringType, syncSchemaRef } from "kubb/kit";
|
|
5
5
|
import ts from "typescript";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
|
|
7
7
|
//#region ../../internals/utils/src/casing.ts
|
|
@@ -817,7 +817,7 @@ function buildTupleNode(node, print) {
|
|
|
817
817
|
*/
|
|
818
818
|
function buildPropertyType(schema, baseType, optionalType, optional) {
|
|
819
819
|
const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType);
|
|
820
|
-
const meta =
|
|
820
|
+
const meta = syncSchemaRef(schema);
|
|
821
821
|
let type = baseType;
|
|
822
822
|
if (meta.nullable) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] });
|
|
823
823
|
if ((optional || meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
|
|
@@ -911,6 +911,115 @@ function Enum({ node, enum: enumOptions, resolver }) {
|
|
|
911
911
|
})] });
|
|
912
912
|
}
|
|
913
913
|
//#endregion
|
|
914
|
+
//#region src/utils.ts
|
|
915
|
+
/**
|
|
916
|
+
* Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
|
|
917
|
+
* rather than a named enum reference or a runtime enum declaration.
|
|
918
|
+
*
|
|
919
|
+
* The parser folds `const` into a single-value enum node. The adapter decides which schemas are
|
|
920
|
+
* named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
|
|
921
|
+
* example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
|
|
922
|
+
* a named enum, which keeps the declaration and its references in sync across adapter versions.
|
|
923
|
+
*/
|
|
924
|
+
function isInlineConstEnum(node, enumSchemaNames) {
|
|
925
|
+
return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Collects JSDoc annotation strings for a schema node.
|
|
929
|
+
*
|
|
930
|
+
* Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
|
|
931
|
+
* Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
|
|
932
|
+
|
|
933
|
+
*/
|
|
934
|
+
function isSchemaOptional(schema) {
|
|
935
|
+
return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
|
|
936
|
+
}
|
|
937
|
+
function formatExample(value) {
|
|
938
|
+
if (value === null || typeof value !== "object") return String(value);
|
|
939
|
+
return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
|
|
940
|
+
}
|
|
941
|
+
function buildPropertyJSDocComments(schema, optional) {
|
|
942
|
+
const meta = syncSchemaRef(schema);
|
|
943
|
+
const isArray = meta?.primitive === "array";
|
|
944
|
+
const hasDescription = meta && "description" in meta && meta.description;
|
|
945
|
+
const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
|
|
946
|
+
const exampleValues = meta?.examples ?? [];
|
|
947
|
+
const comments = [
|
|
948
|
+
hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
|
|
949
|
+
...formatComment,
|
|
950
|
+
meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
|
|
951
|
+
!isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
|
|
952
|
+
!isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
|
|
953
|
+
meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
|
|
954
|
+
meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" && typeof meta.default === "string" ? stringify(meta.default) : meta.default}` : null,
|
|
955
|
+
...exampleValues.map((example) => `@example ${formatExample(example)}`)
|
|
956
|
+
].filter(Boolean);
|
|
957
|
+
const typeTag = comments.length && meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null;
|
|
958
|
+
return [...comments, typeTag].filter(Boolean);
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Builds the object schema for a group of parameters sharing one `in` location (path, query, or
|
|
962
|
+
* header), embedding each param's own schema (and JSDoc) directly rather than referencing a
|
|
963
|
+
* separate per-param type — the group itself is the only type these params get exported as.
|
|
964
|
+
*/
|
|
965
|
+
function buildParams({ params }) {
|
|
966
|
+
return ast.factory.createSchema({
|
|
967
|
+
type: "object",
|
|
968
|
+
properties: params.map((param) => ast.factory.createProperty({
|
|
969
|
+
name: param.name,
|
|
970
|
+
required: param.required,
|
|
971
|
+
schema: ast.factory.createSchema({
|
|
972
|
+
...param.schema,
|
|
973
|
+
optional: !param.required
|
|
974
|
+
})
|
|
975
|
+
}))
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function buildResponseUnion(node, { resolver }) {
|
|
979
|
+
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
980
|
+
if (responsesWithSchema.length === 0) return null;
|
|
981
|
+
return ast.factory.createSchema({
|
|
982
|
+
type: "union",
|
|
983
|
+
members: responsesWithSchema.map((res) => ast.factory.createSchema({
|
|
984
|
+
type: "ref",
|
|
985
|
+
name: resolver.response.status(node, res.statusCode)
|
|
986
|
+
}))
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
//#endregion
|
|
990
|
+
//#region src/components/Type.tsx
|
|
991
|
+
function Type({ name, node, printer, enum: enumOptions, resolver }) {
|
|
992
|
+
const enumSchemaNodes = ast.collectSync(node, { schema(n) {
|
|
993
|
+
const enumNode = ast.narrowSchema(n, ast.schemaTypes.enum);
|
|
994
|
+
if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
|
|
995
|
+
} });
|
|
996
|
+
const output = printer.print(node);
|
|
997
|
+
if (!output) return;
|
|
998
|
+
const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
|
|
999
|
+
return {
|
|
1000
|
+
node,
|
|
1001
|
+
...getEnumNames({
|
|
1002
|
+
node,
|
|
1003
|
+
enum: enumOptions,
|
|
1004
|
+
resolver
|
|
1005
|
+
})
|
|
1006
|
+
};
|
|
1007
|
+
});
|
|
1008
|
+
const shouldExportEnums = enumOptions.type !== "inlineLiteral";
|
|
1009
|
+
const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
|
|
1010
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ jsx(Enum, {
|
|
1011
|
+
node,
|
|
1012
|
+
enum: enumOptions,
|
|
1013
|
+
resolver
|
|
1014
|
+
}, node.name)), shouldExportType && /* @__PURE__ */ jsx(File.Source, {
|
|
1015
|
+
name,
|
|
1016
|
+
isTypeOnly: true,
|
|
1017
|
+
isExportable: true,
|
|
1018
|
+
isIndexable: true,
|
|
1019
|
+
children: output
|
|
1020
|
+
})] });
|
|
1021
|
+
}
|
|
1022
|
+
//#endregion
|
|
914
1023
|
//#region ../../internals/shared/src/params.ts
|
|
915
1024
|
/**
|
|
916
1025
|
* Drops parameters that share the same name, keeping the first.
|
|
@@ -978,16 +1087,137 @@ function resolveContentTypeVariants(entries, baseName) {
|
|
|
978
1087
|
};
|
|
979
1088
|
});
|
|
980
1089
|
}
|
|
1090
|
+
const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
1091
|
+
/**
|
|
1092
|
+
* Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
|
|
1093
|
+
* group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
|
|
1094
|
+
* (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
|
|
1095
|
+
* same parameters once per plugin.
|
|
1096
|
+
*/
|
|
981
1097
|
function getOperationParameters(node) {
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
})),
|
|
1098
|
+
const cached = operationParameterGroupsByNode.get(node);
|
|
1099
|
+
if (cached) return cached;
|
|
1100
|
+
const groups = {
|
|
1101
|
+
path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
|
|
987
1102
|
query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
|
|
988
1103
|
header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
|
|
989
1104
|
cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
|
|
990
1105
|
};
|
|
1106
|
+
operationParameterGroupsByNode.set(node, groups);
|
|
1107
|
+
return groups;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Builds the combined `{ body, path, query, headers }` options object schema for an operation,
|
|
1111
|
+
* referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s
|
|
1112
|
+
* `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same
|
|
1113
|
+
* shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
|
|
1114
|
+
* printer to emit `z.object(…)` rather than a record.
|
|
1115
|
+
*/
|
|
1116
|
+
function buildOptionsSchema(node, resolver) {
|
|
1117
|
+
const { path, query, header } = getOperationParameters(node);
|
|
1118
|
+
const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
1119
|
+
const createNever = () => ast.factory.createSchema({
|
|
1120
|
+
type: "never",
|
|
1121
|
+
primitive: void 0,
|
|
1122
|
+
optional: true
|
|
1123
|
+
});
|
|
1124
|
+
const groups = [
|
|
1125
|
+
{
|
|
1126
|
+
name: "path",
|
|
1127
|
+
params: path,
|
|
1128
|
+
resolve: resolver.param.path
|
|
1129
|
+
},
|
|
1130
|
+
{
|
|
1131
|
+
name: "query",
|
|
1132
|
+
params: query,
|
|
1133
|
+
resolve: resolver.param.query
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
name: "headers",
|
|
1137
|
+
params: header,
|
|
1138
|
+
resolve: resolver.param.headers
|
|
1139
|
+
}
|
|
1140
|
+
];
|
|
1141
|
+
return ast.factory.createSchema({
|
|
1142
|
+
type: "object",
|
|
1143
|
+
primitive: "object",
|
|
1144
|
+
deprecated: node.deprecated,
|
|
1145
|
+
properties: [ast.factory.createProperty({
|
|
1146
|
+
name: "body",
|
|
1147
|
+
required: hasBody,
|
|
1148
|
+
schema: hasBody ? ast.factory.createSchema({
|
|
1149
|
+
type: "ref",
|
|
1150
|
+
name: resolver.response.body(node)
|
|
1151
|
+
}) : createNever()
|
|
1152
|
+
}), ...groups.map(({ name, params, resolve }) => {
|
|
1153
|
+
const required = params.some((param) => param.required);
|
|
1154
|
+
return ast.factory.createProperty({
|
|
1155
|
+
name,
|
|
1156
|
+
required,
|
|
1157
|
+
schema: params.length > 0 ? ast.factory.createSchema({
|
|
1158
|
+
type: "ref",
|
|
1159
|
+
name: resolve.call(resolver.param, node, params[0]),
|
|
1160
|
+
optional: !required
|
|
1161
|
+
}) : createNever()
|
|
1162
|
+
});
|
|
1163
|
+
})]
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* The schema a status occupies in the `<Name>Responses` record. A status that documents several
|
|
1168
|
+
* content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
|
|
1169
|
+
* on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
|
|
1170
|
+
* query hooks and `result.data` use.
|
|
1171
|
+
*/
|
|
1172
|
+
function buildResponseRecordEntry(node, res, resolver) {
|
|
1173
|
+
const statusName = resolver.response.status(node, res.statusCode);
|
|
1174
|
+
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1175
|
+
if (variants.length <= 1) return ast.factory.createSchema({
|
|
1176
|
+
type: "ref",
|
|
1177
|
+
name: statusName
|
|
1178
|
+
});
|
|
1179
|
+
return ast.factory.createSchema({
|
|
1180
|
+
type: "union",
|
|
1181
|
+
members: resolveContentTypeVariants(variants, statusName).map((variant) => ast.factory.createSchema({
|
|
1182
|
+
type: "object",
|
|
1183
|
+
primitive: "object",
|
|
1184
|
+
properties: [ast.factory.createProperty({
|
|
1185
|
+
name: "contentType",
|
|
1186
|
+
required: true,
|
|
1187
|
+
schema: ast.factory.createSchema({
|
|
1188
|
+
type: "enum",
|
|
1189
|
+
enumValues: [variant.contentType]
|
|
1190
|
+
})
|
|
1191
|
+
}), ast.factory.createProperty({
|
|
1192
|
+
name: "data",
|
|
1193
|
+
required: true,
|
|
1194
|
+
schema: ast.factory.createSchema({
|
|
1195
|
+
type: "ref",
|
|
1196
|
+
name: variant.name
|
|
1197
|
+
})
|
|
1198
|
+
})]
|
|
1199
|
+
}))
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved
|
|
1204
|
+
* `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s
|
|
1205
|
+
* inferred responses schema, so both emit the same shape from the same inputs.
|
|
1206
|
+
*
|
|
1207
|
+
* Always emits the keyed record, even when an operation declares no responses. An operation with no
|
|
1208
|
+
* responses renders as an empty object, which keeps every consumer's import (for example the axios
|
|
1209
|
+
* SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.
|
|
1210
|
+
*/
|
|
1211
|
+
function buildResponses(node, resolver) {
|
|
1212
|
+
return ast.factory.createSchema({
|
|
1213
|
+
type: "object",
|
|
1214
|
+
primitive: "object",
|
|
1215
|
+
properties: node.responses.map((res) => ast.factory.createProperty({
|
|
1216
|
+
name: String(res.statusCode),
|
|
1217
|
+
required: true,
|
|
1218
|
+
schema: buildResponseRecordEntry(node, res, resolver)
|
|
1219
|
+
}))
|
|
1220
|
+
});
|
|
991
1221
|
}
|
|
992
1222
|
//#endregion
|
|
993
1223
|
//#region ../../internals/shared/src/resolver.ts
|
|
@@ -1072,7 +1302,7 @@ function createCasedFile(caseLast) {
|
|
|
1072
1302
|
* `resolver.imports` would resolve file paths that are then discarded.
|
|
1073
1303
|
*/
|
|
1074
1304
|
function collectRefNames(schema) {
|
|
1075
|
-
return ast.
|
|
1305
|
+
return ast.collectSync(schema, { schema: (node) => {
|
|
1076
1306
|
const refNode = ast.narrowSchema(node, "ref");
|
|
1077
1307
|
if (!refNode?.ref) return null;
|
|
1078
1308
|
return ast.resolveRefName(refNode);
|
|
@@ -1139,223 +1369,6 @@ function mapSchemaItems(node, transform) {
|
|
|
1139
1369
|
}));
|
|
1140
1370
|
}
|
|
1141
1371
|
//#endregion
|
|
1142
|
-
//#region src/utils.ts
|
|
1143
|
-
/**
|
|
1144
|
-
* Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
|
|
1145
|
-
* rather than a named enum reference or a runtime enum declaration.
|
|
1146
|
-
*
|
|
1147
|
-
* The parser folds `const` into a single-value enum node. The adapter decides which schemas are
|
|
1148
|
-
* named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
|
|
1149
|
-
* example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
|
|
1150
|
-
* a named enum, which keeps the declaration and its references in sync across adapter versions.
|
|
1151
|
-
*/
|
|
1152
|
-
function isInlineConstEnum(node, enumSchemaNames) {
|
|
1153
|
-
return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
|
|
1154
|
-
}
|
|
1155
|
-
/**
|
|
1156
|
-
* Collects JSDoc annotation strings for a schema node.
|
|
1157
|
-
*
|
|
1158
|
-
* Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
|
|
1159
|
-
* Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
|
|
1160
|
-
|
|
1161
|
-
*/
|
|
1162
|
-
function isSchemaOptional(schema) {
|
|
1163
|
-
return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
|
|
1164
|
-
}
|
|
1165
|
-
function formatExample(value) {
|
|
1166
|
-
if (value === null || typeof value !== "object") return String(value);
|
|
1167
|
-
return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
|
|
1168
|
-
}
|
|
1169
|
-
function buildPropertyJSDocComments(schema, optional) {
|
|
1170
|
-
const meta = ast.syncSchemaRef(schema);
|
|
1171
|
-
const isArray = meta?.primitive === "array";
|
|
1172
|
-
const hasDescription = meta && "description" in meta && meta.description;
|
|
1173
|
-
const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
|
|
1174
|
-
const exampleValues = meta?.examples ?? [];
|
|
1175
|
-
return [
|
|
1176
|
-
hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
|
|
1177
|
-
...formatComment,
|
|
1178
|
-
meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
|
|
1179
|
-
!isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
|
|
1180
|
-
!isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
|
|
1181
|
-
meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
|
|
1182
|
-
meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" && typeof meta.default === "string" ? stringify(meta.default) : meta.default}` : null,
|
|
1183
|
-
...exampleValues.map((example) => `@example ${formatExample(example)}`),
|
|
1184
|
-
meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
|
|
1185
|
-
].filter(Boolean);
|
|
1186
|
-
}
|
|
1187
|
-
/**
|
|
1188
|
-
* Builds the object schema for a group of parameters sharing one `in` location (path, query, or
|
|
1189
|
-
* header), embedding each param's own schema (and JSDoc) directly rather than referencing a
|
|
1190
|
-
* separate per-param type — the group itself is the only type these params get exported as.
|
|
1191
|
-
*/
|
|
1192
|
-
function buildParams({ params }) {
|
|
1193
|
-
return ast.factory.createSchema({
|
|
1194
|
-
type: "object",
|
|
1195
|
-
properties: params.map((param) => ast.factory.createProperty({
|
|
1196
|
-
name: param.name,
|
|
1197
|
-
required: param.required,
|
|
1198
|
-
schema: ast.factory.createSchema({
|
|
1199
|
-
...param.schema,
|
|
1200
|
-
optional: !param.required
|
|
1201
|
-
})
|
|
1202
|
-
}))
|
|
1203
|
-
});
|
|
1204
|
-
}
|
|
1205
|
-
function buildOptions(node, { resolver }) {
|
|
1206
|
-
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
1207
|
-
const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
1208
|
-
const hasRequiredPath = pathParams.some((param) => param.required);
|
|
1209
|
-
const hasRequiredQuery = queryParams.some((param) => param.required);
|
|
1210
|
-
const hasRequiredHeader = headerParams.some((param) => param.required);
|
|
1211
|
-
return ast.factory.createSchema({
|
|
1212
|
-
type: "object",
|
|
1213
|
-
deprecated: node.deprecated,
|
|
1214
|
-
properties: [
|
|
1215
|
-
ast.factory.createProperty({
|
|
1216
|
-
name: "body",
|
|
1217
|
-
required: hasBody,
|
|
1218
|
-
schema: hasBody ? ast.factory.createSchema({
|
|
1219
|
-
type: "ref",
|
|
1220
|
-
name: resolver.response.body(node)
|
|
1221
|
-
}) : ast.factory.createSchema({
|
|
1222
|
-
type: "never",
|
|
1223
|
-
primitive: void 0,
|
|
1224
|
-
optional: true
|
|
1225
|
-
})
|
|
1226
|
-
}),
|
|
1227
|
-
ast.factory.createProperty({
|
|
1228
|
-
name: "path",
|
|
1229
|
-
required: hasRequiredPath,
|
|
1230
|
-
schema: pathParams.length > 0 ? ast.factory.createSchema({
|
|
1231
|
-
type: "ref",
|
|
1232
|
-
name: resolver.param.path(node, pathParams[0]),
|
|
1233
|
-
optional: !hasRequiredPath
|
|
1234
|
-
}) : ast.factory.createSchema({
|
|
1235
|
-
type: "never",
|
|
1236
|
-
primitive: void 0,
|
|
1237
|
-
optional: true
|
|
1238
|
-
})
|
|
1239
|
-
}),
|
|
1240
|
-
ast.factory.createProperty({
|
|
1241
|
-
name: "query",
|
|
1242
|
-
required: hasRequiredQuery,
|
|
1243
|
-
schema: queryParams.length > 0 ? ast.factory.createSchema({
|
|
1244
|
-
type: "ref",
|
|
1245
|
-
name: resolver.param.query(node, queryParams[0]),
|
|
1246
|
-
optional: !hasRequiredQuery
|
|
1247
|
-
}) : ast.factory.createSchema({
|
|
1248
|
-
type: "never",
|
|
1249
|
-
primitive: void 0,
|
|
1250
|
-
optional: true
|
|
1251
|
-
})
|
|
1252
|
-
}),
|
|
1253
|
-
ast.factory.createProperty({
|
|
1254
|
-
name: "headers",
|
|
1255
|
-
required: hasRequiredHeader,
|
|
1256
|
-
schema: headerParams.length > 0 ? ast.factory.createSchema({
|
|
1257
|
-
type: "ref",
|
|
1258
|
-
name: resolver.param.headers(node, headerParams[0]),
|
|
1259
|
-
optional: !hasRequiredHeader
|
|
1260
|
-
}) : ast.factory.createSchema({
|
|
1261
|
-
type: "never",
|
|
1262
|
-
primitive: void 0,
|
|
1263
|
-
optional: true
|
|
1264
|
-
})
|
|
1265
|
-
})
|
|
1266
|
-
]
|
|
1267
|
-
});
|
|
1268
|
-
}
|
|
1269
|
-
/**
|
|
1270
|
-
* The schema a status occupies in the `<Name>Responses` record. A status that documents several
|
|
1271
|
-
* content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
|
|
1272
|
-
* on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
|
|
1273
|
-
* query hooks and `result.data` use.
|
|
1274
|
-
*/
|
|
1275
|
-
function buildResponseRecordEntry(node, res, resolver) {
|
|
1276
|
-
const statusName = resolver.response.status(node, res.statusCode);
|
|
1277
|
-
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1278
|
-
if (variants.length <= 1) return ast.factory.createSchema({
|
|
1279
|
-
type: "ref",
|
|
1280
|
-
name: statusName
|
|
1281
|
-
});
|
|
1282
|
-
return ast.factory.createSchema({
|
|
1283
|
-
type: "union",
|
|
1284
|
-
members: resolveContentTypeVariants(variants, statusName).map((variant) => ast.factory.createSchema({
|
|
1285
|
-
type: "object",
|
|
1286
|
-
properties: [ast.factory.createProperty({
|
|
1287
|
-
name: "contentType",
|
|
1288
|
-
required: true,
|
|
1289
|
-
schema: ast.factory.createSchema({
|
|
1290
|
-
type: "enum",
|
|
1291
|
-
enumValues: [variant.contentType]
|
|
1292
|
-
})
|
|
1293
|
-
}), ast.factory.createProperty({
|
|
1294
|
-
name: "data",
|
|
1295
|
-
required: true,
|
|
1296
|
-
schema: ast.factory.createSchema({
|
|
1297
|
-
type: "ref",
|
|
1298
|
-
name: variant.name
|
|
1299
|
-
})
|
|
1300
|
-
})]
|
|
1301
|
-
}))
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
|
-
function buildResponses(node, { resolver }) {
|
|
1305
|
-
return ast.factory.createSchema({
|
|
1306
|
-
type: "object",
|
|
1307
|
-
properties: node.responses.map((res) => ast.factory.createProperty({
|
|
1308
|
-
name: String(res.statusCode),
|
|
1309
|
-
required: true,
|
|
1310
|
-
schema: buildResponseRecordEntry(node, res, resolver)
|
|
1311
|
-
}))
|
|
1312
|
-
});
|
|
1313
|
-
}
|
|
1314
|
-
function buildResponseUnion(node, { resolver }) {
|
|
1315
|
-
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
1316
|
-
if (responsesWithSchema.length === 0) return null;
|
|
1317
|
-
return ast.factory.createSchema({
|
|
1318
|
-
type: "union",
|
|
1319
|
-
members: responsesWithSchema.map((res) => ast.factory.createSchema({
|
|
1320
|
-
type: "ref",
|
|
1321
|
-
name: resolver.response.status(node, res.statusCode)
|
|
1322
|
-
}))
|
|
1323
|
-
});
|
|
1324
|
-
}
|
|
1325
|
-
//#endregion
|
|
1326
|
-
//#region src/components/Type.tsx
|
|
1327
|
-
function Type({ name, node, printer, enum: enumOptions, resolver }) {
|
|
1328
|
-
const enumSchemaNodes = ast.collect(node, { schema(n) {
|
|
1329
|
-
const enumNode = ast.narrowSchema(n, ast.schemaTypes.enum);
|
|
1330
|
-
if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
|
|
1331
|
-
} });
|
|
1332
|
-
const output = printer.print(node);
|
|
1333
|
-
if (!output) return;
|
|
1334
|
-
const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
|
|
1335
|
-
return {
|
|
1336
|
-
node,
|
|
1337
|
-
...getEnumNames({
|
|
1338
|
-
node,
|
|
1339
|
-
enum: enumOptions,
|
|
1340
|
-
resolver
|
|
1341
|
-
})
|
|
1342
|
-
};
|
|
1343
|
-
});
|
|
1344
|
-
const shouldExportEnums = enumOptions.type !== "inlineLiteral";
|
|
1345
|
-
const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
|
|
1346
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ jsx(Enum, {
|
|
1347
|
-
node,
|
|
1348
|
-
enum: enumOptions,
|
|
1349
|
-
resolver
|
|
1350
|
-
}, node.name)), shouldExportType && /* @__PURE__ */ jsx(File.Source, {
|
|
1351
|
-
name,
|
|
1352
|
-
isTypeOnly: true,
|
|
1353
|
-
isExportable: true,
|
|
1354
|
-
isIndexable: true,
|
|
1355
|
-
children: output
|
|
1356
|
-
})] });
|
|
1357
|
-
}
|
|
1358
|
-
//#endregion
|
|
1359
1372
|
//#region src/printers/printerTs.ts
|
|
1360
1373
|
const { isNonNullable } = factory_exports;
|
|
1361
1374
|
/**
|
|
@@ -1430,11 +1443,11 @@ const printerTs = ast.createPrinter((options) => {
|
|
|
1430
1443
|
const hasStringLiteral = members.some((m) => {
|
|
1431
1444
|
return ast.narrowSchema(m, ast.schemaTypes.enum)?.primitive === "string";
|
|
1432
1445
|
});
|
|
1433
|
-
const hasPlainString = members.some((m) =>
|
|
1446
|
+
const hasPlainString = members.some((m) => isStringType(m));
|
|
1434
1447
|
if (hasStringLiteral && hasPlainString) return createUnionDeclaration({
|
|
1435
1448
|
withParentheses: true,
|
|
1436
1449
|
nodes: members.map((m) => {
|
|
1437
|
-
if (
|
|
1450
|
+
if (isStringType(m)) return createIntersectionDeclaration({
|
|
1438
1451
|
nodes: [keywordTypeNodes.string, createTypeLiteralNode([])],
|
|
1439
1452
|
withParentheses: true
|
|
1440
1453
|
});
|
|
@@ -1468,7 +1481,7 @@ const printerTs = ast.createPrinter((options) => {
|
|
|
1468
1481
|
const baseType = output ?? keywordTypeNodes.unknown;
|
|
1469
1482
|
const optional = !property.required || !!property.schema.optional || !!property.schema.nullish;
|
|
1470
1483
|
const type = buildPropertyType(property.schema, baseType, options.optionalType, optional);
|
|
1471
|
-
const propMeta =
|
|
1484
|
+
const propMeta = syncSchemaRef(property.schema);
|
|
1472
1485
|
return appendJSDocToNode({
|
|
1473
1486
|
node: createPropertySignature({
|
|
1474
1487
|
questionToken: optional ? addsQuestionToken : false,
|
|
@@ -1489,7 +1502,7 @@ const printerTs = ast.createPrinter((options) => {
|
|
|
1489
1502
|
const { name, syntaxType = "type", description, keysToOmit } = this.options;
|
|
1490
1503
|
const transformed = this.transform(node);
|
|
1491
1504
|
if (!transformed) return null;
|
|
1492
|
-
const meta =
|
|
1505
|
+
const meta = syncSchemaRef(node);
|
|
1493
1506
|
if (!name) {
|
|
1494
1507
|
const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [transformed, keywordTypeNodes.null] }) : transformed;
|
|
1495
1508
|
const result = (meta.nullish || meta.optional) && addsUndefined ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
|
|
@@ -1745,11 +1758,11 @@ const typeGenerator = defineGenerator({
|
|
|
1745
1758
|
});
|
|
1746
1759
|
});
|
|
1747
1760
|
const optionsType = renderSchemaType({
|
|
1748
|
-
schema:
|
|
1761
|
+
schema: buildOptionsSchema(node, resolver),
|
|
1749
1762
|
name: resolver.response.options(node)
|
|
1750
1763
|
});
|
|
1751
1764
|
const responsesType = renderSchemaType({
|
|
1752
|
-
schema: buildResponses(node,
|
|
1765
|
+
schema: buildResponses(node, resolver),
|
|
1753
1766
|
name: resolver.response.responses(node)
|
|
1754
1767
|
});
|
|
1755
1768
|
function buildResponseType() {
|