@openpkg-ts/extract 0.14.4 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/tspec.js +10 -9
- package/dist/shared/{chunk-axmbd6k1.js → chunk-0bt6mcnx.js} +703 -623
- package/dist/src/index.d.ts +38 -31
- package/dist/src/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,54 +1,63 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/types/schema-builder.ts
|
|
2
2
|
import ts from "typescript";
|
|
3
|
+
var BUILTIN_TYPE_SCHEMAS = {
|
|
4
|
+
Date: { type: "string", format: "date-time" },
|
|
5
|
+
RegExp: { type: "object", description: "RegExp" },
|
|
6
|
+
Error: { type: "object" },
|
|
7
|
+
Promise: { type: "object" },
|
|
8
|
+
Map: { type: "object" },
|
|
9
|
+
Set: { type: "object" },
|
|
10
|
+
WeakMap: { type: "object" },
|
|
11
|
+
WeakSet: { type: "object" },
|
|
12
|
+
Function: { type: "object" },
|
|
13
|
+
ArrayBuffer: { type: "string", format: "binary" },
|
|
14
|
+
ArrayBufferLike: { type: "string", format: "binary" },
|
|
15
|
+
DataView: { type: "string", format: "binary" },
|
|
16
|
+
Uint8Array: { type: "string", format: "byte" },
|
|
17
|
+
Uint16Array: { type: "string", format: "byte" },
|
|
18
|
+
Uint32Array: { type: "string", format: "byte" },
|
|
19
|
+
Int8Array: { type: "string", format: "byte" },
|
|
20
|
+
Int16Array: { type: "string", format: "byte" },
|
|
21
|
+
Int32Array: { type: "string", format: "byte" },
|
|
22
|
+
Float32Array: { type: "string", format: "byte" },
|
|
23
|
+
Float64Array: { type: "string", format: "byte" },
|
|
24
|
+
BigInt64Array: { type: "string", format: "byte" },
|
|
25
|
+
BigUint64Array: { type: "string", format: "byte" }
|
|
26
|
+
};
|
|
3
27
|
var PRIMITIVES = new Set([
|
|
4
28
|
"string",
|
|
5
29
|
"number",
|
|
6
30
|
"boolean",
|
|
7
31
|
"void",
|
|
8
|
-
"any",
|
|
9
32
|
"undefined",
|
|
10
33
|
"null",
|
|
11
|
-
"
|
|
34
|
+
"any",
|
|
12
35
|
"unknown",
|
|
36
|
+
"never",
|
|
13
37
|
"object",
|
|
14
38
|
"symbol",
|
|
15
39
|
"bigint"
|
|
16
40
|
]);
|
|
17
|
-
var
|
|
41
|
+
var BUILTIN_GENERICS = new Set([
|
|
18
42
|
"Array",
|
|
19
|
-
"
|
|
20
|
-
"ArrayBufferLike",
|
|
21
|
-
"ArrayLike",
|
|
43
|
+
"ReadonlyArray",
|
|
22
44
|
"Promise",
|
|
45
|
+
"PromiseLike",
|
|
23
46
|
"Map",
|
|
24
47
|
"Set",
|
|
25
48
|
"WeakMap",
|
|
26
49
|
"WeakSet",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"Symbol",
|
|
36
|
-
"BigInt",
|
|
37
|
-
"Uint8Array",
|
|
38
|
-
"Int8Array",
|
|
39
|
-
"Uint16Array",
|
|
40
|
-
"Int16Array",
|
|
41
|
-
"Uint32Array",
|
|
42
|
-
"Int32Array",
|
|
43
|
-
"Float32Array",
|
|
44
|
-
"Float64Array",
|
|
45
|
-
"BigInt64Array",
|
|
46
|
-
"BigUint64Array",
|
|
47
|
-
"DataView",
|
|
48
|
-
"ReadonlyArray",
|
|
49
|
-
"Readonly",
|
|
50
|
+
"Iterable",
|
|
51
|
+
"Iterator",
|
|
52
|
+
"IterableIterator",
|
|
53
|
+
"AsyncIterable",
|
|
54
|
+
"AsyncIterator",
|
|
55
|
+
"AsyncIterableIterator",
|
|
56
|
+
"Generator",
|
|
57
|
+
"AsyncGenerator",
|
|
50
58
|
"Partial",
|
|
51
59
|
"Required",
|
|
60
|
+
"Readonly",
|
|
52
61
|
"Pick",
|
|
53
62
|
"Omit",
|
|
54
63
|
"Record",
|
|
@@ -59,360 +68,404 @@ var BUILTINS = new Set([
|
|
|
59
68
|
"ReturnType",
|
|
60
69
|
"ConstructorParameters",
|
|
61
70
|
"InstanceType",
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"AsyncIterable",
|
|
71
|
-
"AsyncIterator",
|
|
72
|
-
"AsyncIterableIterator",
|
|
71
|
+
"Awaited"
|
|
72
|
+
]);
|
|
73
|
+
var BUILTIN_TYPES = new Set([
|
|
74
|
+
"Date",
|
|
75
|
+
"RegExp",
|
|
76
|
+
"Error",
|
|
77
|
+
"Function",
|
|
78
|
+
"ArrayBuffer",
|
|
73
79
|
"SharedArrayBuffer",
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
80
|
+
"DataView",
|
|
81
|
+
"Uint8Array",
|
|
82
|
+
"Int8Array",
|
|
83
|
+
"Uint16Array",
|
|
84
|
+
"Int16Array",
|
|
85
|
+
"Uint32Array",
|
|
86
|
+
"Int32Array",
|
|
87
|
+
"Float32Array",
|
|
88
|
+
"Float64Array",
|
|
89
|
+
"BigInt64Array",
|
|
90
|
+
"BigUint64Array"
|
|
79
91
|
]);
|
|
80
|
-
function
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
function isPrimitiveName(name) {
|
|
93
|
+
return PRIMITIVES.has(name);
|
|
94
|
+
}
|
|
95
|
+
function isBuiltinGeneric(name) {
|
|
96
|
+
return BUILTIN_GENERICS.has(name);
|
|
97
|
+
}
|
|
98
|
+
function isAnonymous(type) {
|
|
99
|
+
const symbol = type.getSymbol() || type.aliasSymbol;
|
|
100
|
+
if (!symbol)
|
|
86
101
|
return true;
|
|
87
|
-
|
|
102
|
+
const name = symbol.getName();
|
|
103
|
+
return name.startsWith("__") || name === "";
|
|
88
104
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
function withDepth(ctx, fn) {
|
|
106
|
+
ctx.currentDepth++;
|
|
107
|
+
try {
|
|
108
|
+
return fn();
|
|
109
|
+
} finally {
|
|
110
|
+
ctx.currentDepth--;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function isAtMaxDepth(ctx) {
|
|
114
|
+
if (!ctx)
|
|
92
115
|
return false;
|
|
93
|
-
return
|
|
116
|
+
return ctx.currentDepth >= ctx.maxTypeDepth;
|
|
94
117
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
processing = new Set;
|
|
99
|
-
add(type) {
|
|
100
|
-
this.types.set(type.id, type);
|
|
118
|
+
function buildSchema(type, checker, ctx, _depth = 0) {
|
|
119
|
+
if (isAtMaxDepth(ctx)) {
|
|
120
|
+
return { type: checker.typeToString(type) };
|
|
101
121
|
}
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
if (ctx?.visitedTypes.has(type)) {
|
|
123
|
+
const callSignatures = type.getCallSignatures();
|
|
124
|
+
if (callSignatures.length > 0) {
|
|
125
|
+
return buildFunctionSchema(callSignatures, checker, ctx);
|
|
126
|
+
}
|
|
127
|
+
const symbol2 = type.getSymbol() || type.aliasSymbol;
|
|
128
|
+
if (symbol2) {
|
|
129
|
+
return { $ref: `#/types/${symbol2.getName()}` };
|
|
130
|
+
}
|
|
131
|
+
return { type: checker.typeToString(type) };
|
|
104
132
|
}
|
|
105
|
-
|
|
106
|
-
|
|
133
|
+
if (ctx && type.flags & ts.TypeFlags.Object) {
|
|
134
|
+
ctx.visitedTypes.add(type);
|
|
107
135
|
}
|
|
108
|
-
|
|
109
|
-
return
|
|
136
|
+
if (type.flags & ts.TypeFlags.String)
|
|
137
|
+
return { type: "string" };
|
|
138
|
+
if (type.flags & ts.TypeFlags.Number)
|
|
139
|
+
return { type: "number" };
|
|
140
|
+
if (type.flags & ts.TypeFlags.Boolean)
|
|
141
|
+
return { type: "boolean" };
|
|
142
|
+
if (type.flags & ts.TypeFlags.Undefined)
|
|
143
|
+
return { type: "undefined" };
|
|
144
|
+
if (type.flags & ts.TypeFlags.Null)
|
|
145
|
+
return { type: "null" };
|
|
146
|
+
if (type.flags & ts.TypeFlags.Void)
|
|
147
|
+
return { type: "void" };
|
|
148
|
+
if (type.flags & ts.TypeFlags.Any)
|
|
149
|
+
return { type: "any" };
|
|
150
|
+
if (type.flags & ts.TypeFlags.Unknown)
|
|
151
|
+
return { type: "unknown" };
|
|
152
|
+
if (type.flags & ts.TypeFlags.Never)
|
|
153
|
+
return { type: "never" };
|
|
154
|
+
if (type.flags & ts.TypeFlags.BigInt)
|
|
155
|
+
return { type: "bigint" };
|
|
156
|
+
if (type.flags & ts.TypeFlags.ESSymbol)
|
|
157
|
+
return { type: "symbol" };
|
|
158
|
+
if (type.flags & ts.TypeFlags.StringLiteral) {
|
|
159
|
+
const literal = type.value;
|
|
160
|
+
return { type: "string", enum: [literal] };
|
|
110
161
|
}
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
return;
|
|
115
|
-
const name = symbol.getName();
|
|
116
|
-
if (PRIMITIVES.has(name))
|
|
117
|
-
return;
|
|
118
|
-
if (BUILTINS.has(name))
|
|
119
|
-
return;
|
|
120
|
-
if (name.startsWith("__"))
|
|
121
|
-
return;
|
|
122
|
-
if (symbol.flags & ts.SymbolFlags.EnumMember)
|
|
123
|
-
return;
|
|
124
|
-
if (symbol.flags & ts.SymbolFlags.TypeParameter)
|
|
125
|
-
return;
|
|
126
|
-
if (symbol.flags & ts.SymbolFlags.Method)
|
|
127
|
-
return;
|
|
128
|
-
if (symbol.flags & ts.SymbolFlags.Function)
|
|
129
|
-
return;
|
|
130
|
-
if (isGenericTypeParameter(name))
|
|
131
|
-
return;
|
|
132
|
-
if (this.has(name))
|
|
133
|
-
return name;
|
|
134
|
-
if (this.processing.has(name))
|
|
135
|
-
return name;
|
|
136
|
-
this.processing.add(name);
|
|
137
|
-
try {
|
|
138
|
-
const specType = this.buildStubType(symbol, checker);
|
|
139
|
-
if (specType) {
|
|
140
|
-
this.add(specType);
|
|
141
|
-
return specType.id;
|
|
142
|
-
}
|
|
143
|
-
} finally {
|
|
144
|
-
this.processing.delete(name);
|
|
145
|
-
}
|
|
146
|
-
return;
|
|
162
|
+
if (type.flags & ts.TypeFlags.NumberLiteral) {
|
|
163
|
+
const literal = type.value;
|
|
164
|
+
return { type: "number", enum: [literal] };
|
|
147
165
|
}
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
else if (ts.isEnumDeclaration(decl))
|
|
159
|
-
kind = "enum";
|
|
166
|
+
if (type.flags & ts.TypeFlags.BooleanLiteral) {
|
|
167
|
+
const intrinsicName = type.intrinsicName;
|
|
168
|
+
return { type: "boolean", enum: [intrinsicName === "true"] };
|
|
169
|
+
}
|
|
170
|
+
if (type.isUnion()) {
|
|
171
|
+
const types = type.types;
|
|
172
|
+
const allStringLiterals = types.every((t) => t.flags & ts.TypeFlags.StringLiteral);
|
|
173
|
+
if (allStringLiterals) {
|
|
174
|
+
const enumValues = types.map((t) => t.value);
|
|
175
|
+
return { type: "string", enum: enumValues };
|
|
160
176
|
}
|
|
161
|
-
|
|
162
|
-
|
|
177
|
+
const allNumberLiterals = types.every((t) => t.flags & ts.TypeFlags.NumberLiteral);
|
|
178
|
+
if (allNumberLiterals) {
|
|
179
|
+
const enumValues = types.map((t) => t.value);
|
|
180
|
+
return { type: "number", enum: enumValues };
|
|
163
181
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
schema: { type: typeString },
|
|
171
|
-
...external ? { external: true } : {}
|
|
172
|
-
};
|
|
182
|
+
if (ctx) {
|
|
183
|
+
return withDepth(ctx, () => ({
|
|
184
|
+
anyOf: types.map((t) => buildSchema(t, checker, ctx))
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
return { anyOf: types.map((t) => buildSchema(t, checker, ctx)) };
|
|
173
188
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (specType) {
|
|
180
|
-
this.add(specType);
|
|
181
|
-
return specType;
|
|
189
|
+
if (type.isIntersection()) {
|
|
190
|
+
if (ctx) {
|
|
191
|
+
return withDepth(ctx, () => ({
|
|
192
|
+
allOf: type.types.map((t) => buildSchema(t, checker, ctx))
|
|
193
|
+
}));
|
|
182
194
|
}
|
|
183
|
-
return;
|
|
195
|
+
return { allOf: type.types.map((t) => buildSchema(t, checker, ctx)) };
|
|
184
196
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const text = tag.text.trim();
|
|
195
|
-
const fenceMatch = text.match(/^```(\w*)\n([\s\S]*?)\n?```$/);
|
|
196
|
-
if (fenceMatch) {
|
|
197
|
-
const lang = fenceMatch[1] || undefined;
|
|
198
|
-
const code = fenceMatch[2].trim();
|
|
199
|
-
const example = { code };
|
|
200
|
-
if (lang && ["ts", "js", "tsx", "jsx", "shell", "json"].includes(lang)) {
|
|
201
|
-
example.language = lang;
|
|
197
|
+
if (checker.isArrayType(type)) {
|
|
198
|
+
const typeRef2 = type;
|
|
199
|
+
const elementType = typeRef2.typeArguments?.[0];
|
|
200
|
+
if (elementType) {
|
|
201
|
+
if (ctx) {
|
|
202
|
+
return withDepth(ctx, () => ({
|
|
203
|
+
type: "array",
|
|
204
|
+
items: buildSchema(elementType, checker, ctx)
|
|
205
|
+
}));
|
|
202
206
|
}
|
|
203
|
-
|
|
204
|
-
} else if (text) {
|
|
205
|
-
examples.push({ code: text });
|
|
207
|
+
return { type: "array", items: buildSchema(elementType, checker, ctx) };
|
|
206
208
|
}
|
|
209
|
+
return { type: "array" };
|
|
207
210
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (jsDocComments && jsDocComments.length > 0) {
|
|
219
|
-
const firstDoc = jsDocComments[0];
|
|
220
|
-
if (firstDoc.comment) {
|
|
221
|
-
description = typeof firstDoc.comment === "string" ? firstDoc.comment : ts2.getTextOfJSDocComment(firstDoc.comment);
|
|
211
|
+
if (checker.isTupleType(type)) {
|
|
212
|
+
const typeRef2 = type;
|
|
213
|
+
const elementTypes = typeRef2.typeArguments ?? [];
|
|
214
|
+
if (ctx) {
|
|
215
|
+
return withDepth(ctx, () => ({
|
|
216
|
+
type: "array",
|
|
217
|
+
prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
|
|
218
|
+
minItems: elementTypes.length,
|
|
219
|
+
maxItems: elementTypes.length
|
|
220
|
+
}));
|
|
222
221
|
}
|
|
222
|
+
return {
|
|
223
|
+
type: "array",
|
|
224
|
+
prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
|
|
225
|
+
minItems: elementTypes.length,
|
|
226
|
+
maxItems: elementTypes.length
|
|
227
|
+
};
|
|
223
228
|
}
|
|
224
|
-
const
|
|
225
|
-
|
|
229
|
+
const typeRef = type;
|
|
230
|
+
if (typeRef.target && typeRef.typeArguments && typeRef.typeArguments.length > 0) {
|
|
231
|
+
const symbol2 = typeRef.target.getSymbol();
|
|
232
|
+
const name = symbol2?.getName();
|
|
233
|
+
if (name && BUILTIN_TYPES.has(name)) {
|
|
234
|
+
return { $ref: `#/types/${name}` };
|
|
235
|
+
}
|
|
236
|
+
if (name && (isBuiltinGeneric(name) || !isAnonymous(typeRef.target))) {
|
|
237
|
+
if (ctx) {
|
|
238
|
+
return withDepth(ctx, () => ({
|
|
239
|
+
$ref: `#/types/${name}`,
|
|
240
|
+
typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
$ref: `#/types/${name}`,
|
|
245
|
+
typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (type.flags & ts.TypeFlags.Object) {
|
|
250
|
+
const callSignatures = type.getCallSignatures();
|
|
251
|
+
if (callSignatures.length > 0) {
|
|
252
|
+
return buildFunctionSchema(callSignatures, checker, ctx);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const symbol = type.getSymbol() || type.aliasSymbol;
|
|
256
|
+
if (symbol && !isAnonymous(type)) {
|
|
257
|
+
const name = symbol.getName();
|
|
258
|
+
if (isPrimitiveName(name)) {
|
|
259
|
+
return { type: name };
|
|
260
|
+
}
|
|
261
|
+
if (BUILTIN_TYPES.has(name)) {
|
|
262
|
+
return { $ref: `#/types/${name}` };
|
|
263
|
+
}
|
|
264
|
+
if (!name.startsWith("__")) {
|
|
265
|
+
return { $ref: `#/types/${name}` };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (type.flags & ts.TypeFlags.Object) {
|
|
269
|
+
const objectType = type;
|
|
270
|
+
const properties = type.getProperties();
|
|
271
|
+
if (properties.length > 0 || objectType.objectFlags & ts.ObjectFlags.Anonymous) {
|
|
272
|
+
return buildObjectSchema(properties, checker, ctx);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return { type: checker.typeToString(type) };
|
|
226
276
|
}
|
|
227
|
-
function
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
277
|
+
function buildFunctionSchema(callSignatures, checker, ctx) {
|
|
278
|
+
const buildSignatures = () => {
|
|
279
|
+
const signatures = callSignatures.map((sig) => {
|
|
280
|
+
const params = sig.getParameters().map((param) => {
|
|
281
|
+
const paramType = checker.getTypeOfSymbolAtLocation(param, param.valueDeclaration);
|
|
282
|
+
return {
|
|
283
|
+
name: param.getName(),
|
|
284
|
+
schema: buildSchema(paramType, checker, ctx),
|
|
285
|
+
required: !(param.flags & ts.SymbolFlags.Optional)
|
|
286
|
+
};
|
|
287
|
+
});
|
|
288
|
+
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
289
|
+
return {
|
|
290
|
+
parameters: params,
|
|
291
|
+
returns: {
|
|
292
|
+
schema: buildSchema(returnType, checker, ctx)
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
return signatures;
|
|
232
297
|
};
|
|
298
|
+
if (ctx) {
|
|
299
|
+
return withDepth(ctx, () => ({ type: "function", signatures: buildSignatures() }));
|
|
300
|
+
}
|
|
301
|
+
return { type: "function", signatures: buildSignatures() };
|
|
233
302
|
}
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const
|
|
243
|
-
|
|
303
|
+
function buildObjectSchema(properties, checker, ctx) {
|
|
304
|
+
const buildProps = () => {
|
|
305
|
+
const props = {};
|
|
306
|
+
const required = [];
|
|
307
|
+
for (const prop of properties) {
|
|
308
|
+
const propName = prop.getName();
|
|
309
|
+
if (propName.startsWith("_"))
|
|
310
|
+
continue;
|
|
311
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
312
|
+
props[propName] = buildSchema(propType, checker, ctx);
|
|
313
|
+
if (!(prop.flags & ts.SymbolFlags.Optional)) {
|
|
314
|
+
required.push(propName);
|
|
315
|
+
}
|
|
244
316
|
}
|
|
317
|
+
return {
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: props,
|
|
320
|
+
...required.length > 0 ? { required } : {}
|
|
321
|
+
};
|
|
322
|
+
};
|
|
323
|
+
if (ctx) {
|
|
324
|
+
return withDepth(ctx, buildProps);
|
|
245
325
|
}
|
|
246
|
-
return;
|
|
326
|
+
return buildProps();
|
|
247
327
|
}
|
|
248
|
-
function
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const name = tp.name.text;
|
|
254
|
-
let constraint;
|
|
255
|
-
if (tp.constraint) {
|
|
256
|
-
const constraintType = checker.getTypeAtLocation(tp.constraint);
|
|
257
|
-
constraint = checker.typeToString(constraintType);
|
|
258
|
-
}
|
|
259
|
-
let defaultType;
|
|
260
|
-
if (tp.default) {
|
|
261
|
-
const defType = checker.getTypeAtLocation(tp.default);
|
|
262
|
-
defaultType = checker.typeToString(defType);
|
|
263
|
-
}
|
|
328
|
+
function isPureRefSchema(schema) {
|
|
329
|
+
return typeof schema === "object" && Object.keys(schema).length === 1 && "$ref" in schema;
|
|
330
|
+
}
|
|
331
|
+
function withDescription(schema, description) {
|
|
332
|
+
if (isPureRefSchema(schema)) {
|
|
264
333
|
return {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
...defaultType ? { default: defaultType } : {}
|
|
334
|
+
allOf: [schema],
|
|
335
|
+
description
|
|
268
336
|
};
|
|
269
|
-
}
|
|
337
|
+
}
|
|
338
|
+
return { ...schema, description };
|
|
270
339
|
}
|
|
271
|
-
function
|
|
272
|
-
if (
|
|
273
|
-
return
|
|
340
|
+
function schemaIsAny(schema) {
|
|
341
|
+
if (typeof schema === "string") {
|
|
342
|
+
return schema === "any";
|
|
274
343
|
}
|
|
275
|
-
|
|
276
|
-
if (jsDocTags.some((tag) => tag.name.toLowerCase() === "deprecated")) {
|
|
344
|
+
if ("type" in schema && schema.type === "any" && Object.keys(schema).length === 1) {
|
|
277
345
|
return true;
|
|
278
346
|
}
|
|
279
|
-
for (const declaration of symbol.getDeclarations() ?? []) {
|
|
280
|
-
if (ts2.getJSDocDeprecatedTag(declaration)) {
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
347
|
return false;
|
|
285
348
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
import ts3 from "typescript";
|
|
290
|
-
var DEFAULT_COMPILER_OPTIONS = {
|
|
291
|
-
target: ts3.ScriptTarget.Latest,
|
|
292
|
-
module: ts3.ModuleKind.CommonJS,
|
|
293
|
-
lib: ["lib.es2021.d.ts"],
|
|
294
|
-
declaration: true,
|
|
295
|
-
moduleResolution: ts3.ModuleResolutionKind.NodeJs
|
|
296
|
-
};
|
|
297
|
-
function createProgram({
|
|
298
|
-
entryFile,
|
|
299
|
-
baseDir = path.dirname(entryFile),
|
|
300
|
-
content
|
|
301
|
-
}) {
|
|
302
|
-
const configPath = ts3.findConfigFile(baseDir, ts3.sys.fileExists, "tsconfig.json");
|
|
303
|
-
let compilerOptions = { ...DEFAULT_COMPILER_OPTIONS };
|
|
304
|
-
if (configPath) {
|
|
305
|
-
const configFile = ts3.readConfigFile(configPath, ts3.sys.readFile);
|
|
306
|
-
const parsedConfig = ts3.parseJsonConfigFileContent(configFile.config, ts3.sys, path.dirname(configPath));
|
|
307
|
-
compilerOptions = { ...compilerOptions, ...parsedConfig.options };
|
|
349
|
+
function schemasAreEqual(left, right) {
|
|
350
|
+
if (typeof left !== typeof right) {
|
|
351
|
+
return false;
|
|
308
352
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
compilerOptions = { ...compilerOptions, allowJs: false, checkJs: false };
|
|
353
|
+
if (typeof left === "string" && typeof right === "string") {
|
|
354
|
+
return left === right;
|
|
312
355
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (content !== undefined) {
|
|
316
|
-
inMemorySource = ts3.createSourceFile(entryFile, content, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TS);
|
|
317
|
-
const originalGetSourceFile = compilerHost.getSourceFile.bind(compilerHost);
|
|
318
|
-
compilerHost.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
319
|
-
if (fileName === entryFile) {
|
|
320
|
-
return inMemorySource;
|
|
321
|
-
}
|
|
322
|
-
return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
|
323
|
-
};
|
|
356
|
+
if (left == null || right == null) {
|
|
357
|
+
return left === right;
|
|
324
358
|
}
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
359
|
+
const normalize = (value) => {
|
|
360
|
+
if (Array.isArray(value)) {
|
|
361
|
+
return value.map((item) => normalize(item));
|
|
362
|
+
}
|
|
363
|
+
if (value && typeof value === "object") {
|
|
364
|
+
const sortedEntries = Object.entries(value).map(([key, val]) => [key, normalize(val)]).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
|
|
365
|
+
return Object.fromEntries(sortedEntries);
|
|
366
|
+
}
|
|
367
|
+
return value;
|
|
333
368
|
};
|
|
369
|
+
return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right));
|
|
334
370
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
371
|
+
function deduplicateSchemas(schemas) {
|
|
372
|
+
const result = [];
|
|
373
|
+
for (const schema of schemas) {
|
|
374
|
+
const isDuplicate = result.some((existing) => schemasAreEqual(existing, schema));
|
|
375
|
+
if (!isDuplicate) {
|
|
376
|
+
result.push(schema);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
function findDiscriminatorProperty(unionTypes, checker) {
|
|
382
|
+
const memberProps = [];
|
|
383
|
+
for (const t of unionTypes) {
|
|
384
|
+
if (t.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) {
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
const props = t.getProperties();
|
|
388
|
+
if (!props || props.length === 0) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const propValues = new Map;
|
|
392
|
+
for (const prop of props) {
|
|
393
|
+
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
394
|
+
if (!declaration)
|
|
395
|
+
continue;
|
|
396
|
+
try {
|
|
397
|
+
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
398
|
+
if (propType.isStringLiteral()) {
|
|
399
|
+
propValues.set(prop.getName(), propType.value);
|
|
400
|
+
} else if (propType.isNumberLiteral()) {
|
|
401
|
+
propValues.set(prop.getName(), propType.value);
|
|
402
|
+
}
|
|
403
|
+
} catch {}
|
|
404
|
+
}
|
|
405
|
+
memberProps.push(propValues);
|
|
406
|
+
}
|
|
407
|
+
if (memberProps.length < 2) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const firstMember = memberProps[0];
|
|
411
|
+
for (const [propName, firstValue] of firstMember) {
|
|
412
|
+
const values = new Set([firstValue]);
|
|
413
|
+
let isDiscriminator = true;
|
|
414
|
+
for (let i = 1;i < memberProps.length; i++) {
|
|
415
|
+
const value = memberProps[i].get(propName);
|
|
416
|
+
if (value === undefined) {
|
|
417
|
+
isDiscriminator = false;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
if (values.has(value)) {
|
|
421
|
+
isDiscriminator = false;
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
values.add(value);
|
|
425
|
+
}
|
|
426
|
+
if (isDiscriminator) {
|
|
427
|
+
return propName;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/ast/registry.ts
|
|
434
|
+
import ts2 from "typescript";
|
|
362
435
|
var PRIMITIVES2 = new Set([
|
|
363
436
|
"string",
|
|
364
437
|
"number",
|
|
365
438
|
"boolean",
|
|
366
439
|
"void",
|
|
440
|
+
"any",
|
|
367
441
|
"undefined",
|
|
368
442
|
"null",
|
|
369
|
-
"any",
|
|
370
|
-
"unknown",
|
|
371
443
|
"never",
|
|
444
|
+
"unknown",
|
|
372
445
|
"object",
|
|
373
446
|
"symbol",
|
|
374
447
|
"bigint"
|
|
375
448
|
]);
|
|
376
|
-
var
|
|
449
|
+
var BUILTINS = new Set([
|
|
377
450
|
"Array",
|
|
378
|
-
"
|
|
451
|
+
"ArrayBuffer",
|
|
452
|
+
"ArrayBufferLike",
|
|
453
|
+
"ArrayLike",
|
|
379
454
|
"Promise",
|
|
380
|
-
"PromiseLike",
|
|
381
455
|
"Map",
|
|
382
456
|
"Set",
|
|
383
457
|
"WeakMap",
|
|
384
458
|
"WeakSet",
|
|
385
|
-
"Iterable",
|
|
386
|
-
"Iterator",
|
|
387
|
-
"IterableIterator",
|
|
388
|
-
"AsyncIterable",
|
|
389
|
-
"AsyncIterator",
|
|
390
|
-
"AsyncIterableIterator",
|
|
391
|
-
"Generator",
|
|
392
|
-
"AsyncGenerator",
|
|
393
|
-
"Partial",
|
|
394
|
-
"Required",
|
|
395
|
-
"Readonly",
|
|
396
|
-
"Pick",
|
|
397
|
-
"Omit",
|
|
398
|
-
"Record",
|
|
399
|
-
"Exclude",
|
|
400
|
-
"Extract",
|
|
401
|
-
"NonNullable",
|
|
402
|
-
"Parameters",
|
|
403
|
-
"ReturnType",
|
|
404
|
-
"ConstructorParameters",
|
|
405
|
-
"InstanceType",
|
|
406
|
-
"Awaited"
|
|
407
|
-
]);
|
|
408
|
-
var BUILTIN_TYPES = new Set([
|
|
409
459
|
"Date",
|
|
410
460
|
"RegExp",
|
|
411
461
|
"Error",
|
|
412
462
|
"Function",
|
|
413
|
-
"
|
|
414
|
-
"
|
|
415
|
-
"
|
|
463
|
+
"Object",
|
|
464
|
+
"String",
|
|
465
|
+
"Number",
|
|
466
|
+
"Boolean",
|
|
467
|
+
"Symbol",
|
|
468
|
+
"BigInt",
|
|
416
469
|
"Uint8Array",
|
|
417
470
|
"Int8Array",
|
|
418
471
|
"Uint16Array",
|
|
@@ -422,230 +475,158 @@ var BUILTIN_TYPES = new Set([
|
|
|
422
475
|
"Float32Array",
|
|
423
476
|
"Float64Array",
|
|
424
477
|
"BigInt64Array",
|
|
425
|
-
"BigUint64Array"
|
|
478
|
+
"BigUint64Array",
|
|
479
|
+
"DataView",
|
|
480
|
+
"ReadonlyArray",
|
|
481
|
+
"Readonly",
|
|
482
|
+
"Partial",
|
|
483
|
+
"Required",
|
|
484
|
+
"Pick",
|
|
485
|
+
"Omit",
|
|
486
|
+
"Record",
|
|
487
|
+
"Exclude",
|
|
488
|
+
"Extract",
|
|
489
|
+
"NonNullable",
|
|
490
|
+
"Parameters",
|
|
491
|
+
"ReturnType",
|
|
492
|
+
"ConstructorParameters",
|
|
493
|
+
"InstanceType",
|
|
494
|
+
"ThisType",
|
|
495
|
+
"Awaited",
|
|
496
|
+
"PromiseLike",
|
|
497
|
+
"Iterable",
|
|
498
|
+
"Iterator",
|
|
499
|
+
"IterableIterator",
|
|
500
|
+
"Generator",
|
|
501
|
+
"AsyncGenerator",
|
|
502
|
+
"AsyncIterable",
|
|
503
|
+
"AsyncIterator",
|
|
504
|
+
"AsyncIterableIterator",
|
|
505
|
+
"SharedArrayBuffer",
|
|
506
|
+
"Atomics",
|
|
507
|
+
"JSON",
|
|
508
|
+
"Math",
|
|
509
|
+
"console",
|
|
510
|
+
"globalThis"
|
|
426
511
|
]);
|
|
427
|
-
function
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
function isBuiltinGeneric(name) {
|
|
431
|
-
return BUILTIN_GENERICS.has(name);
|
|
432
|
-
}
|
|
433
|
-
function isAnonymous(type) {
|
|
434
|
-
const symbol = type.getSymbol() || type.aliasSymbol;
|
|
435
|
-
if (!symbol)
|
|
512
|
+
function isGenericTypeParameter(name) {
|
|
513
|
+
if (/^[A-Z]$/.test(name))
|
|
436
514
|
return true;
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
try {
|
|
443
|
-
return fn();
|
|
444
|
-
} finally {
|
|
445
|
-
ctx.currentDepth--;
|
|
446
|
-
}
|
|
515
|
+
if (/^T[A-Z]/.test(name))
|
|
516
|
+
return true;
|
|
517
|
+
if (["Key", "Value", "Item", "Element"].includes(name))
|
|
518
|
+
return true;
|
|
519
|
+
return false;
|
|
447
520
|
}
|
|
448
|
-
function
|
|
449
|
-
|
|
521
|
+
function isExternalType(decl) {
|
|
522
|
+
const sourceFile = decl.getSourceFile();
|
|
523
|
+
if (!sourceFile)
|
|
450
524
|
return false;
|
|
451
|
-
return
|
|
525
|
+
return sourceFile.fileName.includes("node_modules");
|
|
452
526
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
if (callSignatures.length > 0) {
|
|
460
|
-
return buildFunctionSchema(callSignatures, checker, ctx);
|
|
461
|
-
}
|
|
462
|
-
const symbol2 = type.getSymbol() || type.aliasSymbol;
|
|
463
|
-
if (symbol2) {
|
|
464
|
-
return { $ref: `#/types/${symbol2.getName()}` };
|
|
465
|
-
}
|
|
466
|
-
return { type: checker.typeToString(type) };
|
|
467
|
-
}
|
|
468
|
-
if (ctx && type.flags & ts4.TypeFlags.Object) {
|
|
469
|
-
ctx.visitedTypes.add(type);
|
|
470
|
-
}
|
|
471
|
-
if (type.flags & ts4.TypeFlags.String)
|
|
472
|
-
return { type: "string" };
|
|
473
|
-
if (type.flags & ts4.TypeFlags.Number)
|
|
474
|
-
return { type: "number" };
|
|
475
|
-
if (type.flags & ts4.TypeFlags.Boolean)
|
|
476
|
-
return { type: "boolean" };
|
|
477
|
-
if (type.flags & ts4.TypeFlags.Undefined)
|
|
478
|
-
return { type: "undefined" };
|
|
479
|
-
if (type.flags & ts4.TypeFlags.Null)
|
|
480
|
-
return { type: "null" };
|
|
481
|
-
if (type.flags & ts4.TypeFlags.Void)
|
|
482
|
-
return { type: "void" };
|
|
483
|
-
if (type.flags & ts4.TypeFlags.Any)
|
|
484
|
-
return { type: "any" };
|
|
485
|
-
if (type.flags & ts4.TypeFlags.Unknown)
|
|
486
|
-
return { type: "unknown" };
|
|
487
|
-
if (type.flags & ts4.TypeFlags.Never)
|
|
488
|
-
return { type: "never" };
|
|
489
|
-
if (type.flags & ts4.TypeFlags.BigInt)
|
|
490
|
-
return { type: "bigint" };
|
|
491
|
-
if (type.flags & ts4.TypeFlags.ESSymbol)
|
|
492
|
-
return { type: "symbol" };
|
|
493
|
-
if (type.flags & ts4.TypeFlags.StringLiteral) {
|
|
494
|
-
const literal = type.value;
|
|
495
|
-
return { type: "string", enum: [literal] };
|
|
496
|
-
}
|
|
497
|
-
if (type.flags & ts4.TypeFlags.NumberLiteral) {
|
|
498
|
-
const literal = type.value;
|
|
499
|
-
return { type: "number", enum: [literal] };
|
|
500
|
-
}
|
|
501
|
-
if (type.flags & ts4.TypeFlags.BooleanLiteral) {
|
|
502
|
-
const intrinsicName = type.intrinsicName;
|
|
503
|
-
return { type: "boolean", enum: [intrinsicName === "true"] };
|
|
504
|
-
}
|
|
505
|
-
if (type.isUnion()) {
|
|
506
|
-
const types = type.types;
|
|
507
|
-
const allStringLiterals = types.every((t) => t.flags & ts4.TypeFlags.StringLiteral);
|
|
508
|
-
if (allStringLiterals) {
|
|
509
|
-
const enumValues = types.map((t) => t.value);
|
|
510
|
-
return { type: "string", enum: enumValues };
|
|
511
|
-
}
|
|
512
|
-
const allNumberLiterals = types.every((t) => t.flags & ts4.TypeFlags.NumberLiteral);
|
|
513
|
-
if (allNumberLiterals) {
|
|
514
|
-
const enumValues = types.map((t) => t.value);
|
|
515
|
-
return { type: "number", enum: enumValues };
|
|
516
|
-
}
|
|
517
|
-
if (ctx) {
|
|
518
|
-
return withDepth(ctx, () => ({
|
|
519
|
-
anyOf: types.map((t) => buildSchema(t, checker, ctx))
|
|
520
|
-
}));
|
|
521
|
-
}
|
|
522
|
-
return { anyOf: types.map((t) => buildSchema(t, checker, ctx)) };
|
|
527
|
+
|
|
528
|
+
class TypeRegistry {
|
|
529
|
+
types = new Map;
|
|
530
|
+
processing = new Set;
|
|
531
|
+
add(type) {
|
|
532
|
+
this.types.set(type.id, type);
|
|
523
533
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
return withDepth(ctx, () => ({
|
|
527
|
-
allOf: type.types.map((t) => buildSchema(t, checker, ctx))
|
|
528
|
-
}));
|
|
529
|
-
}
|
|
530
|
-
return { allOf: type.types.map((t) => buildSchema(t, checker, ctx)) };
|
|
534
|
+
get(id) {
|
|
535
|
+
return this.types.get(id);
|
|
531
536
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
const elementType = typeRef2.typeArguments?.[0];
|
|
535
|
-
if (elementType) {
|
|
536
|
-
if (ctx) {
|
|
537
|
-
return withDepth(ctx, () => ({
|
|
538
|
-
type: "array",
|
|
539
|
-
items: buildSchema(elementType, checker, ctx)
|
|
540
|
-
}));
|
|
541
|
-
}
|
|
542
|
-
return { type: "array", items: buildSchema(elementType, checker, ctx) };
|
|
543
|
-
}
|
|
544
|
-
return { type: "array" };
|
|
537
|
+
has(id) {
|
|
538
|
+
return this.types.has(id);
|
|
545
539
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const elementTypes = typeRef2.typeArguments ?? [];
|
|
549
|
-
if (ctx) {
|
|
550
|
-
return withDepth(ctx, () => ({
|
|
551
|
-
type: "array",
|
|
552
|
-
prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
|
|
553
|
-
minItems: elementTypes.length,
|
|
554
|
-
maxItems: elementTypes.length
|
|
555
|
-
}));
|
|
556
|
-
}
|
|
557
|
-
return {
|
|
558
|
-
type: "array",
|
|
559
|
-
prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
|
|
560
|
-
minItems: elementTypes.length,
|
|
561
|
-
maxItems: elementTypes.length
|
|
562
|
-
};
|
|
540
|
+
getAll() {
|
|
541
|
+
return Array.from(this.types.values());
|
|
563
542
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if (
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
543
|
+
registerType(type, ctx) {
|
|
544
|
+
const symbol = type.getSymbol() || type.aliasSymbol;
|
|
545
|
+
if (!symbol)
|
|
546
|
+
return;
|
|
547
|
+
const name = symbol.getName();
|
|
548
|
+
if (PRIMITIVES2.has(name))
|
|
549
|
+
return;
|
|
550
|
+
if (BUILTINS.has(name))
|
|
551
|
+
return;
|
|
552
|
+
if (name.startsWith("__"))
|
|
553
|
+
return;
|
|
554
|
+
if (symbol.flags & ts2.SymbolFlags.EnumMember)
|
|
555
|
+
return;
|
|
556
|
+
if (symbol.flags & ts2.SymbolFlags.TypeParameter)
|
|
557
|
+
return;
|
|
558
|
+
if (symbol.flags & ts2.SymbolFlags.Method)
|
|
559
|
+
return;
|
|
560
|
+
if (symbol.flags & ts2.SymbolFlags.Function)
|
|
561
|
+
return;
|
|
562
|
+
if (isGenericTypeParameter(name))
|
|
563
|
+
return;
|
|
564
|
+
if (this.has(name))
|
|
565
|
+
return name;
|
|
566
|
+
if (this.processing.has(name))
|
|
567
|
+
return name;
|
|
568
|
+
this.processing.add(name);
|
|
569
|
+
try {
|
|
570
|
+
const specType = this.buildSpecType(type, symbol, ctx);
|
|
571
|
+
if (specType) {
|
|
572
|
+
this.add(specType);
|
|
573
|
+
return specType.id;
|
|
577
574
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
if (type.flags & ts4.TypeFlags.Object) {
|
|
585
|
-
const callSignatures = type.getCallSignatures();
|
|
586
|
-
if (callSignatures.length > 0) {
|
|
587
|
-
return buildFunctionSchema(callSignatures, checker, ctx);
|
|
575
|
+
} finally {
|
|
576
|
+
this.processing.delete(name);
|
|
588
577
|
}
|
|
578
|
+
return;
|
|
589
579
|
}
|
|
590
|
-
|
|
591
|
-
if (symbol && !isAnonymous(type)) {
|
|
580
|
+
buildSpecType(type, symbol, ctx) {
|
|
592
581
|
const name = symbol.getName();
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
schema: buildSchema(paramType, checker, ctx),
|
|
620
|
-
required: !(param.flags & ts4.SymbolFlags.Optional)
|
|
621
|
-
};
|
|
622
|
-
});
|
|
623
|
-
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
624
|
-
return {
|
|
625
|
-
parameters: params,
|
|
626
|
-
returns: {
|
|
627
|
-
schema: buildSchema(returnType, checker, ctx)
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
});
|
|
631
|
-
return signatures;
|
|
632
|
-
};
|
|
633
|
-
if (ctx) {
|
|
634
|
-
return withDepth(ctx, () => ({ type: "function", signatures: buildSignatures() }));
|
|
582
|
+
const decl = symbol.declarations?.[0];
|
|
583
|
+
const checker = ctx.typeChecker;
|
|
584
|
+
let kind = "type";
|
|
585
|
+
const external = decl ? isExternalType(decl) : false;
|
|
586
|
+
if (decl) {
|
|
587
|
+
if (ts2.isClassDeclaration(decl))
|
|
588
|
+
kind = "class";
|
|
589
|
+
else if (ts2.isInterfaceDeclaration(decl))
|
|
590
|
+
kind = "interface";
|
|
591
|
+
else if (ts2.isEnumDeclaration(decl))
|
|
592
|
+
kind = "enum";
|
|
593
|
+
}
|
|
594
|
+
if (external) {
|
|
595
|
+
kind = "external";
|
|
596
|
+
}
|
|
597
|
+
let schema = buildSchema(type, checker, ctx);
|
|
598
|
+
if (this.isSelfRef(schema, name)) {
|
|
599
|
+
schema = this.buildObjectSchemaFromType(type, checker, ctx);
|
|
600
|
+
}
|
|
601
|
+
return {
|
|
602
|
+
id: name,
|
|
603
|
+
name,
|
|
604
|
+
kind,
|
|
605
|
+
schema,
|
|
606
|
+
...external ? { external: true } : {}
|
|
607
|
+
};
|
|
635
608
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
609
|
+
isSelfRef(schema, typeName) {
|
|
610
|
+
if (typeof schema !== "object" || schema === null)
|
|
611
|
+
return false;
|
|
612
|
+
const obj = schema;
|
|
613
|
+
return obj.$ref === `#/types/${typeName}`;
|
|
614
|
+
}
|
|
615
|
+
buildObjectSchemaFromType(type, checker, ctx) {
|
|
616
|
+
const properties = type.getProperties();
|
|
617
|
+
if (properties.length === 0) {
|
|
618
|
+
return { type: checker.typeToString(type) };
|
|
619
|
+
}
|
|
640
620
|
const props = {};
|
|
641
621
|
const required = [];
|
|
642
|
-
for (const prop of properties) {
|
|
622
|
+
for (const prop of properties.slice(0, 20)) {
|
|
643
623
|
const propName = prop.getName();
|
|
644
624
|
if (propName.startsWith("_"))
|
|
645
625
|
continue;
|
|
646
626
|
const propType = checker.getTypeOfSymbol(prop);
|
|
627
|
+
this.registerType(propType, ctx);
|
|
647
628
|
props[propName] = buildSchema(propType, checker, ctx);
|
|
648
|
-
if (!(prop.flags &
|
|
629
|
+
if (!(prop.flags & ts2.SymbolFlags.Optional)) {
|
|
649
630
|
required.push(propName);
|
|
650
631
|
}
|
|
651
632
|
}
|
|
@@ -654,115 +635,156 @@ function buildObjectSchema(properties, checker, ctx) {
|
|
|
654
635
|
properties: props,
|
|
655
636
|
...required.length > 0 ? { required } : {}
|
|
656
637
|
};
|
|
657
|
-
};
|
|
658
|
-
if (ctx) {
|
|
659
|
-
return withDepth(ctx, buildProps);
|
|
660
638
|
}
|
|
661
|
-
return buildProps();
|
|
662
|
-
}
|
|
663
|
-
function isPureRefSchema(schema) {
|
|
664
|
-
return typeof schema === "object" && Object.keys(schema).length === 1 && "$ref" in schema;
|
|
665
639
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
640
|
+
|
|
641
|
+
// src/ast/utils.ts
|
|
642
|
+
import ts3 from "typescript";
|
|
643
|
+
function parseExamplesFromTags(tags) {
|
|
644
|
+
const examples = [];
|
|
645
|
+
for (const tag of tags) {
|
|
646
|
+
if (tag.name !== "example")
|
|
647
|
+
continue;
|
|
648
|
+
const text = tag.text.trim();
|
|
649
|
+
const fenceMatch = text.match(/^```(\w*)\n([\s\S]*?)\n?```$/);
|
|
650
|
+
if (fenceMatch) {
|
|
651
|
+
const lang = fenceMatch[1] || undefined;
|
|
652
|
+
const code = fenceMatch[2].trim();
|
|
653
|
+
const example = { code };
|
|
654
|
+
if (lang && ["ts", "js", "tsx", "jsx", "shell", "json"].includes(lang)) {
|
|
655
|
+
example.language = lang;
|
|
656
|
+
}
|
|
657
|
+
examples.push(example);
|
|
658
|
+
} else if (text) {
|
|
659
|
+
examples.push({ code: text });
|
|
660
|
+
}
|
|
672
661
|
}
|
|
673
|
-
return
|
|
662
|
+
return examples;
|
|
674
663
|
}
|
|
675
|
-
function
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
664
|
+
function getJSDocComment(node) {
|
|
665
|
+
const jsDocTags = ts3.getJSDocTags(node);
|
|
666
|
+
const tags = jsDocTags.map((tag) => ({
|
|
667
|
+
name: tag.tagName.text,
|
|
668
|
+
text: typeof tag.comment === "string" ? tag.comment : ts3.getTextOfJSDocComment(tag.comment) ?? ""
|
|
669
|
+
}));
|
|
670
|
+
const jsDocComments = node.jsDoc;
|
|
671
|
+
let description;
|
|
672
|
+
if (jsDocComments && jsDocComments.length > 0) {
|
|
673
|
+
const firstDoc = jsDocComments[0];
|
|
674
|
+
if (firstDoc.comment) {
|
|
675
|
+
description = typeof firstDoc.comment === "string" ? firstDoc.comment : ts3.getTextOfJSDocComment(firstDoc.comment);
|
|
676
|
+
}
|
|
681
677
|
}
|
|
682
|
-
|
|
678
|
+
const examples = parseExamplesFromTags(tags);
|
|
679
|
+
return { description, tags, examples };
|
|
683
680
|
}
|
|
684
|
-
function
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
return left === right;
|
|
690
|
-
}
|
|
691
|
-
if (left == null || right == null) {
|
|
692
|
-
return left === right;
|
|
693
|
-
}
|
|
694
|
-
const normalize = (value) => {
|
|
695
|
-
if (Array.isArray(value)) {
|
|
696
|
-
return value.map((item) => normalize(item));
|
|
697
|
-
}
|
|
698
|
-
if (value && typeof value === "object") {
|
|
699
|
-
const sortedEntries = Object.entries(value).map(([key, val]) => [key, normalize(val)]).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
|
|
700
|
-
return Object.fromEntries(sortedEntries);
|
|
701
|
-
}
|
|
702
|
-
return value;
|
|
681
|
+
function getSourceLocation(node, sourceFile) {
|
|
682
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
683
|
+
return {
|
|
684
|
+
file: sourceFile.fileName,
|
|
685
|
+
line: line + 1
|
|
703
686
|
};
|
|
704
|
-
return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right));
|
|
705
687
|
}
|
|
706
|
-
function
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
688
|
+
function getParamDescription(propertyName, jsdocTags, inferredAlias) {
|
|
689
|
+
for (const tag of jsdocTags) {
|
|
690
|
+
if (tag.tagName.text !== "param")
|
|
691
|
+
continue;
|
|
692
|
+
const paramTag = tag;
|
|
693
|
+
const tagParamName = paramTag.name?.getText() ?? "";
|
|
694
|
+
const isMatch = tagParamName === propertyName || inferredAlias && tagParamName === `${inferredAlias}.${propertyName}` || tagParamName.endsWith(`.${propertyName}`);
|
|
695
|
+
if (isMatch) {
|
|
696
|
+
const comment = typeof tag.comment === "string" ? tag.comment : ts3.getTextOfJSDocComment(tag.comment);
|
|
697
|
+
return comment?.trim() || undefined;
|
|
712
698
|
}
|
|
713
699
|
}
|
|
714
|
-
return
|
|
700
|
+
return;
|
|
715
701
|
}
|
|
716
|
-
function
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
702
|
+
function extractTypeParameters(node, checker) {
|
|
703
|
+
if (!node.typeParameters || node.typeParameters.length === 0) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
return node.typeParameters.map((tp) => {
|
|
707
|
+
const name = tp.name.text;
|
|
708
|
+
let constraint;
|
|
709
|
+
if (tp.constraint) {
|
|
710
|
+
const constraintType = checker.getTypeAtLocation(tp.constraint);
|
|
711
|
+
constraint = checker.typeToString(constraintType);
|
|
721
712
|
}
|
|
722
|
-
|
|
723
|
-
if (
|
|
724
|
-
|
|
713
|
+
let defaultType;
|
|
714
|
+
if (tp.default) {
|
|
715
|
+
const defType = checker.getTypeAtLocation(tp.default);
|
|
716
|
+
defaultType = checker.typeToString(defType);
|
|
725
717
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
718
|
+
return {
|
|
719
|
+
name,
|
|
720
|
+
...constraint ? { constraint } : {},
|
|
721
|
+
...defaultType ? { default: defaultType } : {}
|
|
722
|
+
};
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
function isSymbolDeprecated(symbol) {
|
|
726
|
+
if (!symbol) {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
const jsDocTags = symbol.getJsDocTags();
|
|
730
|
+
if (jsDocTags.some((tag) => tag.name.toLowerCase() === "deprecated")) {
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
for (const declaration of symbol.getDeclarations() ?? []) {
|
|
734
|
+
if (ts3.getJSDocDeprecatedTag(declaration)) {
|
|
735
|
+
return true;
|
|
739
736
|
}
|
|
740
|
-
memberProps.push(propValues);
|
|
741
737
|
}
|
|
742
|
-
|
|
743
|
-
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/compiler/program.ts
|
|
742
|
+
import * as path from "node:path";
|
|
743
|
+
import ts4 from "typescript";
|
|
744
|
+
var DEFAULT_COMPILER_OPTIONS = {
|
|
745
|
+
target: ts4.ScriptTarget.Latest,
|
|
746
|
+
module: ts4.ModuleKind.CommonJS,
|
|
747
|
+
lib: ["lib.es2021.d.ts"],
|
|
748
|
+
declaration: true,
|
|
749
|
+
moduleResolution: ts4.ModuleResolutionKind.NodeJs
|
|
750
|
+
};
|
|
751
|
+
function createProgram({
|
|
752
|
+
entryFile,
|
|
753
|
+
baseDir = path.dirname(entryFile),
|
|
754
|
+
content
|
|
755
|
+
}) {
|
|
756
|
+
const configPath = ts4.findConfigFile(baseDir, ts4.sys.fileExists, "tsconfig.json");
|
|
757
|
+
let compilerOptions = { ...DEFAULT_COMPILER_OPTIONS };
|
|
758
|
+
if (configPath) {
|
|
759
|
+
const configFile = ts4.readConfigFile(configPath, ts4.sys.readFile);
|
|
760
|
+
const parsedConfig = ts4.parseJsonConfigFileContent(configFile.config, ts4.sys, path.dirname(configPath));
|
|
761
|
+
compilerOptions = { ...compilerOptions, ...parsedConfig.options };
|
|
744
762
|
}
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
if (
|
|
756
|
-
|
|
757
|
-
break;
|
|
763
|
+
const allowJsVal = compilerOptions.allowJs;
|
|
764
|
+
if (typeof allowJsVal === "boolean" && allowJsVal) {
|
|
765
|
+
compilerOptions = { ...compilerOptions, allowJs: false, checkJs: false };
|
|
766
|
+
}
|
|
767
|
+
const compilerHost = ts4.createCompilerHost(compilerOptions, true);
|
|
768
|
+
let inMemorySource;
|
|
769
|
+
if (content !== undefined) {
|
|
770
|
+
inMemorySource = ts4.createSourceFile(entryFile, content, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TS);
|
|
771
|
+
const originalGetSourceFile = compilerHost.getSourceFile.bind(compilerHost);
|
|
772
|
+
compilerHost.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
773
|
+
if (fileName === entryFile) {
|
|
774
|
+
return inMemorySource;
|
|
758
775
|
}
|
|
759
|
-
|
|
760
|
-
}
|
|
761
|
-
if (isDiscriminator) {
|
|
762
|
-
return propName;
|
|
763
|
-
}
|
|
776
|
+
return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
|
777
|
+
};
|
|
764
778
|
}
|
|
765
|
-
|
|
779
|
+
const program = ts4.createProgram([entryFile], compilerOptions, compilerHost);
|
|
780
|
+
const sourceFile = inMemorySource ?? program.getSourceFile(entryFile);
|
|
781
|
+
return {
|
|
782
|
+
program,
|
|
783
|
+
compilerHost,
|
|
784
|
+
compilerOptions,
|
|
785
|
+
sourceFile,
|
|
786
|
+
configPath
|
|
787
|
+
};
|
|
766
788
|
}
|
|
767
789
|
|
|
768
790
|
// src/types/parameters.ts
|
|
@@ -890,8 +912,8 @@ function registerReferencedTypes(type, ctx, depth = 0) {
|
|
|
890
912
|
if (!isPrimitive) {
|
|
891
913
|
ctx.visitedTypes.add(type);
|
|
892
914
|
}
|
|
893
|
-
const { typeChecker: checker, typeRegistry
|
|
894
|
-
typeRegistry.registerType(type,
|
|
915
|
+
const { typeChecker: checker, typeRegistry } = ctx;
|
|
916
|
+
typeRegistry.registerType(type, ctx);
|
|
895
917
|
const typeArgs = type.typeArguments;
|
|
896
918
|
if (typeArgs) {
|
|
897
919
|
for (const arg of typeArgs) {
|
|
@@ -1673,24 +1695,82 @@ function serializeDeclaration(declaration, exportSymbol, _targetSymbol, exportNa
|
|
|
1673
1695
|
result = serializeVariable(declaration, varStatement, ctx);
|
|
1674
1696
|
}
|
|
1675
1697
|
} else if (ts8.isNamespaceExport(declaration) || ts8.isModuleDeclaration(declaration)) {
|
|
1676
|
-
result = serializeNamespaceExport(exportSymbol, exportName);
|
|
1698
|
+
result = serializeNamespaceExport(exportSymbol, exportName, ctx);
|
|
1677
1699
|
} else if (ts8.isSourceFile(declaration)) {
|
|
1678
|
-
result = serializeNamespaceExport(exportSymbol, exportName);
|
|
1700
|
+
result = serializeNamespaceExport(exportSymbol, exportName, ctx);
|
|
1679
1701
|
}
|
|
1680
1702
|
if (result) {
|
|
1681
1703
|
result = withExportName(result, exportName);
|
|
1682
1704
|
}
|
|
1683
1705
|
return result;
|
|
1684
1706
|
}
|
|
1685
|
-
function serializeNamespaceExport(symbol, exportName) {
|
|
1707
|
+
function serializeNamespaceExport(symbol, exportName, ctx) {
|
|
1686
1708
|
const { description, tags, examples } = getJSDocFromExportSymbol(symbol);
|
|
1709
|
+
const members = [];
|
|
1710
|
+
const checker = ctx.program.getTypeChecker();
|
|
1711
|
+
let targetSymbol = symbol;
|
|
1712
|
+
if (symbol.flags & ts8.SymbolFlags.Alias) {
|
|
1713
|
+
const aliased = checker.getAliasedSymbol(symbol);
|
|
1714
|
+
if (aliased && aliased !== symbol) {
|
|
1715
|
+
targetSymbol = aliased;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
const nsExports = checker.getExportsOfModule(targetSymbol);
|
|
1719
|
+
for (const memberSymbol of nsExports) {
|
|
1720
|
+
const memberName = memberSymbol.getName();
|
|
1721
|
+
const member = serializeNamespaceMember(memberSymbol, memberName, ctx);
|
|
1722
|
+
if (member) {
|
|
1723
|
+
members.push(member);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1687
1726
|
return {
|
|
1688
1727
|
id: exportName,
|
|
1689
1728
|
name: exportName,
|
|
1690
1729
|
kind: "namespace",
|
|
1691
1730
|
description,
|
|
1692
1731
|
tags,
|
|
1693
|
-
...examples.length > 0 ? { examples } : {}
|
|
1732
|
+
...examples.length > 0 ? { examples } : {},
|
|
1733
|
+
...members.length > 0 ? { members } : {}
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
function serializeNamespaceMember(symbol, memberName, ctx) {
|
|
1737
|
+
const checker = ctx.program.getTypeChecker();
|
|
1738
|
+
let targetSymbol = symbol;
|
|
1739
|
+
if (symbol.flags & ts8.SymbolFlags.Alias) {
|
|
1740
|
+
const aliased = checker.getAliasedSymbol(symbol);
|
|
1741
|
+
if (aliased && aliased !== symbol) {
|
|
1742
|
+
targetSymbol = aliased;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
const declarations = targetSymbol.declarations ?? [];
|
|
1746
|
+
const declaration = targetSymbol.valueDeclaration || declarations.find((d) => d.kind !== ts8.SyntaxKind.ExportSpecifier) || declarations[0];
|
|
1747
|
+
if (!declaration)
|
|
1748
|
+
return null;
|
|
1749
|
+
let kind = "variable";
|
|
1750
|
+
if (ts8.isFunctionDeclaration(declaration) || ts8.isFunctionExpression(declaration)) {
|
|
1751
|
+
kind = "function";
|
|
1752
|
+
} else if (ts8.isClassDeclaration(declaration)) {
|
|
1753
|
+
kind = "class";
|
|
1754
|
+
} else if (ts8.isInterfaceDeclaration(declaration)) {
|
|
1755
|
+
kind = "interface";
|
|
1756
|
+
} else if (ts8.isTypeAliasDeclaration(declaration)) {
|
|
1757
|
+
kind = "type";
|
|
1758
|
+
} else if (ts8.isEnumDeclaration(declaration)) {
|
|
1759
|
+
kind = "enum";
|
|
1760
|
+
} else if (ts8.isVariableDeclaration(declaration)) {
|
|
1761
|
+
const type = checker.getTypeAtLocation(declaration);
|
|
1762
|
+
const callSignatures = type.getCallSignatures();
|
|
1763
|
+
if (callSignatures.length > 0) {
|
|
1764
|
+
kind = "function";
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
const docComment = targetSymbol.getDocumentationComment(checker);
|
|
1768
|
+
const description = docComment.map((c) => c.text).join(`
|
|
1769
|
+
`) || undefined;
|
|
1770
|
+
return {
|
|
1771
|
+
name: memberName,
|
|
1772
|
+
kind,
|
|
1773
|
+
...description ? { description } : {}
|
|
1694
1774
|
};
|
|
1695
1775
|
}
|
|
1696
1776
|
function getJSDocFromExportSymbol(symbol) {
|
|
@@ -1787,4 +1867,4 @@ async function getPackageMeta(entryFile, baseDir) {
|
|
|
1787
1867
|
} catch {}
|
|
1788
1868
|
return { name: path2.basename(searchDir) };
|
|
1789
1869
|
}
|
|
1790
|
-
export {
|
|
1870
|
+
export { BUILTIN_TYPE_SCHEMAS, isPrimitiveName, isBuiltinGeneric, isAnonymous, buildSchema, isPureRefSchema, withDescription, schemaIsAny, schemasAreEqual, deduplicateSchemas, findDiscriminatorProperty, TypeRegistry, getJSDocComment, getSourceLocation, getParamDescription, extractTypeParameters, isSymbolDeprecated, createProgram, extractParameters, registerReferencedTypes, serializeClass, serializeEnum, serializeFunctionExport, serializeInterface, serializeTypeAlias, serializeVariable, extract };
|