@kubb/ast 5.0.0-beta.61 → 5.0.0-beta.63
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/{defineMacro-BLIR6k-j.d.ts → defineMacro-BATi7xoC.d.ts} +11 -20
- package/dist/{defineMacro-Bv9R_9a2.cjs → defineMacro-CEZHaCXE.cjs} +13 -21
- package/dist/defineMacro-CEZHaCXE.cjs.map +1 -0
- package/dist/{defineMacro-BTXvS8nI.js → defineMacro-Shz8f6SG.js} +12 -19
- package/dist/defineMacro-Shz8f6SG.js.map +1 -0
- package/dist/factory.cjs +88 -28
- package/dist/factory.cjs.map +1 -0
- package/dist/factory.d.ts +6 -41
- package/dist/factory.js +58 -3
- package/dist/factory.js.map +1 -0
- package/dist/{index-BzjwdK2M.d.ts → index-B9cc8MBS.d.ts} +164 -194
- package/dist/index.cjs +144 -274
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +43 -37
- package/dist/index.js +109 -235
- package/dist/index.js.map +1 -1
- package/dist/macros.cjs +41 -28
- package/dist/macros.cjs.map +1 -1
- package/dist/macros.d.ts +14 -12
- package/dist/macros.js +34 -21
- package/dist/macros.js.map +1 -1
- package/dist/{operationParams-BZ07xDm0.d.ts → operationParams-k5CKwSWZ.d.ts} +9 -2
- package/dist/{visitor-DJ6ZEJvq.js → refs-BjNDuCBD.js} +116 -159
- package/dist/refs-BjNDuCBD.js.map +1 -0
- package/dist/{visitor-DpKZ9Tk0.cjs → refs-u5SDdyV7.cjs} +140 -195
- package/dist/refs-u5SDdyV7.cjs.map +1 -0
- package/dist/{response-hnSw2NKE.cjs → schema-BkvrrOAr.cjs} +496 -231
- package/dist/schema-BkvrrOAr.cjs.map +1 -0
- package/dist/{response-KUdWiDWw.js → schema-Cbnxmz4b.js} +472 -220
- package/dist/schema-Cbnxmz4b.js.map +1 -0
- package/dist/{types-DyDzizSf.d.ts → types-BB_xgRJ3.d.ts} +49 -123
- package/dist/types.d.ts +5 -6
- package/dist/{utils-BLJwyza-.cjs → utils-CEepwqmb.cjs} +76 -69
- package/dist/utils-CEepwqmb.cjs.map +1 -0
- package/dist/{utils-CF_-Pn_c.js → utils-DaXkewb1.js} +58 -51
- package/dist/utils-DaXkewb1.js.map +1 -0
- package/dist/utils.cjs +11 -11
- package/dist/utils.d.ts +53 -30
- package/dist/utils.js +3 -3
- package/package.json +1 -1
- package/src/constants.ts +3 -36
- package/src/defineMacro.ts +20 -13
- package/src/{node.ts → defineNode.ts} +12 -38
- package/src/{printer.ts → definePrinter.ts} +5 -3
- package/src/dialect.ts +52 -19
- package/src/factory.ts +7 -94
- package/src/index.ts +7 -9
- package/src/infer.ts +1 -1
- package/src/macros/macroDiscriminatorEnum.ts +7 -1
- package/src/macros/macroEnumName.ts +10 -2
- package/src/macros/macroSimplifyUnion.ts +29 -19
- package/src/nodes/code.ts +40 -80
- package/src/nodes/content.ts +2 -7
- package/src/nodes/file.ts +107 -18
- package/src/nodes/function.ts +46 -46
- package/src/nodes/index.ts +2 -3
- package/src/nodes/input.ts +13 -9
- package/src/nodes/operation.ts +8 -5
- package/src/nodes/output.ts +1 -1
- package/src/nodes/parameter.ts +5 -5
- package/src/nodes/property.ts +5 -5
- package/src/nodes/requestBody.ts +6 -13
- package/src/nodes/response.ts +93 -8
- package/src/nodes/schema.ts +5 -5
- package/src/optionality.ts +15 -0
- package/src/registry.ts +1 -1
- package/src/types.ts +4 -6
- package/src/utils/codegen.ts +6 -7
- package/src/utils/extractStringsFromNodes.ts +1 -0
- package/src/utils/fileMerge.ts +7 -8
- package/src/utils/operationParams.ts +9 -9
- package/src/utils/refs.ts +14 -18
- package/src/utils/schemaGraph.ts +41 -33
- package/src/utils/schemaMerge.ts +2 -2
- package/src/visitor.ts +3 -14
- package/dist/defineMacro-BTXvS8nI.js.map +0 -1
- package/dist/defineMacro-Bv9R_9a2.cjs.map +0 -1
- package/dist/extractStringsFromNodes-Cja-xxx5.js +0 -29
- package/dist/extractStringsFromNodes-Cja-xxx5.js.map +0 -1
- package/dist/extractStringsFromNodes-DKgDjFO0.cjs +0 -34
- package/dist/extractStringsFromNodes-DKgDjFO0.cjs.map +0 -1
- package/dist/extractStringsFromNodes-p4mX1TQD.d.ts +0 -14
- package/dist/factory-CZNOGI-N.js +0 -283
- package/dist/factory-CZNOGI-N.js.map +0 -1
- package/dist/factory-DG1CVkEb.cjs +0 -300
- package/dist/factory-DG1CVkEb.cjs.map +0 -1
- package/dist/response-KUdWiDWw.js.map +0 -1
- package/dist/response-hnSw2NKE.cjs.map +0 -1
- package/dist/utils-BLJwyza-.cjs.map +0 -1
- package/dist/utils-CF_-Pn_c.js.map +0 -1
- package/dist/visitor-DJ6ZEJvq.js.map +0 -1
- package/dist/visitor-DpKZ9Tk0.cjs.map +0 -1
- package/src/dedupe.ts +0 -239
- package/src/mocks.ts +0 -56
- package/src/nodes/http.ts +0 -85
|
@@ -1,56 +1,49 @@
|
|
|
1
1
|
import "./chunk-CNktS9qV.js";
|
|
2
|
-
|
|
2
|
+
import { hash } from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/defineNode.ts
|
|
5
|
+
/**
|
|
6
|
+
* Visitor callback names, one per traversable node kind, in traversal order.
|
|
7
|
+
* Kept in sync with the keys of `Visitor` in `visitor.ts`.
|
|
8
|
+
*/
|
|
9
|
+
const visitorKeys = [
|
|
10
|
+
"input",
|
|
11
|
+
"output",
|
|
12
|
+
"operation",
|
|
13
|
+
"schema",
|
|
14
|
+
"property",
|
|
15
|
+
"parameter",
|
|
16
|
+
"response"
|
|
17
|
+
];
|
|
3
18
|
/**
|
|
4
19
|
* Builds a type guard that matches nodes of the given `kind`.
|
|
5
20
|
*/
|
|
6
21
|
function isKind(kind) {
|
|
7
|
-
return (node) => node
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Updates a schema's `optional` and `nullish` flags from a parent's `required`
|
|
11
|
-
* value and the schema's own `nullable`. Mirrors how OpenAPI parameters and
|
|
12
|
-
* object properties combine "required" and "nullable" into a single AST.
|
|
13
|
-
*
|
|
14
|
-
* - Non-required + non-nullable → `optional: true`.
|
|
15
|
-
* - Non-required + nullable → `nullish: true`.
|
|
16
|
-
* - Required → both flags cleared.
|
|
17
|
-
*/
|
|
18
|
-
function syncOptionality(schema, required) {
|
|
19
|
-
const nullable = schema.nullable ?? false;
|
|
20
|
-
return {
|
|
21
|
-
...schema,
|
|
22
|
-
optional: !required && !nullable ? true : void 0,
|
|
23
|
-
nullish: !required && nullable ? true : void 0
|
|
24
|
-
};
|
|
22
|
+
return (node) => node?.kind === kind;
|
|
25
23
|
}
|
|
26
24
|
/**
|
|
27
25
|
* Defines a node once and derives its `create` builder, `is` guard, and traversal
|
|
28
26
|
* metadata. `create` merges `defaults`, the `build` hook (or the raw input), and the
|
|
29
27
|
* `kind`, so node construction lives in one place without scattered `as` casts.
|
|
30
28
|
*
|
|
31
|
-
* Set `rebuild: true` when the `build` hook derives fields from children. After a
|
|
32
|
-
* transform rewrites those children, the registry reruns `create` so the derived
|
|
33
|
-
* fields stay correct.
|
|
34
|
-
*
|
|
35
29
|
* @example Simple node
|
|
36
30
|
* ```ts
|
|
37
31
|
* const importDef = defineNode<ImportNode>({ kind: 'Import' })
|
|
38
32
|
* const createImport = importDef.create
|
|
39
33
|
* ```
|
|
40
34
|
*
|
|
41
|
-
* @example Node with a build hook
|
|
35
|
+
* @example Node with a build hook
|
|
42
36
|
* ```ts
|
|
43
37
|
* const propertyDef = defineNode<PropertyNode, UserPropertyNode>({
|
|
44
38
|
* kind: 'Property',
|
|
45
39
|
* build: (props) => ({ ...props, required: props.required ?? false }),
|
|
46
40
|
* children: ['schema'],
|
|
47
41
|
* visitorKey: 'property',
|
|
48
|
-
* rebuild: true,
|
|
49
42
|
* })
|
|
50
43
|
* ```
|
|
51
44
|
*/
|
|
52
45
|
function defineNode(config) {
|
|
53
|
-
const { kind, defaults, build, children, visitorKey
|
|
46
|
+
const { kind, defaults, build, children, visitorKey } = config;
|
|
54
47
|
function create(input) {
|
|
55
48
|
const base = build ? build(input) : input;
|
|
56
49
|
return {
|
|
@@ -64,110 +57,48 @@ function defineNode(config) {
|
|
|
64
57
|
create,
|
|
65
58
|
is: isKind(kind),
|
|
66
59
|
children,
|
|
67
|
-
visitorKey
|
|
68
|
-
rebuild
|
|
60
|
+
visitorKey
|
|
69
61
|
};
|
|
70
62
|
}
|
|
71
63
|
//#endregion
|
|
72
|
-
//#region src/nodes/
|
|
64
|
+
//#region src/nodes/code.ts
|
|
73
65
|
/**
|
|
74
|
-
*
|
|
75
|
-
* Primitive types map to themselves. Special string formats map to `'string'`.
|
|
76
|
-
* Complex types (`ref`, `enum`, `union`, `intersection`, `tuple`, `blob`) are left unset.
|
|
66
|
+
* Definition for the {@link ConstNode}.
|
|
77
67
|
*/
|
|
78
|
-
const
|
|
79
|
-
string: "string",
|
|
80
|
-
number: "number",
|
|
81
|
-
integer: "integer",
|
|
82
|
-
bigint: "bigint",
|
|
83
|
-
boolean: "boolean",
|
|
84
|
-
null: "null",
|
|
85
|
-
any: "any",
|
|
86
|
-
unknown: "unknown",
|
|
87
|
-
void: "void",
|
|
88
|
-
never: "never",
|
|
89
|
-
object: "object",
|
|
90
|
-
array: "array",
|
|
91
|
-
date: "date",
|
|
92
|
-
uuid: "string",
|
|
93
|
-
email: "string",
|
|
94
|
-
url: "string",
|
|
95
|
-
datetime: "string",
|
|
96
|
-
time: "string"
|
|
97
|
-
};
|
|
68
|
+
const constDef = defineNode({ kind: "Const" });
|
|
98
69
|
/**
|
|
99
|
-
* Definition for the {@link
|
|
100
|
-
* empty array, and `primitive` is inferred from `type` when not explicitly provided.
|
|
70
|
+
* Definition for the {@link TypeNode}.
|
|
101
71
|
*/
|
|
102
|
-
const
|
|
103
|
-
kind: "Schema",
|
|
104
|
-
build: (props) => {
|
|
105
|
-
if (props.type === "object") return {
|
|
106
|
-
properties: [],
|
|
107
|
-
primitive: "object",
|
|
108
|
-
...props
|
|
109
|
-
};
|
|
110
|
-
return {
|
|
111
|
-
primitive: TYPE_TO_PRIMITIVE[props.type],
|
|
112
|
-
...props
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
children: [
|
|
116
|
-
"properties",
|
|
117
|
-
"items",
|
|
118
|
-
"members",
|
|
119
|
-
"additionalProperties"
|
|
120
|
-
],
|
|
121
|
-
visitorKey: "schema"
|
|
122
|
-
});
|
|
123
|
-
function createSchema(props) {
|
|
124
|
-
return schemaDef.create(props);
|
|
125
|
-
}
|
|
126
|
-
//#endregion
|
|
127
|
-
//#region ../../internals/utils/src/casing.ts
|
|
72
|
+
const typeDef = defineNode({ kind: "Type" });
|
|
128
73
|
/**
|
|
129
|
-
*
|
|
130
|
-
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
131
|
-
* and capitalizes each word according to `pascal`.
|
|
132
|
-
*
|
|
133
|
-
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
74
|
+
* Definition for the {@link FunctionNode}.
|
|
134
75
|
*/
|
|
135
|
-
|
|
136
|
-
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
137
|
-
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
138
|
-
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
139
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
140
|
-
}
|
|
76
|
+
const functionDef = defineNode({ kind: "Function" });
|
|
141
77
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* @example Word boundaries
|
|
145
|
-
* `camelCase('hello-world') // 'helloWorld'`
|
|
146
|
-
*
|
|
147
|
-
* @example With a prefix
|
|
148
|
-
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
78
|
+
* Definition for the {@link ArrowFunctionNode}.
|
|
149
79
|
*/
|
|
150
|
-
|
|
151
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
152
|
-
}
|
|
80
|
+
const arrowFunctionDef = defineNode({ kind: "ArrowFunction" });
|
|
153
81
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* @example Word boundaries
|
|
157
|
-
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
158
|
-
*
|
|
159
|
-
* @example With a suffix
|
|
160
|
-
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
82
|
+
* Definition for the {@link TextNode}.
|
|
161
83
|
*/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
//#region src/nodes/code.ts
|
|
84
|
+
const textDef = defineNode({
|
|
85
|
+
kind: "Text",
|
|
86
|
+
build: (value) => ({ value })
|
|
87
|
+
});
|
|
167
88
|
/**
|
|
168
|
-
* Definition for the {@link
|
|
89
|
+
* Definition for the {@link BreakNode}.
|
|
169
90
|
*/
|
|
170
|
-
const
|
|
91
|
+
const breakDef = defineNode({
|
|
92
|
+
kind: "Break",
|
|
93
|
+
build: () => ({})
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* Definition for the {@link JsxNode}.
|
|
97
|
+
*/
|
|
98
|
+
const jsxDef = defineNode({
|
|
99
|
+
kind: "Jsx",
|
|
100
|
+
build: (value) => ({ value })
|
|
101
|
+
});
|
|
171
102
|
/**
|
|
172
103
|
* Creates a `ConstNode` representing a TypeScript `const` declaration.
|
|
173
104
|
*
|
|
@@ -179,10 +110,6 @@ const constDef = defineNode({ kind: "Const" });
|
|
|
179
110
|
*/
|
|
180
111
|
const createConst = constDef.create;
|
|
181
112
|
/**
|
|
182
|
-
* Definition for the {@link TypeNode}.
|
|
183
|
-
*/
|
|
184
|
-
const typeDef = defineNode({ kind: "Type" });
|
|
185
|
-
/**
|
|
186
113
|
* Creates a `TypeNode` representing a TypeScript `type` alias declaration.
|
|
187
114
|
*
|
|
188
115
|
* @example
|
|
@@ -193,10 +120,6 @@ const typeDef = defineNode({ kind: "Type" });
|
|
|
193
120
|
*/
|
|
194
121
|
const createType = typeDef.create;
|
|
195
122
|
/**
|
|
196
|
-
* Definition for the {@link FunctionNode}.
|
|
197
|
-
*/
|
|
198
|
-
const functionDef = defineNode({ kind: "Function" });
|
|
199
|
-
/**
|
|
200
123
|
* Creates a `FunctionNode` representing a TypeScript `function` declaration.
|
|
201
124
|
*
|
|
202
125
|
* @example
|
|
@@ -207,10 +130,6 @@ const functionDef = defineNode({ kind: "Function" });
|
|
|
207
130
|
*/
|
|
208
131
|
const createFunction = functionDef.create;
|
|
209
132
|
/**
|
|
210
|
-
* Definition for the {@link ArrowFunctionNode}.
|
|
211
|
-
*/
|
|
212
|
-
const arrowFunctionDef = defineNode({ kind: "ArrowFunction" });
|
|
213
|
-
/**
|
|
214
133
|
* Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
|
|
215
134
|
*
|
|
216
135
|
* @example
|
|
@@ -221,13 +140,6 @@ const arrowFunctionDef = defineNode({ kind: "ArrowFunction" });
|
|
|
221
140
|
*/
|
|
222
141
|
const createArrowFunction = arrowFunctionDef.create;
|
|
223
142
|
/**
|
|
224
|
-
* Definition for the {@link TextNode}.
|
|
225
|
-
*/
|
|
226
|
-
const textDef = defineNode({
|
|
227
|
-
kind: "Text",
|
|
228
|
-
build: (value) => ({ value })
|
|
229
|
-
});
|
|
230
|
-
/**
|
|
231
143
|
* Creates a {@link TextNode} representing a raw string fragment in the source output.
|
|
232
144
|
*
|
|
233
145
|
* @example
|
|
@@ -238,13 +150,6 @@ const textDef = defineNode({
|
|
|
238
150
|
*/
|
|
239
151
|
const createText = textDef.create;
|
|
240
152
|
/**
|
|
241
|
-
* Definition for the {@link BreakNode}.
|
|
242
|
-
*/
|
|
243
|
-
const breakDef = defineNode({
|
|
244
|
-
kind: "Break",
|
|
245
|
-
build: () => ({})
|
|
246
|
-
});
|
|
247
|
-
/**
|
|
248
153
|
* Creates a {@link BreakNode} representing a line break in the source output.
|
|
249
154
|
*
|
|
250
155
|
* @example
|
|
@@ -257,13 +162,6 @@ function createBreak() {
|
|
|
257
162
|
return breakDef.create();
|
|
258
163
|
}
|
|
259
164
|
/**
|
|
260
|
-
* Definition for the {@link JsxNode}.
|
|
261
|
-
*/
|
|
262
|
-
const jsxDef = defineNode({
|
|
263
|
-
kind: "Jsx",
|
|
264
|
-
build: (value) => ({ value })
|
|
265
|
-
});
|
|
266
|
-
/**
|
|
267
165
|
* Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
|
|
268
166
|
*
|
|
269
167
|
* @example
|
|
@@ -287,12 +185,252 @@ const contentDef = defineNode({
|
|
|
287
185
|
*/
|
|
288
186
|
const createContent = contentDef.create;
|
|
289
187
|
//#endregion
|
|
188
|
+
//#region ../../internals/utils/src/casing.ts
|
|
189
|
+
/**
|
|
190
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
191
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
192
|
+
* and capitalizes each word according to `pascal`.
|
|
193
|
+
*
|
|
194
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
195
|
+
*/
|
|
196
|
+
function toCamelOrPascal(text, pascal) {
|
|
197
|
+
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
198
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
199
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
200
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Converts `text` to camelCase.
|
|
204
|
+
*
|
|
205
|
+
* @example Word boundaries
|
|
206
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
207
|
+
*
|
|
208
|
+
* @example With a prefix
|
|
209
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
210
|
+
*/
|
|
211
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
212
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Converts `text` to PascalCase.
|
|
216
|
+
*
|
|
217
|
+
* @example Word boundaries
|
|
218
|
+
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
219
|
+
*
|
|
220
|
+
* @example With a suffix
|
|
221
|
+
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
222
|
+
*/
|
|
223
|
+
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
224
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
225
|
+
}
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region ../../internals/utils/src/fs.ts
|
|
228
|
+
/**
|
|
229
|
+
* Strips the file extension from a path or file name.
|
|
230
|
+
* Only removes the last `.ext` segment when the dot is not part of a directory name.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* trimExtName('petStore.ts') // 'petStore'
|
|
234
|
+
* trimExtName('/src/models/pet.ts') // '/src/models/pet'
|
|
235
|
+
* trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'
|
|
236
|
+
* trimExtName('noExtension') // 'noExtension'
|
|
237
|
+
*/
|
|
238
|
+
function trimExtName(text) {
|
|
239
|
+
const dotIndex = text.lastIndexOf(".");
|
|
240
|
+
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
241
|
+
return text;
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/utils/extractStringsFromNodes.ts
|
|
245
|
+
/**
|
|
246
|
+
* Extracts all string content from a `CodeNode` tree recursively.
|
|
247
|
+
*
|
|
248
|
+
* Collects text node values, identifier references in string fields (`params`, `generics`, `returnType`, `type`),
|
|
249
|
+
* and nested node content. Used to build the full source string for import filtering.
|
|
250
|
+
*/
|
|
251
|
+
function extractStringsFromNodes(nodes) {
|
|
252
|
+
if (!nodes?.length) return "";
|
|
253
|
+
return nodes.map((node) => {
|
|
254
|
+
if (typeof node === "string") return node;
|
|
255
|
+
if (node.kind === "Text") return node.value;
|
|
256
|
+
if (node.kind === "Break") return "";
|
|
257
|
+
if (node.kind === "Jsx") return node.value;
|
|
258
|
+
const parts = [];
|
|
259
|
+
if ("params" in node && node.params) parts.push(node.params);
|
|
260
|
+
if ("generics" in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(", ") : node.generics);
|
|
261
|
+
if ("returnType" in node && node.returnType) parts.push(node.returnType);
|
|
262
|
+
if ("type" in node && typeof node.type === "string") parts.push(node.type);
|
|
263
|
+
const nested = extractStringsFromNodes(node.nodes);
|
|
264
|
+
if (nested) parts.push(nested);
|
|
265
|
+
return parts.join("\n");
|
|
266
|
+
}).filter(Boolean).join("\n");
|
|
267
|
+
}
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/utils/fileMerge.ts
|
|
270
|
+
function sourceKey(source) {
|
|
271
|
+
return `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
|
|
272
|
+
}
|
|
273
|
+
function pathTypeKey(path, isTypeOnly) {
|
|
274
|
+
return `${path}:${isTypeOnly ?? false}`;
|
|
275
|
+
}
|
|
276
|
+
function exportKey(path, name, isTypeOnly, asAlias) {
|
|
277
|
+
return `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
|
|
278
|
+
}
|
|
279
|
+
function importKey(path, name, isTypeOnly) {
|
|
280
|
+
return `${path}:${name ?? ""}:${isTypeOnly ?? false}`;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Computes a multi-level sort key for exports and imports:
|
|
284
|
+
* non-array names first (wildcards/namespace aliases). Type-only before value. Alphabetical path. Unnamed before named.
|
|
285
|
+
*/
|
|
286
|
+
function sortKey(node) {
|
|
287
|
+
const isArray = Array.isArray(node.name) ? "1" : "0";
|
|
288
|
+
const typeOnly = node.isTypeOnly ? "0" : "1";
|
|
289
|
+
const hasName = node.name != null ? "1" : "0";
|
|
290
|
+
const name = Array.isArray(node.name) ? node.name.toSorted().join("\0") : node.name ?? "";
|
|
291
|
+
return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Deduplicates `SourceNode` objects by `name + isExportable + isTypeOnly`, keeping the first of each
|
|
295
|
+
* key. Unnamed sources fall back to their extracted node strings as the name part of the key. Returns
|
|
296
|
+
* the deduplicated array in original order.
|
|
297
|
+
*/
|
|
298
|
+
function combineSources(sources) {
|
|
299
|
+
const seen = /* @__PURE__ */ new Map();
|
|
300
|
+
for (const source of sources) {
|
|
301
|
+
const key = sourceKey(source);
|
|
302
|
+
if (!seen.has(key)) seen.set(key, source);
|
|
303
|
+
}
|
|
304
|
+
return [...seen.values()];
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Merges `incoming` names into `existing`, preserving order and dropping duplicates.
|
|
308
|
+
*
|
|
309
|
+
* Shared by `combineExports` and `combineImports` for the same-path name-merge case.
|
|
310
|
+
*/
|
|
311
|
+
function mergeNameArrays(existing, incoming) {
|
|
312
|
+
const merged = new Set(existing);
|
|
313
|
+
for (const name of incoming) merged.add(name);
|
|
314
|
+
return [...merged];
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Deduplicates and merges `ExportNode` objects by path and type.
|
|
318
|
+
*
|
|
319
|
+
* Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
|
|
320
|
+
* Non-array exports are deduplicated by exact identity. Returns a sorted, deduplicated array.
|
|
321
|
+
*/
|
|
322
|
+
function combineExports(exports) {
|
|
323
|
+
const result = [];
|
|
324
|
+
const namedByPath = /* @__PURE__ */ new Map();
|
|
325
|
+
const seen = /* @__PURE__ */ new Set();
|
|
326
|
+
const keyed = exports.map((node) => ({
|
|
327
|
+
node,
|
|
328
|
+
key: sortKey(node)
|
|
329
|
+
}));
|
|
330
|
+
keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
331
|
+
for (const { node: curr } of keyed) {
|
|
332
|
+
const { name, path, isTypeOnly, asAlias } = curr;
|
|
333
|
+
if (Array.isArray(name)) {
|
|
334
|
+
if (!name.length) continue;
|
|
335
|
+
const key = pathTypeKey(path, isTypeOnly);
|
|
336
|
+
const existing = namedByPath.get(key);
|
|
337
|
+
if (existing && Array.isArray(existing.name)) existing.name = mergeNameArrays(existing.name, name);
|
|
338
|
+
else {
|
|
339
|
+
const newItem = {
|
|
340
|
+
...curr,
|
|
341
|
+
name: [...new Set(name)]
|
|
342
|
+
};
|
|
343
|
+
result.push(newItem);
|
|
344
|
+
namedByPath.set(key, newItem);
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
const key = exportKey(path, name, isTypeOnly, asAlias);
|
|
348
|
+
if (!seen.has(key)) {
|
|
349
|
+
result.push(curr);
|
|
350
|
+
seen.add(key);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Deduplicates and merges `ImportNode` objects, filtering out unused imports.
|
|
358
|
+
*
|
|
359
|
+
* Retains imports that are referenced in `source` or re-exported. Imports with the same path and
|
|
360
|
+
* `isTypeOnly` flag have their names merged. Returns a sorted, deduplicated, filtered array.
|
|
361
|
+
*/
|
|
362
|
+
function combineImports(imports, exports, source) {
|
|
363
|
+
const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
|
|
364
|
+
const isUsed = (importName) => !source || source.includes(importName) || exportedNames.has(importName);
|
|
365
|
+
const importNameMemo = /* @__PURE__ */ new Map();
|
|
366
|
+
const canonicalizeName = (n) => {
|
|
367
|
+
if (typeof n === "string") return n;
|
|
368
|
+
const key = `${n.propertyName}:${n.name ?? ""}`;
|
|
369
|
+
if (!importNameMemo.has(key)) importNameMemo.set(key, n);
|
|
370
|
+
return importNameMemo.get(key);
|
|
371
|
+
};
|
|
372
|
+
const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
|
|
373
|
+
for (const node of imports) {
|
|
374
|
+
if (!Array.isArray(node.name)) continue;
|
|
375
|
+
if (node.name.some((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName))) pathsWithUsedNamedImport.add(node.path);
|
|
376
|
+
}
|
|
377
|
+
const result = [];
|
|
378
|
+
const namedByPath = /* @__PURE__ */ new Map();
|
|
379
|
+
const seen = /* @__PURE__ */ new Set();
|
|
380
|
+
const keyed = imports.map((node) => ({
|
|
381
|
+
node,
|
|
382
|
+
key: sortKey(node)
|
|
383
|
+
}));
|
|
384
|
+
keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
385
|
+
for (const { node: curr } of keyed) {
|
|
386
|
+
if (curr.path === curr.root) continue;
|
|
387
|
+
const { path, isTypeOnly } = curr;
|
|
388
|
+
let { name } = curr;
|
|
389
|
+
if (Array.isArray(name)) {
|
|
390
|
+
name = [...new Set(name.map(canonicalizeName))].filter((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName));
|
|
391
|
+
if (!name.length) continue;
|
|
392
|
+
const key = pathTypeKey(path, isTypeOnly);
|
|
393
|
+
const existing = namedByPath.get(key);
|
|
394
|
+
if (existing && Array.isArray(existing.name)) existing.name = mergeNameArrays(existing.name, name);
|
|
395
|
+
else {
|
|
396
|
+
const newItem = {
|
|
397
|
+
...curr,
|
|
398
|
+
name
|
|
399
|
+
};
|
|
400
|
+
result.push(newItem);
|
|
401
|
+
namedByPath.set(key, newItem);
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
if (name && !isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
|
|
405
|
+
const key = importKey(path, name, isTypeOnly);
|
|
406
|
+
if (!seen.has(key)) {
|
|
407
|
+
result.push(curr);
|
|
408
|
+
seen.add(key);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
//#endregion
|
|
290
415
|
//#region src/nodes/file.ts
|
|
291
416
|
/**
|
|
292
417
|
* Definition for the {@link ImportNode}.
|
|
293
418
|
*/
|
|
294
419
|
const importDef = defineNode({ kind: "Import" });
|
|
295
420
|
/**
|
|
421
|
+
* Definition for the {@link ExportNode}.
|
|
422
|
+
*/
|
|
423
|
+
const exportDef = defineNode({ kind: "Export" });
|
|
424
|
+
/**
|
|
425
|
+
* Definition for the {@link SourceNode}.
|
|
426
|
+
*/
|
|
427
|
+
const sourceDef = defineNode({ kind: "Source" });
|
|
428
|
+
/**
|
|
429
|
+
* Definition for the {@link FileNode}. The fully resolved builder lives in
|
|
430
|
+
* `createFile`, so this definition only supplies the guard.
|
|
431
|
+
*/
|
|
432
|
+
const fileDef = defineNode({ kind: "File" });
|
|
433
|
+
/**
|
|
296
434
|
* Creates an `ImportNode` representing a language-agnostic import/dependency declaration.
|
|
297
435
|
*
|
|
298
436
|
* @example Named import
|
|
@@ -303,10 +441,6 @@ const importDef = defineNode({ kind: "Import" });
|
|
|
303
441
|
*/
|
|
304
442
|
const createImport = importDef.create;
|
|
305
443
|
/**
|
|
306
|
-
* Definition for the {@link ExportNode}.
|
|
307
|
-
*/
|
|
308
|
-
const exportDef = defineNode({ kind: "Export" });
|
|
309
|
-
/**
|
|
310
444
|
* Creates an `ExportNode` representing a language-agnostic export/public API declaration.
|
|
311
445
|
*
|
|
312
446
|
* @example Named export
|
|
@@ -317,10 +451,6 @@ const exportDef = defineNode({ kind: "Export" });
|
|
|
317
451
|
*/
|
|
318
452
|
const createExport = exportDef.create;
|
|
319
453
|
/**
|
|
320
|
-
* Definition for the {@link SourceNode}.
|
|
321
|
-
*/
|
|
322
|
-
const sourceDef = defineNode({ kind: "Source" });
|
|
323
|
-
/**
|
|
324
454
|
* Creates a `SourceNode` representing a fragment of source code within a file.
|
|
325
455
|
*
|
|
326
456
|
* @example
|
|
@@ -330,10 +460,64 @@ const sourceDef = defineNode({ kind: "Source" });
|
|
|
330
460
|
*/
|
|
331
461
|
const createSource = sourceDef.create;
|
|
332
462
|
/**
|
|
333
|
-
*
|
|
334
|
-
*
|
|
463
|
+
* Creates a fully resolved `FileNode` from a file input descriptor.
|
|
464
|
+
*
|
|
465
|
+
* Computes:
|
|
466
|
+
* - `id` SHA256 hash of the file path
|
|
467
|
+
* - `name` `baseName` without extension
|
|
468
|
+
* - `extname` extension extracted from `baseName`
|
|
469
|
+
*
|
|
470
|
+
* Deduplicates:
|
|
471
|
+
* - `sources` via `combineSources`
|
|
472
|
+
* - `exports` via `combineExports`
|
|
473
|
+
* - `imports` via `combineImports` (also filters unused imports)
|
|
474
|
+
*
|
|
475
|
+
* @throws {Error} when `baseName` has no extension.
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* ```ts
|
|
479
|
+
* const file = createFile({
|
|
480
|
+
* baseName: 'petStore.ts',
|
|
481
|
+
* path: 'src/models/petStore.ts',
|
|
482
|
+
* sources: [createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')] })],
|
|
483
|
+
* imports: [createImport({ name: ['z'], path: 'zod' })],
|
|
484
|
+
* exports: [createExport({ name: ['Pet'], path: './petStore' })],
|
|
485
|
+
* })
|
|
486
|
+
* // file.id = SHA256 hash of 'src/models/petStore.ts'
|
|
487
|
+
* // file.name = 'petStore'
|
|
488
|
+
* // file.extname = '.ts'
|
|
489
|
+
* ```
|
|
335
490
|
*/
|
|
336
|
-
|
|
491
|
+
function createFile(input) {
|
|
492
|
+
const extname = path.extname(input.baseName) || (input.baseName.startsWith(".") ? input.baseName : "");
|
|
493
|
+
if (!extname) throw new Error(`No extname found for ${input.baseName}`);
|
|
494
|
+
const source = (input.sources ?? []).flatMap((item) => item.nodes ?? []).map((node) => extractStringsFromNodes([node])).filter(Boolean).join("\n\n");
|
|
495
|
+
const resolvedExports = input.exports?.length ? combineExports(input.exports) : [];
|
|
496
|
+
const combinedImports = input.imports?.length ? combineImports(input.imports, resolvedExports, source || void 0) : [];
|
|
497
|
+
const localNames = new Set((input.sources ?? []).map((item) => item.name).filter((name) => Boolean(name)));
|
|
498
|
+
const nameOf = (item) => typeof item === "string" ? item : item.name ?? item.propertyName;
|
|
499
|
+
const resolvedImports = combinedImports.filter((imp) => imp.path !== input.path).flatMap((imp) => {
|
|
500
|
+
if (!Array.isArray(imp.name)) return typeof imp.name === "string" && localNames.has(imp.name) ? [] : [imp];
|
|
501
|
+
const kept = imp.name.filter((item) => !localNames.has(nameOf(item)));
|
|
502
|
+
if (!kept.length) return [];
|
|
503
|
+
return [kept.length === imp.name.length ? imp : {
|
|
504
|
+
...imp,
|
|
505
|
+
name: kept
|
|
506
|
+
}];
|
|
507
|
+
});
|
|
508
|
+
const resolvedSources = input.sources?.length ? combineSources(input.sources) : [];
|
|
509
|
+
return {
|
|
510
|
+
kind: "File",
|
|
511
|
+
...input,
|
|
512
|
+
id: hash("sha256", input.path, "hex"),
|
|
513
|
+
name: trimExtName(input.baseName),
|
|
514
|
+
extname,
|
|
515
|
+
imports: resolvedImports,
|
|
516
|
+
exports: resolvedExports,
|
|
517
|
+
sources: resolvedSources,
|
|
518
|
+
meta: input.meta ?? {}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
337
521
|
//#endregion
|
|
338
522
|
//#region src/nodes/function.ts
|
|
339
523
|
/**
|
|
@@ -341,44 +525,14 @@ const fileDef = defineNode({ kind: "File" });
|
|
|
341
525
|
*/
|
|
342
526
|
const typeLiteralDef = defineNode({ kind: "TypeLiteral" });
|
|
343
527
|
/**
|
|
344
|
-
* Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
|
|
345
|
-
*
|
|
346
|
-
* @example
|
|
347
|
-
* ```ts
|
|
348
|
-
* createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
|
|
349
|
-
* // { petId: string }
|
|
350
|
-
* ```
|
|
351
|
-
*/
|
|
352
|
-
const createTypeLiteral = typeLiteralDef.create;
|
|
353
|
-
/**
|
|
354
528
|
* Definition for the {@link IndexedAccessTypeNode}.
|
|
355
529
|
*/
|
|
356
530
|
const indexedAccessTypeDef = defineNode({ kind: "IndexedAccessType" });
|
|
357
531
|
/**
|
|
358
|
-
* Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
|
|
359
|
-
*
|
|
360
|
-
* @example
|
|
361
|
-
* ```ts
|
|
362
|
-
* createIndexedAccessType({ objectType: 'DeletePetPathParams', indexType: 'petId' })
|
|
363
|
-
* // DeletePetPathParams['petId']
|
|
364
|
-
* ```
|
|
365
|
-
*/
|
|
366
|
-
const createIndexedAccessType = indexedAccessTypeDef.create;
|
|
367
|
-
/**
|
|
368
532
|
* Definition for the {@link ObjectBindingPatternNode}.
|
|
369
533
|
*/
|
|
370
534
|
const objectBindingPatternDef = defineNode({ kind: "ObjectBindingPattern" });
|
|
371
535
|
/**
|
|
372
|
-
* Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
|
|
373
|
-
*
|
|
374
|
-
* @example
|
|
375
|
-
* ```ts
|
|
376
|
-
* createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
|
|
377
|
-
* // { id, name }
|
|
378
|
-
* ```
|
|
379
|
-
*/
|
|
380
|
-
const createObjectBindingPattern = objectBindingPatternDef.create;
|
|
381
|
-
/**
|
|
382
536
|
* Definition for the {@link FunctionParameterNode}. `optional` defaults to `false`.
|
|
383
537
|
* Passing `properties` builds a destructured group: an {@link ObjectBindingPatternNode} name
|
|
384
538
|
* paired with a {@link TypeLiteralNode} type.
|
|
@@ -403,6 +557,43 @@ const functionParameterDef = defineNode({
|
|
|
403
557
|
}
|
|
404
558
|
});
|
|
405
559
|
/**
|
|
560
|
+
* Definition for the {@link FunctionParametersNode}.
|
|
561
|
+
*/
|
|
562
|
+
const functionParametersDef = defineNode({
|
|
563
|
+
kind: "FunctionParameters",
|
|
564
|
+
defaults: { params: [] }
|
|
565
|
+
});
|
|
566
|
+
/**
|
|
567
|
+
* Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
|
|
568
|
+
*
|
|
569
|
+
* @example
|
|
570
|
+
* ```ts
|
|
571
|
+
* createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
|
|
572
|
+
* // { petId: string }
|
|
573
|
+
* ```
|
|
574
|
+
*/
|
|
575
|
+
const createTypeLiteral = typeLiteralDef.create;
|
|
576
|
+
/**
|
|
577
|
+
* Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
|
|
578
|
+
*
|
|
579
|
+
* @example
|
|
580
|
+
* ```ts
|
|
581
|
+
* createIndexedAccessType({ target: 'DeletePetPathParams', key: 'petId' })
|
|
582
|
+
* // DeletePetPathParams['petId']
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
585
|
+
const createIndexedAccessType = indexedAccessTypeDef.create;
|
|
586
|
+
/**
|
|
587
|
+
* Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* ```ts
|
|
591
|
+
* createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
|
|
592
|
+
* // { id, name }
|
|
593
|
+
* ```
|
|
594
|
+
*/
|
|
595
|
+
const createObjectBindingPattern = objectBindingPatternDef.create;
|
|
596
|
+
/**
|
|
406
597
|
* Creates a `FunctionParameterNode`. `optional` defaults to `false`.
|
|
407
598
|
*
|
|
408
599
|
* @example Optional param
|
|
@@ -419,13 +610,6 @@ const functionParameterDef = defineNode({
|
|
|
419
610
|
*/
|
|
420
611
|
const createFunctionParameter = functionParameterDef.create;
|
|
421
612
|
/**
|
|
422
|
-
* Definition for the {@link FunctionParametersNode}.
|
|
423
|
-
*/
|
|
424
|
-
const functionParametersDef = defineNode({
|
|
425
|
-
kind: "FunctionParameters",
|
|
426
|
-
defaults: { params: [] }
|
|
427
|
-
});
|
|
428
|
-
/**
|
|
429
613
|
* Creates a `FunctionParametersNode` from an ordered list of parameters.
|
|
430
614
|
*
|
|
431
615
|
* @example
|
|
@@ -457,8 +641,8 @@ const inputDef = defineNode({
|
|
|
457
641
|
});
|
|
458
642
|
/**
|
|
459
643
|
* Creates an `InputNode`. Pass `stream: true` for the streaming variant whose `schemas` and
|
|
460
|
-
* `operations` are `AsyncIterable` sources
|
|
461
|
-
*
|
|
644
|
+
* `operations` are `AsyncIterable` sources. Otherwise it builds the eager variant with array
|
|
645
|
+
* `schemas`/`operations`. Both variants get the defaulted `meta`.
|
|
462
646
|
*
|
|
463
647
|
* @example Eager
|
|
464
648
|
* ```ts
|
|
@@ -475,6 +659,10 @@ function createInput(options = {}) {
|
|
|
475
659
|
const { stream, ...overrides } = options;
|
|
476
660
|
if (stream) return {
|
|
477
661
|
kind: "Input",
|
|
662
|
+
meta: {
|
|
663
|
+
circularNames: [],
|
|
664
|
+
enumNames: []
|
|
665
|
+
},
|
|
478
666
|
...overrides
|
|
479
667
|
};
|
|
480
668
|
return inputDef.create(overrides);
|
|
@@ -482,18 +670,15 @@ function createInput(options = {}) {
|
|
|
482
670
|
//#endregion
|
|
483
671
|
//#region src/nodes/requestBody.ts
|
|
484
672
|
/**
|
|
485
|
-
* Definition for the {@link RequestBodyNode}
|
|
673
|
+
* Definition for the {@link RequestBodyNode}. Content entries are built upfront with
|
|
674
|
+
* {@link createContent}, mirroring how `parameters` and `responses` take prebuilt nodes.
|
|
486
675
|
*/
|
|
487
676
|
const requestBodyDef = defineNode({
|
|
488
677
|
kind: "RequestBody",
|
|
489
|
-
build: (props) => ({
|
|
490
|
-
...props,
|
|
491
|
-
content: props.content?.map(createContent)
|
|
492
|
-
}),
|
|
493
678
|
children: ["content"]
|
|
494
679
|
});
|
|
495
680
|
/**
|
|
496
|
-
* Creates a `RequestBodyNode
|
|
681
|
+
* Creates a `RequestBodyNode`.
|
|
497
682
|
*/
|
|
498
683
|
const createRequestBody = requestBodyDef.create;
|
|
499
684
|
//#endregion
|
|
@@ -550,10 +735,24 @@ function createOutput(overrides = {}) {
|
|
|
550
735
|
return outputDef.create(overrides);
|
|
551
736
|
}
|
|
552
737
|
//#endregion
|
|
738
|
+
//#region src/optionality.ts
|
|
739
|
+
/**
|
|
740
|
+
* Generic JSON Schema optionality: a non-required field is optional, and a
|
|
741
|
+
* non-required nullable field is nullish.
|
|
742
|
+
*/
|
|
743
|
+
function optionality(schema, required) {
|
|
744
|
+
const nullable = schema.nullable ?? false;
|
|
745
|
+
return {
|
|
746
|
+
...schema,
|
|
747
|
+
optional: !required && !nullable ? true : void 0,
|
|
748
|
+
nullish: !required && nullable ? true : void 0
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
//#endregion
|
|
553
752
|
//#region src/nodes/parameter.ts
|
|
554
753
|
/**
|
|
555
|
-
* Definition for the {@link ParameterNode}. `required` defaults to `false
|
|
556
|
-
*
|
|
754
|
+
* Definition for the {@link ParameterNode}. `required` defaults to `false`, and the schema's
|
|
755
|
+
* `optional`/`nullish` flags are derived from it through {@link optionality}.
|
|
557
756
|
*/
|
|
558
757
|
const parameterDef = defineNode({
|
|
559
758
|
kind: "Parameter",
|
|
@@ -562,12 +761,11 @@ const parameterDef = defineNode({
|
|
|
562
761
|
return {
|
|
563
762
|
...props,
|
|
564
763
|
required,
|
|
565
|
-
schema:
|
|
764
|
+
schema: optionality(props.schema, required)
|
|
566
765
|
};
|
|
567
766
|
},
|
|
568
767
|
children: ["schema"],
|
|
569
|
-
visitorKey: "parameter"
|
|
570
|
-
rebuild: true
|
|
768
|
+
visitorKey: "parameter"
|
|
571
769
|
});
|
|
572
770
|
/**
|
|
573
771
|
* Creates a `ParameterNode`.
|
|
@@ -586,8 +784,8 @@ const createParameter = parameterDef.create;
|
|
|
586
784
|
//#endregion
|
|
587
785
|
//#region src/nodes/property.ts
|
|
588
786
|
/**
|
|
589
|
-
* Definition for the {@link PropertyNode}. `required` defaults to `false
|
|
590
|
-
*
|
|
787
|
+
* Definition for the {@link PropertyNode}. `required` defaults to `false`, and the schema's
|
|
788
|
+
* `optional`/`nullish` flags are derived from it through {@link optionality}.
|
|
591
789
|
*/
|
|
592
790
|
const propertyDef = defineNode({
|
|
593
791
|
kind: "Property",
|
|
@@ -596,12 +794,11 @@ const propertyDef = defineNode({
|
|
|
596
794
|
return {
|
|
597
795
|
...props,
|
|
598
796
|
required,
|
|
599
|
-
schema:
|
|
797
|
+
schema: optionality(props.schema, required)
|
|
600
798
|
};
|
|
601
799
|
},
|
|
602
800
|
children: ["schema"],
|
|
603
|
-
visitorKey: "property"
|
|
604
|
-
rebuild: true
|
|
801
|
+
visitorKey: "property"
|
|
605
802
|
});
|
|
606
803
|
/**
|
|
607
804
|
* Creates a `PropertyNode`.
|
|
@@ -627,14 +824,14 @@ const responseDef = defineNode({
|
|
|
627
824
|
kind: "Response",
|
|
628
825
|
build: (props) => {
|
|
629
826
|
const { schema, mediaType, keysToOmit, content, ...rest } = props;
|
|
630
|
-
const entries = content ?? (schema ? [{
|
|
827
|
+
const entries = content ?? (schema ? [createContent({
|
|
631
828
|
contentType: mediaType ?? "application/json",
|
|
632
829
|
schema,
|
|
633
830
|
keysToOmit: keysToOmit ?? null
|
|
634
|
-
}] : void 0);
|
|
831
|
+
})] : void 0);
|
|
635
832
|
return {
|
|
636
833
|
...rest,
|
|
637
|
-
content: entries
|
|
834
|
+
content: entries
|
|
638
835
|
};
|
|
639
836
|
},
|
|
640
837
|
children: ["content"],
|
|
@@ -647,12 +844,67 @@ const responseDef = defineNode({
|
|
|
647
844
|
* ```ts
|
|
648
845
|
* const response = createResponse({
|
|
649
846
|
* statusCode: '200',
|
|
650
|
-
* content: [{ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) }],
|
|
847
|
+
* content: [createContent({ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) })],
|
|
651
848
|
* })
|
|
652
849
|
* ```
|
|
653
850
|
*/
|
|
654
851
|
const createResponse = responseDef.create;
|
|
655
852
|
//#endregion
|
|
656
|
-
|
|
853
|
+
//#region src/nodes/schema.ts
|
|
854
|
+
/**
|
|
855
|
+
* Maps schema `type` to its underlying `primitive`.
|
|
856
|
+
* Primitive types map to themselves and special string formats map to `'string'`.
|
|
857
|
+
* Any type not listed here (such as `ref`, `enum`, `union`, `intersection`, `tuple`, `ipv4`, `ipv6`, `blob`) has no `primitive`.
|
|
858
|
+
*/
|
|
859
|
+
const TYPE_TO_PRIMITIVE = {
|
|
860
|
+
string: "string",
|
|
861
|
+
number: "number",
|
|
862
|
+
integer: "integer",
|
|
863
|
+
bigint: "bigint",
|
|
864
|
+
boolean: "boolean",
|
|
865
|
+
null: "null",
|
|
866
|
+
any: "any",
|
|
867
|
+
unknown: "unknown",
|
|
868
|
+
void: "void",
|
|
869
|
+
never: "never",
|
|
870
|
+
object: "object",
|
|
871
|
+
array: "array",
|
|
872
|
+
date: "date",
|
|
873
|
+
uuid: "string",
|
|
874
|
+
email: "string",
|
|
875
|
+
url: "string",
|
|
876
|
+
datetime: "string",
|
|
877
|
+
time: "string"
|
|
878
|
+
};
|
|
879
|
+
/**
|
|
880
|
+
* Definition for the {@link SchemaNode}. Object schemas default `properties` to an
|
|
881
|
+
* empty array, and `primitive` is inferred from `type` when not explicitly provided.
|
|
882
|
+
*/
|
|
883
|
+
const schemaDef = defineNode({
|
|
884
|
+
kind: "Schema",
|
|
885
|
+
build: (props) => {
|
|
886
|
+
if (props.type === "object") return {
|
|
887
|
+
properties: [],
|
|
888
|
+
primitive: "object",
|
|
889
|
+
...props
|
|
890
|
+
};
|
|
891
|
+
return {
|
|
892
|
+
primitive: TYPE_TO_PRIMITIVE[props.type],
|
|
893
|
+
...props
|
|
894
|
+
};
|
|
895
|
+
},
|
|
896
|
+
children: [
|
|
897
|
+
"properties",
|
|
898
|
+
"items",
|
|
899
|
+
"members",
|
|
900
|
+
"additionalProperties"
|
|
901
|
+
],
|
|
902
|
+
visitorKey: "schema"
|
|
903
|
+
});
|
|
904
|
+
function createSchema(props) {
|
|
905
|
+
return schemaDef.create(props);
|
|
906
|
+
}
|
|
907
|
+
//#endregion
|
|
908
|
+
export { textDef as $, createImport as A, createContent as B, functionParameterDef as C, typeLiteralDef as D, objectBindingPatternDef as E, sourceDef as F, createBreak as G, breakDef as H, extractStringsFromNodes as I, createJsx as J, createConst as K, camelCase as L, exportDef as M, fileDef as N, createExport as O, importDef as P, jsxDef as Q, pascalCase as R, createTypeLiteral as S, indexedAccessTypeDef as T, constDef as U, arrowFunctionDef as V, createArrowFunction as W, createType as X, createText as Y, functionDef as Z, inputDef as _, createProperty as a, createIndexedAccessType as b, parameterDef as c, outputDef as d, typeDef as et, createOperation as f, createInput as g, requestBodyDef as h, responseDef as i, createSource as j, createFile as k, optionality as l, createRequestBody as m, schemaDef as n, visitorKeys as nt, propertyDef as o, operationDef as p, createFunction as q, createResponse as r, createParameter as s, createSchema as t, defineNode as tt, createOutput as u, createFunctionParameter as v, functionParametersDef as w, createObjectBindingPattern as x, createFunctionParameters as y, contentDef as z };
|
|
657
909
|
|
|
658
|
-
//# sourceMappingURL=
|
|
910
|
+
//# sourceMappingURL=schema-Cbnxmz4b.js.map
|