@openpkg-ts/extract 0.14.5 → 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;
347
+ return false;
348
+ }
349
+ function schemasAreEqual(left, right) {
350
+ if (typeof left !== typeof right) {
351
+ return false;
352
+ }
353
+ if (typeof left === "string" && typeof right === "string") {
354
+ return left === right;
355
+ }
356
+ if (left == null || right == null) {
357
+ return left === right;
358
+ }
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;
368
+ };
369
+ return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right));
370
+ }
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);
282
377
  }
283
378
  }
284
- return false;
379
+ return result;
285
380
  }
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 };
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);
308
406
  }
309
- const allowJsVal = compilerOptions.allowJs;
310
- if (typeof allowJsVal === "boolean" && allowJsVal) {
311
- compilerOptions = { ...compilerOptions, allowJs: false, checkJs: false };
407
+ if (memberProps.length < 2) {
408
+ return;
312
409
  }
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;
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;
321
419
  }
322
- return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
323
- };
420
+ if (values.has(value)) {
421
+ isDiscriminator = false;
422
+ break;
423
+ }
424
+ values.add(value);
425
+ }
426
+ if (isDiscriminator) {
427
+ return propName;
428
+ }
324
429
  }
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
333
- };
430
+ return;
334
431
  }
335
432
 
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
- };
433
+ // src/ast/registry.ts
434
+ import ts2 from "typescript";
362
435
  var PRIMITIVES2 = new Set([
363
436
  "string",
364
437
  "number",
365
- "boolean",
366
- "void",
367
- "undefined",
368
- "null",
369
- "any",
370
- "unknown",
371
- "never",
372
- "object",
373
- "symbol",
374
- "bigint"
375
- ]);
376
- var BUILTIN_GENERICS = new Set([
377
- "Array",
378
- "ReadonlyArray",
379
- "Promise",
380
- "PromiseLike",
381
- "Map",
382
- "Set",
383
- "WeakMap",
384
- "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"
438
+ "boolean",
439
+ "void",
440
+ "any",
441
+ "undefined",
442
+ "null",
443
+ "never",
444
+ "unknown",
445
+ "object",
446
+ "symbol",
447
+ "bigint"
407
448
  ]);
408
- var BUILTIN_TYPES = new Set([
449
+ var BUILTINS = new Set([
450
+ "Array",
451
+ "ArrayBuffer",
452
+ "ArrayBufferLike",
453
+ "ArrayLike",
454
+ "Promise",
455
+ "Map",
456
+ "Set",
457
+ "WeakMap",
458
+ "WeakSet",
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 };
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";
595
593
  }
596
- if (BUILTIN_TYPES.has(name)) {
597
- return { $ref: `#/types/${name}` };
594
+ if (external) {
595
+ kind = "external";
598
596
  }
599
- if (!name.startsWith("__")) {
600
- return { $ref: `#/types/${name}` };
597
+ let schema = buildSchema(type, checker, ctx);
598
+ if (this.isSelfRef(schema, name)) {
599
+ schema = this.buildObjectSchemaFromType(type, checker, ctx);
601
600
  }
601
+ return {
602
+ id: name,
603
+ name,
604
+ kind,
605
+ schema,
606
+ ...external ? { external: true } : {}
607
+ };
602
608
  }
603
- if (type.flags & ts4.TypeFlags.Object) {
604
- const objectType = type;
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) {
605
616
  const properties = type.getProperties();
606
- if (properties.length > 0 || objectType.objectFlags & ts4.ObjectFlags.Anonymous) {
607
- return buildObjectSchema(properties, checker, ctx);
617
+ if (properties.length === 0) {
618
+ return { type: checker.typeToString(type) };
608
619
  }
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() }));
635
- }
636
- return { type: "function", signatures: buildSignatures() };
637
- }
638
- function buildObjectSchema(properties, checker, ctx) {
639
- const buildProps = () => {
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
639
  }
663
- function isPureRefSchema(schema) {
664
- return typeof schema === "object" && Object.keys(schema).length === 1 && "$ref" in schema;
665
- }
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) {
@@ -1845,4 +1867,4 @@ async function getPackageMeta(entryFile, baseDir) {
1845
1867
  } catch {}
1846
1868
  return { name: path2.basename(searchDir) };
1847
1869
  }
1848
- 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 };