@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.
@@ -1,54 +1,63 @@
1
- // src/ast/registry.ts
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
- "never",
34
+ "any",
12
35
  "unknown",
36
+ "never",
13
37
  "object",
14
38
  "symbol",
15
39
  "bigint"
16
40
  ]);
17
- var BUILTINS = new Set([
41
+ var BUILTIN_GENERICS = new Set([
18
42
  "Array",
19
- "ArrayBuffer",
20
- "ArrayBufferLike",
21
- "ArrayLike",
43
+ "ReadonlyArray",
22
44
  "Promise",
45
+ "PromiseLike",
23
46
  "Map",
24
47
  "Set",
25
48
  "WeakMap",
26
49
  "WeakSet",
27
- "Date",
28
- "RegExp",
29
- "Error",
30
- "Function",
31
- "Object",
32
- "String",
33
- "Number",
34
- "Boolean",
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
- "ThisType",
63
- "Awaited",
64
- "PromiseLike",
65
- "Iterable",
66
- "Iterator",
67
- "IterableIterator",
68
- "Generator",
69
- "AsyncGenerator",
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
- "Atomics",
75
- "JSON",
76
- "Math",
77
- "console",
78
- "globalThis"
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 isGenericTypeParameter(name) {
81
- if (/^[A-Z]$/.test(name))
82
- return true;
83
- if (/^T[A-Z]/.test(name))
84
- return true;
85
- if (["Key", "Value", "Item", "Element"].includes(name))
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
- return false;
102
+ const name = symbol.getName();
103
+ return name.startsWith("__") || name === "";
88
104
  }
89
- function isExternalType(decl) {
90
- const sourceFile = decl.getSourceFile();
91
- if (!sourceFile)
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 sourceFile.fileName.includes("node_modules");
116
+ return ctx.currentDepth >= ctx.maxTypeDepth;
94
117
  }
95
-
96
- class TypeRegistry {
97
- types = new Map;
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
- get(id) {
103
- return this.types.get(id);
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
- has(id) {
106
- return this.types.has(id);
133
+ if (ctx && type.flags & ts.TypeFlags.Object) {
134
+ ctx.visitedTypes.add(type);
107
135
  }
108
- getAll() {
109
- return Array.from(this.types.values());
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
- registerType(type, checker, exportedIds) {
112
- const symbol = type.getSymbol() || type.aliasSymbol;
113
- if (!symbol)
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
- buildStubType(symbol, checker) {
149
- const name = symbol.getName();
150
- const decl = symbol.declarations?.[0];
151
- let kind = "type";
152
- const external = decl ? isExternalType(decl) : false;
153
- if (decl) {
154
- if (ts.isClassDeclaration(decl))
155
- kind = "class";
156
- else if (ts.isInterfaceDeclaration(decl))
157
- kind = "interface";
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
- if (external) {
162
- kind = "external";
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
- const type = checker.getDeclaredTypeOfSymbol(symbol);
165
- const typeString = checker.typeToString(type);
166
- return {
167
- id: name,
168
- name,
169
- kind,
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
- registerFromSymbol(symbol, checker) {
175
- const name = symbol.getName();
176
- if (this.has(name))
177
- return this.get(name);
178
- const specType = this.buildStubType(symbol, checker);
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
- // src/ast/utils.ts
188
- import ts2 from "typescript";
189
- function parseExamplesFromTags(tags) {
190
- const examples = [];
191
- for (const tag of tags) {
192
- if (tag.name !== "example")
193
- continue;
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
- examples.push(example);
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
- return examples;
209
- }
210
- function getJSDocComment(node) {
211
- const jsDocTags = ts2.getJSDocTags(node);
212
- const tags = jsDocTags.map((tag) => ({
213
- name: tag.tagName.text,
214
- text: typeof tag.comment === "string" ? tag.comment : ts2.getTextOfJSDocComment(tag.comment) ?? ""
215
- }));
216
- const jsDocComments = node.jsDoc;
217
- let description;
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 examples = parseExamplesFromTags(tags);
225
- return { description, tags, examples };
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 getSourceLocation(node, sourceFile) {
228
- const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
229
- return {
230
- file: sourceFile.fileName,
231
- line: line + 1
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 getParamDescription(propertyName, jsdocTags, inferredAlias) {
235
- for (const tag of jsdocTags) {
236
- if (tag.tagName.text !== "param")
237
- continue;
238
- const paramTag = tag;
239
- const tagParamName = paramTag.name?.getText() ?? "";
240
- const isMatch = tagParamName === propertyName || inferredAlias && tagParamName === `${inferredAlias}.${propertyName}` || tagParamName.endsWith(`.${propertyName}`);
241
- if (isMatch) {
242
- const comment = typeof tag.comment === "string" ? tag.comment : ts2.getTextOfJSDocComment(tag.comment);
243
- return comment?.trim() || undefined;
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 extractTypeParameters(node, checker) {
249
- if (!node.typeParameters || node.typeParameters.length === 0) {
250
- return;
251
- }
252
- return node.typeParameters.map((tp) => {
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
- name,
266
- ...constraint ? { constraint } : {},
267
- ...defaultType ? { default: defaultType } : {}
334
+ allOf: [schema],
335
+ description
268
336
  };
269
- });
337
+ }
338
+ return { ...schema, description };
270
339
  }
271
- function isSymbolDeprecated(symbol) {
272
- if (!symbol) {
273
- return false;
340
+ function schemaIsAny(schema) {
341
+ if (typeof schema === "string") {
342
+ return schema === "any";
274
343
  }
275
- const jsDocTags = symbol.getJsDocTags();
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
- // src/compiler/program.ts
288
- import * as path from "node:path";
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
- const allowJsVal = compilerOptions.allowJs;
310
- if (typeof allowJsVal === "boolean" && allowJsVal) {
311
- compilerOptions = { ...compilerOptions, allowJs: false, checkJs: false };
353
+ if (typeof left === "string" && typeof right === "string") {
354
+ return left === right;
312
355
  }
313
- const compilerHost = ts3.createCompilerHost(compilerOptions, true);
314
- let inMemorySource;
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 program = ts3.createProgram([entryFile], compilerOptions, compilerHost);
326
- const sourceFile = inMemorySource ?? program.getSourceFile(entryFile);
327
- return {
328
- program,
329
- compilerHost,
330
- compilerOptions,
331
- sourceFile,
332
- configPath
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
- // src/types/schema-builder.ts
337
- import ts4 from "typescript";
338
- var BUILTIN_TYPE_SCHEMAS = {
339
- Date: { type: "string", format: "date-time" },
340
- RegExp: { type: "object", description: "RegExp" },
341
- Error: { type: "object" },
342
- Promise: { type: "object" },
343
- Map: { type: "object" },
344
- Set: { type: "object" },
345
- WeakMap: { type: "object" },
346
- WeakSet: { type: "object" },
347
- Function: { type: "object" },
348
- ArrayBuffer: { type: "string", format: "binary" },
349
- ArrayBufferLike: { type: "string", format: "binary" },
350
- DataView: { type: "string", format: "binary" },
351
- Uint8Array: { type: "string", format: "byte" },
352
- Uint16Array: { type: "string", format: "byte" },
353
- Uint32Array: { type: "string", format: "byte" },
354
- Int8Array: { type: "string", format: "byte" },
355
- Int16Array: { type: "string", format: "byte" },
356
- Int32Array: { type: "string", format: "byte" },
357
- Float32Array: { type: "string", format: "byte" },
358
- Float64Array: { type: "string", format: "byte" },
359
- BigInt64Array: { type: "string", format: "byte" },
360
- BigUint64Array: { type: "string", format: "byte" }
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 BUILTIN_GENERICS = new Set([
449
+ var BUILTINS = new Set([
377
450
  "Array",
378
- "ReadonlyArray",
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
- "ArrayBuffer",
414
- "SharedArrayBuffer",
415
- "DataView",
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 isPrimitiveName(name) {
428
- return PRIMITIVES2.has(name);
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
- const name = symbol.getName();
438
- return name.startsWith("__") || name === "";
439
- }
440
- function withDepth(ctx, fn) {
441
- ctx.currentDepth++;
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 isAtMaxDepth(ctx) {
449
- if (!ctx)
521
+ function isExternalType(decl) {
522
+ const sourceFile = decl.getSourceFile();
523
+ if (!sourceFile)
450
524
  return false;
451
- return ctx.currentDepth >= ctx.maxTypeDepth;
525
+ return sourceFile.fileName.includes("node_modules");
452
526
  }
453
- function buildSchema(type, checker, ctx, _depth = 0) {
454
- if (isAtMaxDepth(ctx)) {
455
- return { type: checker.typeToString(type) };
456
- }
457
- if (ctx?.visitedTypes.has(type)) {
458
- const callSignatures = type.getCallSignatures();
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
- if (type.isIntersection()) {
525
- if (ctx) {
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
- if (checker.isArrayType(type)) {
533
- const typeRef2 = type;
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
- if (checker.isTupleType(type)) {
547
- const typeRef2 = type;
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
- const typeRef = type;
565
- if (typeRef.target && typeRef.typeArguments && typeRef.typeArguments.length > 0) {
566
- const symbol2 = typeRef.target.getSymbol();
567
- const name = symbol2?.getName();
568
- if (name && BUILTIN_TYPES.has(name)) {
569
- return { $ref: `#/types/${name}` };
570
- }
571
- if (name && (isBuiltinGeneric(name) || !isAnonymous(typeRef.target))) {
572
- if (ctx) {
573
- return withDepth(ctx, () => ({
574
- $ref: `#/types/${name}`,
575
- typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
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
- return {
579
- $ref: `#/types/${name}`,
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
- const symbol = type.getSymbol() || type.aliasSymbol;
591
- if (symbol && !isAnonymous(type)) {
580
+ buildSpecType(type, symbol, ctx) {
592
581
  const name = symbol.getName();
593
- if (isPrimitiveName(name)) {
594
- return { type: name };
595
- }
596
- if (BUILTIN_TYPES.has(name)) {
597
- return { $ref: `#/types/${name}` };
598
- }
599
- if (!name.startsWith("__")) {
600
- return { $ref: `#/types/${name}` };
601
- }
602
- }
603
- if (type.flags & ts4.TypeFlags.Object) {
604
- const objectType = type;
605
- const properties = type.getProperties();
606
- if (properties.length > 0 || objectType.objectFlags & ts4.ObjectFlags.Anonymous) {
607
- return buildObjectSchema(properties, checker, ctx);
608
- }
609
- }
610
- return { type: checker.typeToString(type) };
611
- }
612
- function buildFunctionSchema(callSignatures, checker, ctx) {
613
- const buildSignatures = () => {
614
- const signatures = callSignatures.map((sig) => {
615
- const params = sig.getParameters().map((param) => {
616
- const paramType = checker.getTypeOfSymbolAtLocation(param, param.valueDeclaration);
617
- return {
618
- name: param.getName(),
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
- 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 & ts4.SymbolFlags.Optional)) {
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
- function withDescription(schema, description) {
667
- if (isPureRefSchema(schema)) {
668
- return {
669
- allOf: [schema],
670
- description
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 { ...schema, description };
662
+ return examples;
674
663
  }
675
- function schemaIsAny(schema) {
676
- if (typeof schema === "string") {
677
- return schema === "any";
678
- }
679
- if ("type" in schema && schema.type === "any" && Object.keys(schema).length === 1) {
680
- return true;
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
- return false;
678
+ const examples = parseExamplesFromTags(tags);
679
+ return { description, tags, examples };
683
680
  }
684
- function schemasAreEqual(left, right) {
685
- if (typeof left !== typeof right) {
686
- return false;
687
- }
688
- if (typeof left === "string" && typeof right === "string") {
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 deduplicateSchemas(schemas) {
707
- const result = [];
708
- for (const schema of schemas) {
709
- const isDuplicate = result.some((existing) => schemasAreEqual(existing, schema));
710
- if (!isDuplicate) {
711
- result.push(schema);
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 result;
700
+ return;
715
701
  }
716
- function findDiscriminatorProperty(unionTypes, checker) {
717
- const memberProps = [];
718
- for (const t of unionTypes) {
719
- if (t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined)) {
720
- continue;
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
- const props = t.getProperties();
723
- if (!props || props.length === 0) {
724
- return;
713
+ let defaultType;
714
+ if (tp.default) {
715
+ const defType = checker.getTypeAtLocation(tp.default);
716
+ defaultType = checker.typeToString(defType);
725
717
  }
726
- const propValues = new Map;
727
- for (const prop of props) {
728
- const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
729
- if (!declaration)
730
- continue;
731
- try {
732
- const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
733
- if (propType.isStringLiteral()) {
734
- propValues.set(prop.getName(), propType.value);
735
- } else if (propType.isNumberLiteral()) {
736
- propValues.set(prop.getName(), propType.value);
737
- }
738
- } catch {}
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
- if (memberProps.length < 2) {
743
- return;
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 firstMember = memberProps[0];
746
- for (const [propName, firstValue] of firstMember) {
747
- const values = new Set([firstValue]);
748
- let isDiscriminator = true;
749
- for (let i = 1;i < memberProps.length; i++) {
750
- const value = memberProps[i].get(propName);
751
- if (value === undefined) {
752
- isDiscriminator = false;
753
- break;
754
- }
755
- if (values.has(value)) {
756
- isDiscriminator = false;
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
- values.add(value);
760
- }
761
- if (isDiscriminator) {
762
- return propName;
763
- }
776
+ return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
777
+ };
764
778
  }
765
- return;
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, exportedIds } = ctx;
894
- typeRegistry.registerType(type, checker, exportedIds);
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 { TypeRegistry, getJSDocComment, getSourceLocation, getParamDescription, extractTypeParameters, isSymbolDeprecated, createProgram, BUILTIN_TYPE_SCHEMAS, isPrimitiveName, isBuiltinGeneric, isAnonymous, buildSchema, isPureRefSchema, withDescription, schemaIsAny, schemasAreEqual, deduplicateSchemas, findDiscriminatorProperty, extractParameters, registerReferencedTypes, serializeClass, serializeEnum, serializeFunctionExport, serializeInterface, serializeTypeAlias, serializeVariable, extract };
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 };