@openpkg-ts/extract 0.14.5 → 0.16.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 +1 -1
- package/dist/shared/{chunk-mhaxcbzq.js → chunk-2v40ecks.js} +771 -636
- package/dist/src/index.d.ts +55 -32
- 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
|
-
|
|
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";
|
|
598
593
|
}
|
|
599
|
-
if (
|
|
600
|
-
|
|
594
|
+
if (external) {
|
|
595
|
+
kind = "external";
|
|
601
596
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const properties = type.getProperties();
|
|
606
|
-
if (properties.length > 0 || objectType.objectFlags & ts4.ObjectFlags.Anonymous) {
|
|
607
|
-
return buildObjectSchema(properties, checker, ctx);
|
|
597
|
+
let schema = buildSchema(type, checker, ctx);
|
|
598
|
+
if (this.isSelfRef(schema, name)) {
|
|
599
|
+
schema = this.buildObjectSchemaFromType(type, checker, ctx);
|
|
608
600
|
}
|
|
601
|
+
return {
|
|
602
|
+
id: name,
|
|
603
|
+
name,
|
|
604
|
+
kind,
|
|
605
|
+
schema,
|
|
606
|
+
...external ? { external: true } : {}
|
|
607
|
+
};
|
|
609
608
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
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() }));
|
|
635
|
-
}
|
|
636
|
-
return { type: "function", signatures: buildSignatures() };
|
|
637
|
-
}
|
|
638
|
-
function buildObjectSchema(properties, checker, ctx) {
|
|
639
|
-
const buildProps = () => {
|
|
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) {
|
|
@@ -1587,14 +1609,26 @@ async function extract(options) {
|
|
|
1587
1609
|
}
|
|
1588
1610
|
const meta = await getPackageMeta(entryFile, baseDir);
|
|
1589
1611
|
const types = ctx.typeRegistry.getAll();
|
|
1590
|
-
const
|
|
1591
|
-
for (const
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1612
|
+
const forgottenExports = collectForgottenExports(exports, types, program, sourceFile);
|
|
1613
|
+
for (const forgotten of forgottenExports) {
|
|
1614
|
+
const refSummary = forgotten.referencedBy.slice(0, 3).map((r) => `${r.exportName} (${r.location})`).join(", ");
|
|
1615
|
+
const moreRefs = forgotten.referencedBy.length > 3 ? ` +${forgotten.referencedBy.length - 3} more` : "";
|
|
1616
|
+
if (forgotten.isExternal) {
|
|
1617
|
+
diagnostics.push({
|
|
1618
|
+
message: `External type '${forgotten.name}' referenced by: ${refSummary}${moreRefs}`,
|
|
1619
|
+
severity: "info",
|
|
1620
|
+
code: "EXTERNAL_TYPE_REF",
|
|
1621
|
+
suggestion: forgotten.definedIn ? `Type is from: ${forgotten.definedIn}` : "Type is from an external package"
|
|
1622
|
+
});
|
|
1623
|
+
} else {
|
|
1624
|
+
diagnostics.push({
|
|
1625
|
+
message: `Forgotten export: '${forgotten.name}' referenced by: ${refSummary}${moreRefs}`,
|
|
1626
|
+
severity: "warning",
|
|
1627
|
+
code: "FORGOTTEN_EXPORT",
|
|
1628
|
+
suggestion: forgotten.fix ?? `Export this type from your public API`,
|
|
1629
|
+
location: forgotten.definedIn ? { file: forgotten.definedIn } : undefined
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1598
1632
|
}
|
|
1599
1633
|
const externalTypes = types.filter((t) => t.kind === "external");
|
|
1600
1634
|
if (externalTypes.length > 0) {
|
|
@@ -1615,33 +1649,134 @@ async function extract(options) {
|
|
|
1615
1649
|
timestamp: new Date().toISOString()
|
|
1616
1650
|
}
|
|
1617
1651
|
};
|
|
1618
|
-
|
|
1652
|
+
const internalForgotten = forgottenExports.filter((f) => !f.isExternal);
|
|
1653
|
+
return {
|
|
1654
|
+
spec,
|
|
1655
|
+
diagnostics,
|
|
1656
|
+
...internalForgotten.length > 0 ? { forgottenExports: internalForgotten } : {}
|
|
1657
|
+
};
|
|
1619
1658
|
}
|
|
1620
|
-
function
|
|
1659
|
+
function collectAllRefsWithContext(obj, refs, state) {
|
|
1621
1660
|
if (obj === null || obj === undefined)
|
|
1622
1661
|
return;
|
|
1623
1662
|
if (Array.isArray(obj)) {
|
|
1624
|
-
for (
|
|
1625
|
-
|
|
1663
|
+
for (let i = 0;i < obj.length; i++) {
|
|
1664
|
+
collectAllRefsWithContext(obj[i], refs, {
|
|
1665
|
+
...state,
|
|
1666
|
+
path: [...state.path, `[${i}]`]
|
|
1667
|
+
});
|
|
1626
1668
|
}
|
|
1627
1669
|
return;
|
|
1628
1670
|
}
|
|
1629
1671
|
if (typeof obj === "object") {
|
|
1630
1672
|
const record = obj;
|
|
1631
1673
|
if (typeof record.$ref === "string" && record.$ref.startsWith("#/types/")) {
|
|
1632
|
-
|
|
1674
|
+
const typeName = record.$ref.slice("#/types/".length);
|
|
1675
|
+
const existing = refs.get(typeName) ?? [];
|
|
1676
|
+
existing.push({
|
|
1677
|
+
typeName,
|
|
1678
|
+
exportName: state.exportName,
|
|
1679
|
+
location: state.location,
|
|
1680
|
+
path: state.path.join(".") || undefined
|
|
1681
|
+
});
|
|
1682
|
+
refs.set(typeName, existing);
|
|
1683
|
+
}
|
|
1684
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1685
|
+
let newLocation = state.location;
|
|
1686
|
+
if (key === "returnType" || key === "returns")
|
|
1687
|
+
newLocation = "return";
|
|
1688
|
+
else if (key === "parameters" || key === "params")
|
|
1689
|
+
newLocation = "parameter";
|
|
1690
|
+
else if (key === "properties" || key === "members")
|
|
1691
|
+
newLocation = "property";
|
|
1692
|
+
else if (key === "extends" || key === "implements")
|
|
1693
|
+
newLocation = "extends";
|
|
1694
|
+
else if (key === "typeParameters" || key === "typeParams")
|
|
1695
|
+
newLocation = "type-parameter";
|
|
1696
|
+
collectAllRefsWithContext(value, refs, {
|
|
1697
|
+
...state,
|
|
1698
|
+
location: newLocation,
|
|
1699
|
+
path: [...state.path, key]
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
function findTypeDefinition(typeName, program, sourceFile) {
|
|
1705
|
+
const checker = program.getTypeChecker();
|
|
1706
|
+
const findInNode = (node) => {
|
|
1707
|
+
if ((ts8.isInterfaceDeclaration(node) || ts8.isTypeAliasDeclaration(node) || ts8.isClassDeclaration(node) || ts8.isEnumDeclaration(node)) && node.name?.text === typeName) {
|
|
1708
|
+
const sf = node.getSourceFile();
|
|
1709
|
+
return sf.fileName;
|
|
1633
1710
|
}
|
|
1634
|
-
|
|
1635
|
-
|
|
1711
|
+
return ts8.forEachChild(node, findInNode);
|
|
1712
|
+
};
|
|
1713
|
+
const entryResult = findInNode(sourceFile);
|
|
1714
|
+
if (entryResult)
|
|
1715
|
+
return entryResult;
|
|
1716
|
+
for (const sf of program.getSourceFiles()) {
|
|
1717
|
+
if (sf.isDeclarationFile && !sf.fileName.includes("node_modules")) {
|
|
1718
|
+
const result = findInNode(sf);
|
|
1719
|
+
if (result)
|
|
1720
|
+
return result;
|
|
1636
1721
|
}
|
|
1637
1722
|
}
|
|
1723
|
+
const symbol = checker.resolveName(typeName, sourceFile, ts8.SymbolFlags.Type, false);
|
|
1724
|
+
if (symbol?.declarations?.[0]) {
|
|
1725
|
+
return symbol.declarations[0].getSourceFile().fileName;
|
|
1726
|
+
}
|
|
1727
|
+
return;
|
|
1638
1728
|
}
|
|
1639
|
-
function
|
|
1729
|
+
function isExternalType2(definedIn) {
|
|
1730
|
+
if (!definedIn)
|
|
1731
|
+
return true;
|
|
1732
|
+
return definedIn.includes("node_modules");
|
|
1733
|
+
}
|
|
1734
|
+
function hasInternalTag(typeName, program, sourceFile) {
|
|
1735
|
+
const checker = program.getTypeChecker();
|
|
1736
|
+
const symbol = checker.resolveName(typeName, sourceFile, ts8.SymbolFlags.Type, false);
|
|
1737
|
+
if (!symbol)
|
|
1738
|
+
return false;
|
|
1739
|
+
const jsTags = symbol.getJsDocTags();
|
|
1740
|
+
return jsTags.some((tag) => tag.name === "internal");
|
|
1741
|
+
}
|
|
1742
|
+
function collectForgottenExports(exports, types, program, sourceFile) {
|
|
1640
1743
|
const definedTypes = new Set(types.map((t) => t.id));
|
|
1641
|
-
const referencedTypes = new
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1744
|
+
const referencedTypes = new Map;
|
|
1745
|
+
for (const exp of exports) {
|
|
1746
|
+
collectAllRefsWithContext(exp, referencedTypes, {
|
|
1747
|
+
exportName: exp.id || exp.name,
|
|
1748
|
+
location: "property",
|
|
1749
|
+
path: []
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
for (const type of types) {
|
|
1753
|
+
collectAllRefsWithContext(type, referencedTypes, {
|
|
1754
|
+
exportName: type.id,
|
|
1755
|
+
location: "property",
|
|
1756
|
+
path: []
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
const forgottenExports = [];
|
|
1760
|
+
for (const [typeName, references] of referencedTypes) {
|
|
1761
|
+
if (definedTypes.has(typeName))
|
|
1762
|
+
continue;
|
|
1763
|
+
if (BUILTIN_TYPES2.has(typeName))
|
|
1764
|
+
continue;
|
|
1765
|
+
if (shouldSkipDanglingRef(typeName))
|
|
1766
|
+
continue;
|
|
1767
|
+
if (hasInternalTag(typeName, program, sourceFile))
|
|
1768
|
+
continue;
|
|
1769
|
+
const definedIn = findTypeDefinition(typeName, program, sourceFile);
|
|
1770
|
+
const isExternal = isExternalType2(definedIn);
|
|
1771
|
+
forgottenExports.push({
|
|
1772
|
+
name: typeName,
|
|
1773
|
+
definedIn,
|
|
1774
|
+
referencedBy: references,
|
|
1775
|
+
isExternal,
|
|
1776
|
+
fix: isExternal ? undefined : `export { ${typeName} } from '${definedIn ?? "./types"}'`
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
return forgottenExports;
|
|
1645
1780
|
}
|
|
1646
1781
|
function resolveExportTarget(symbol, checker) {
|
|
1647
1782
|
let targetSymbol = symbol;
|
|
@@ -1845,4 +1980,4 @@ async function getPackageMeta(entryFile, baseDir) {
|
|
|
1845
1980
|
} catch {}
|
|
1846
1981
|
return { name: path2.basename(searchDir) };
|
|
1847
1982
|
}
|
|
1848
|
-
export {
|
|
1983
|
+
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 };
|