@openpkg-ts/extract 0.11.4 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/tspec.js +1 -1
- package/dist/shared/chunk-ksf9k654.js +1662 -0
- package/dist/src/index.d.ts +40 -21
- package/dist/src/index.js +8 -27
- package/package.json +3 -2
- package/dist/shared/chunk-twaykyxs.js +0 -681
|
@@ -1,681 +0,0 @@
|
|
|
1
|
-
// src/ast/registry.ts
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
var PRIMITIVES = new Set([
|
|
4
|
-
"string",
|
|
5
|
-
"number",
|
|
6
|
-
"boolean",
|
|
7
|
-
"void",
|
|
8
|
-
"any",
|
|
9
|
-
"undefined",
|
|
10
|
-
"null",
|
|
11
|
-
"never",
|
|
12
|
-
"unknown",
|
|
13
|
-
"object",
|
|
14
|
-
"symbol",
|
|
15
|
-
"bigint"
|
|
16
|
-
]);
|
|
17
|
-
var BUILTINS = new Set([
|
|
18
|
-
"Array",
|
|
19
|
-
"ArrayBuffer",
|
|
20
|
-
"ArrayBufferLike",
|
|
21
|
-
"ArrayLike",
|
|
22
|
-
"Promise",
|
|
23
|
-
"Map",
|
|
24
|
-
"Set",
|
|
25
|
-
"WeakMap",
|
|
26
|
-
"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
|
-
"Partial",
|
|
51
|
-
"Required",
|
|
52
|
-
"Pick",
|
|
53
|
-
"Omit",
|
|
54
|
-
"Record",
|
|
55
|
-
"Exclude",
|
|
56
|
-
"Extract",
|
|
57
|
-
"NonNullable",
|
|
58
|
-
"Parameters",
|
|
59
|
-
"ReturnType",
|
|
60
|
-
"ConstructorParameters",
|
|
61
|
-
"InstanceType",
|
|
62
|
-
"ThisType",
|
|
63
|
-
"Awaited",
|
|
64
|
-
"PromiseLike",
|
|
65
|
-
"Iterable",
|
|
66
|
-
"Iterator",
|
|
67
|
-
"IterableIterator",
|
|
68
|
-
"Generator",
|
|
69
|
-
"AsyncGenerator",
|
|
70
|
-
"AsyncIterable",
|
|
71
|
-
"AsyncIterator",
|
|
72
|
-
"AsyncIterableIterator",
|
|
73
|
-
"SharedArrayBuffer",
|
|
74
|
-
"Atomics",
|
|
75
|
-
"JSON",
|
|
76
|
-
"Math",
|
|
77
|
-
"console",
|
|
78
|
-
"globalThis"
|
|
79
|
-
]);
|
|
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))
|
|
86
|
-
return true;
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
class TypeRegistry {
|
|
91
|
-
types = new Map;
|
|
92
|
-
processing = new Set;
|
|
93
|
-
add(type) {
|
|
94
|
-
this.types.set(type.id, type);
|
|
95
|
-
}
|
|
96
|
-
get(id) {
|
|
97
|
-
return this.types.get(id);
|
|
98
|
-
}
|
|
99
|
-
has(id) {
|
|
100
|
-
return this.types.has(id);
|
|
101
|
-
}
|
|
102
|
-
getAll() {
|
|
103
|
-
return Array.from(this.types.values());
|
|
104
|
-
}
|
|
105
|
-
registerType(type, checker, exportedIds) {
|
|
106
|
-
const symbol = type.getSymbol() || type.aliasSymbol;
|
|
107
|
-
if (!symbol)
|
|
108
|
-
return;
|
|
109
|
-
const name = symbol.getName();
|
|
110
|
-
if (PRIMITIVES.has(name))
|
|
111
|
-
return;
|
|
112
|
-
if (BUILTINS.has(name))
|
|
113
|
-
return;
|
|
114
|
-
if (name.startsWith("__"))
|
|
115
|
-
return;
|
|
116
|
-
if (symbol.flags & ts.SymbolFlags.EnumMember)
|
|
117
|
-
return;
|
|
118
|
-
if (symbol.flags & ts.SymbolFlags.TypeParameter)
|
|
119
|
-
return;
|
|
120
|
-
if (isGenericTypeParameter(name))
|
|
121
|
-
return;
|
|
122
|
-
if (this.has(name))
|
|
123
|
-
return name;
|
|
124
|
-
if (exportedIds.has(name))
|
|
125
|
-
return name;
|
|
126
|
-
if (this.processing.has(name))
|
|
127
|
-
return name;
|
|
128
|
-
this.processing.add(name);
|
|
129
|
-
try {
|
|
130
|
-
const specType = this.buildSpecType(symbol, type, checker);
|
|
131
|
-
if (specType) {
|
|
132
|
-
this.add(specType);
|
|
133
|
-
return specType.id;
|
|
134
|
-
}
|
|
135
|
-
} finally {
|
|
136
|
-
this.processing.delete(name);
|
|
137
|
-
}
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
buildSpecType(symbol, type, checker) {
|
|
141
|
-
const name = symbol.getName();
|
|
142
|
-
const decl = symbol.declarations?.[0];
|
|
143
|
-
let kind = "type";
|
|
144
|
-
if (decl) {
|
|
145
|
-
if (ts.isClassDeclaration(decl))
|
|
146
|
-
kind = "class";
|
|
147
|
-
else if (ts.isInterfaceDeclaration(decl))
|
|
148
|
-
kind = "interface";
|
|
149
|
-
else if (ts.isEnumDeclaration(decl))
|
|
150
|
-
kind = "enum";
|
|
151
|
-
}
|
|
152
|
-
const typeString = checker.typeToString(type);
|
|
153
|
-
return {
|
|
154
|
-
id: name,
|
|
155
|
-
name,
|
|
156
|
-
kind,
|
|
157
|
-
type: typeString !== name ? typeString : undefined
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
registerFromSymbol(symbol, checker) {
|
|
161
|
-
const name = symbol.getName();
|
|
162
|
-
if (this.has(name))
|
|
163
|
-
return this.get(name);
|
|
164
|
-
const type = checker.getDeclaredTypeOfSymbol(symbol);
|
|
165
|
-
const specType = this.buildSpecType(symbol, type, checker);
|
|
166
|
-
if (specType) {
|
|
167
|
-
this.add(specType);
|
|
168
|
-
return specType;
|
|
169
|
-
}
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// src/ast/utils.ts
|
|
175
|
-
import ts2 from "typescript";
|
|
176
|
-
function parseExamplesFromTags(tags) {
|
|
177
|
-
const examples = [];
|
|
178
|
-
for (const tag of tags) {
|
|
179
|
-
if (tag.name !== "example")
|
|
180
|
-
continue;
|
|
181
|
-
const text = tag.text.trim();
|
|
182
|
-
const fenceMatch = text.match(/^```(\w*)\n([\s\S]*?)\n?```$/);
|
|
183
|
-
if (fenceMatch) {
|
|
184
|
-
const lang = fenceMatch[1] || undefined;
|
|
185
|
-
const code = fenceMatch[2].trim();
|
|
186
|
-
const example = { code };
|
|
187
|
-
if (lang && ["ts", "js", "tsx", "jsx", "shell", "json"].includes(lang)) {
|
|
188
|
-
example.language = lang;
|
|
189
|
-
}
|
|
190
|
-
examples.push(example);
|
|
191
|
-
} else if (text) {
|
|
192
|
-
examples.push({ code: text });
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return examples;
|
|
196
|
-
}
|
|
197
|
-
function getJSDocComment(node) {
|
|
198
|
-
const jsDocTags = ts2.getJSDocTags(node);
|
|
199
|
-
const tags = jsDocTags.map((tag) => ({
|
|
200
|
-
name: tag.tagName.text,
|
|
201
|
-
text: typeof tag.comment === "string" ? tag.comment : ts2.getTextOfJSDocComment(tag.comment) ?? ""
|
|
202
|
-
}));
|
|
203
|
-
const jsDocComments = node.jsDoc;
|
|
204
|
-
let description;
|
|
205
|
-
if (jsDocComments && jsDocComments.length > 0) {
|
|
206
|
-
const firstDoc = jsDocComments[0];
|
|
207
|
-
if (firstDoc.comment) {
|
|
208
|
-
description = typeof firstDoc.comment === "string" ? firstDoc.comment : ts2.getTextOfJSDocComment(firstDoc.comment);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
const examples = parseExamplesFromTags(tags);
|
|
212
|
-
return { description, tags, examples };
|
|
213
|
-
}
|
|
214
|
-
function getSourceLocation(node, sourceFile) {
|
|
215
|
-
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
216
|
-
return {
|
|
217
|
-
file: sourceFile.fileName,
|
|
218
|
-
line: line + 1
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// src/compiler/program.ts
|
|
223
|
-
import * as path from "node:path";
|
|
224
|
-
import ts3 from "typescript";
|
|
225
|
-
var DEFAULT_COMPILER_OPTIONS = {
|
|
226
|
-
target: ts3.ScriptTarget.Latest,
|
|
227
|
-
module: ts3.ModuleKind.CommonJS,
|
|
228
|
-
lib: ["lib.es2021.d.ts"],
|
|
229
|
-
declaration: true,
|
|
230
|
-
moduleResolution: ts3.ModuleResolutionKind.NodeJs
|
|
231
|
-
};
|
|
232
|
-
function createProgram({
|
|
233
|
-
entryFile,
|
|
234
|
-
baseDir = path.dirname(entryFile),
|
|
235
|
-
content
|
|
236
|
-
}) {
|
|
237
|
-
const configPath = ts3.findConfigFile(baseDir, ts3.sys.fileExists, "tsconfig.json");
|
|
238
|
-
let compilerOptions = { ...DEFAULT_COMPILER_OPTIONS };
|
|
239
|
-
if (configPath) {
|
|
240
|
-
const configFile = ts3.readConfigFile(configPath, ts3.sys.readFile);
|
|
241
|
-
const parsedConfig = ts3.parseJsonConfigFileContent(configFile.config, ts3.sys, path.dirname(configPath));
|
|
242
|
-
compilerOptions = { ...compilerOptions, ...parsedConfig.options };
|
|
243
|
-
}
|
|
244
|
-
const allowJsVal = compilerOptions.allowJs;
|
|
245
|
-
if (typeof allowJsVal === "boolean" && allowJsVal) {
|
|
246
|
-
compilerOptions = { ...compilerOptions, allowJs: false, checkJs: false };
|
|
247
|
-
}
|
|
248
|
-
const compilerHost = ts3.createCompilerHost(compilerOptions, true);
|
|
249
|
-
let inMemorySource;
|
|
250
|
-
if (content !== undefined) {
|
|
251
|
-
inMemorySource = ts3.createSourceFile(entryFile, content, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TS);
|
|
252
|
-
const originalGetSourceFile = compilerHost.getSourceFile.bind(compilerHost);
|
|
253
|
-
compilerHost.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
254
|
-
if (fileName === entryFile) {
|
|
255
|
-
return inMemorySource;
|
|
256
|
-
}
|
|
257
|
-
return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
const program = ts3.createProgram([entryFile], compilerOptions, compilerHost);
|
|
261
|
-
const sourceFile = inMemorySource ?? program.getSourceFile(entryFile);
|
|
262
|
-
return {
|
|
263
|
-
program,
|
|
264
|
-
compilerHost,
|
|
265
|
-
compilerOptions,
|
|
266
|
-
sourceFile,
|
|
267
|
-
configPath
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// src/serializers/classes.ts
|
|
272
|
-
function serializeClass(node, ctx) {
|
|
273
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
274
|
-
const name = symbol?.getName() ?? node.name?.getText();
|
|
275
|
-
if (!name)
|
|
276
|
-
return null;
|
|
277
|
-
const declSourceFile = node.getSourceFile();
|
|
278
|
-
const { description, tags, examples } = getJSDocComment(node);
|
|
279
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
280
|
-
return {
|
|
281
|
-
id: name,
|
|
282
|
-
name,
|
|
283
|
-
kind: "class",
|
|
284
|
-
description,
|
|
285
|
-
tags,
|
|
286
|
-
source,
|
|
287
|
-
members: [],
|
|
288
|
-
...examples.length > 0 ? { examples } : {}
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// src/serializers/enums.ts
|
|
293
|
-
function serializeEnum(node, ctx) {
|
|
294
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
295
|
-
const name = symbol?.getName() ?? node.name?.getText();
|
|
296
|
-
if (!name)
|
|
297
|
-
return null;
|
|
298
|
-
const declSourceFile = node.getSourceFile();
|
|
299
|
-
const { description, tags, examples } = getJSDocComment(node);
|
|
300
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
301
|
-
const members = node.members.map((member) => {
|
|
302
|
-
const memberSymbol = ctx.typeChecker.getSymbolAtLocation(member.name);
|
|
303
|
-
const memberName = memberSymbol?.getName() ?? member.name.getText();
|
|
304
|
-
return {
|
|
305
|
-
id: memberName,
|
|
306
|
-
name: memberName,
|
|
307
|
-
kind: "enum-member"
|
|
308
|
-
};
|
|
309
|
-
});
|
|
310
|
-
return {
|
|
311
|
-
id: name,
|
|
312
|
-
name,
|
|
313
|
-
kind: "enum",
|
|
314
|
-
description,
|
|
315
|
-
tags,
|
|
316
|
-
source,
|
|
317
|
-
members,
|
|
318
|
-
...examples.length > 0 ? { examples } : {}
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// src/types/parameters.ts
|
|
323
|
-
function extractParameters(signature, ctx) {
|
|
324
|
-
const { typeChecker: checker, typeRegistry, exportedIds } = ctx;
|
|
325
|
-
return signature.getParameters().map((param) => {
|
|
326
|
-
const type = checker.getTypeOfSymbolAtLocation(param, param.valueDeclaration);
|
|
327
|
-
registerReferencedTypes(type, ctx);
|
|
328
|
-
return {
|
|
329
|
-
name: param.getName(),
|
|
330
|
-
schema: { type: checker.typeToString(type) },
|
|
331
|
-
required: !(param.flags & 16777216)
|
|
332
|
-
};
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
function registerReferencedTypes(type, ctx) {
|
|
336
|
-
if (ctx.visitedTypes.has(type))
|
|
337
|
-
return;
|
|
338
|
-
ctx.visitedTypes.add(type);
|
|
339
|
-
const { typeChecker: checker, typeRegistry, exportedIds } = ctx;
|
|
340
|
-
typeRegistry.registerType(type, checker, exportedIds);
|
|
341
|
-
const typeArgs = type.typeArguments;
|
|
342
|
-
if (typeArgs) {
|
|
343
|
-
for (const arg of typeArgs) {
|
|
344
|
-
registerReferencedTypes(arg, ctx);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (type.isUnion()) {
|
|
348
|
-
for (const t of type.types) {
|
|
349
|
-
registerReferencedTypes(t, ctx);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (type.isIntersection()) {
|
|
353
|
-
for (const t of type.types) {
|
|
354
|
-
registerReferencedTypes(t, ctx);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// src/serializers/functions.ts
|
|
360
|
-
function serializeFunctionExport(node, ctx) {
|
|
361
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
362
|
-
const name = symbol?.getName() ?? node.name?.getText();
|
|
363
|
-
if (!name)
|
|
364
|
-
return null;
|
|
365
|
-
const declSourceFile = node.getSourceFile();
|
|
366
|
-
const { description, tags, examples } = getJSDocComment(node);
|
|
367
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
368
|
-
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
369
|
-
const callSignatures = type.getCallSignatures();
|
|
370
|
-
const signatures = callSignatures.map((sig) => {
|
|
371
|
-
const params = extractParameters(sig, ctx);
|
|
372
|
-
const returnType = ctx.typeChecker.getReturnTypeOfSignature(sig);
|
|
373
|
-
registerReferencedTypes(returnType, ctx);
|
|
374
|
-
return {
|
|
375
|
-
parameters: params,
|
|
376
|
-
returns: {
|
|
377
|
-
schema: { type: ctx.typeChecker.typeToString(returnType) }
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
});
|
|
381
|
-
return {
|
|
382
|
-
id: name,
|
|
383
|
-
name,
|
|
384
|
-
kind: "function",
|
|
385
|
-
description,
|
|
386
|
-
tags,
|
|
387
|
-
source,
|
|
388
|
-
signatures,
|
|
389
|
-
...examples.length > 0 ? { examples } : {}
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// src/serializers/interfaces.ts
|
|
394
|
-
function serializeInterface(node, ctx) {
|
|
395
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
396
|
-
const name = symbol?.getName() ?? node.name?.getText();
|
|
397
|
-
if (!name)
|
|
398
|
-
return null;
|
|
399
|
-
const declSourceFile = node.getSourceFile();
|
|
400
|
-
const { description, tags, examples } = getJSDocComment(node);
|
|
401
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
402
|
-
return {
|
|
403
|
-
id: name,
|
|
404
|
-
name,
|
|
405
|
-
kind: "interface",
|
|
406
|
-
description,
|
|
407
|
-
tags,
|
|
408
|
-
source,
|
|
409
|
-
members: [],
|
|
410
|
-
...examples.length > 0 ? { examples } : {}
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// src/serializers/type-aliases.ts
|
|
415
|
-
function serializeTypeAlias(node, ctx) {
|
|
416
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
417
|
-
const name = symbol?.getName() ?? node.name?.getText();
|
|
418
|
-
if (!name)
|
|
419
|
-
return null;
|
|
420
|
-
const declSourceFile = node.getSourceFile();
|
|
421
|
-
const { description, tags, examples } = getJSDocComment(node);
|
|
422
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
423
|
-
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
424
|
-
const typeString = ctx.typeChecker.typeToString(type);
|
|
425
|
-
return {
|
|
426
|
-
id: name,
|
|
427
|
-
name,
|
|
428
|
-
kind: "type",
|
|
429
|
-
description,
|
|
430
|
-
tags,
|
|
431
|
-
source,
|
|
432
|
-
...typeString !== name ? { type: typeString } : {},
|
|
433
|
-
...examples.length > 0 ? { examples } : {}
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// src/serializers/variables.ts
|
|
438
|
-
function serializeVariable(node, statement, ctx) {
|
|
439
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name);
|
|
440
|
-
const name = symbol?.getName() ?? node.name.getText();
|
|
441
|
-
if (!name)
|
|
442
|
-
return null;
|
|
443
|
-
const declSourceFile = node.getSourceFile();
|
|
444
|
-
const { description, tags, examples } = getJSDocComment(statement);
|
|
445
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
446
|
-
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
447
|
-
const typeString = ctx.typeChecker.typeToString(type);
|
|
448
|
-
registerReferencedTypes(type, ctx);
|
|
449
|
-
return {
|
|
450
|
-
id: name,
|
|
451
|
-
name,
|
|
452
|
-
kind: "variable",
|
|
453
|
-
description,
|
|
454
|
-
tags,
|
|
455
|
-
source,
|
|
456
|
-
...typeString && typeString !== name ? { type: typeString } : {},
|
|
457
|
-
...examples.length > 0 ? { examples } : {}
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// src/builder/spec-builder.ts
|
|
462
|
-
import * as fs from "node:fs";
|
|
463
|
-
import * as path2 from "node:path";
|
|
464
|
-
import { SCHEMA_VERSION } from "@openpkg-ts/spec";
|
|
465
|
-
import ts4 from "typescript";
|
|
466
|
-
|
|
467
|
-
// src/serializers/context.ts
|
|
468
|
-
function createContext(program, sourceFile, options = {}) {
|
|
469
|
-
return {
|
|
470
|
-
typeChecker: program.getTypeChecker(),
|
|
471
|
-
program,
|
|
472
|
-
sourceFile,
|
|
473
|
-
maxTypeDepth: options.maxTypeDepth ?? 20,
|
|
474
|
-
resolveExternalTypes: options.resolveExternalTypes ?? true,
|
|
475
|
-
typeRegistry: new TypeRegistry,
|
|
476
|
-
exportedIds: new Set,
|
|
477
|
-
visitedTypes: new Set
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// src/builder/spec-builder.ts
|
|
482
|
-
async function extract(options) {
|
|
483
|
-
const { entryFile, baseDir, content, maxTypeDepth, resolveExternalTypes } = options;
|
|
484
|
-
const diagnostics = [];
|
|
485
|
-
const exports = [];
|
|
486
|
-
const result = createProgram({ entryFile, baseDir, content });
|
|
487
|
-
const { program, sourceFile } = result;
|
|
488
|
-
if (!sourceFile) {
|
|
489
|
-
return {
|
|
490
|
-
spec: createEmptySpec(entryFile),
|
|
491
|
-
diagnostics: [{ message: `Could not load source file: ${entryFile}`, severity: "error" }]
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
const typeChecker = program.getTypeChecker();
|
|
495
|
-
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
496
|
-
if (!moduleSymbol) {
|
|
497
|
-
return {
|
|
498
|
-
spec: createEmptySpec(entryFile),
|
|
499
|
-
diagnostics: [{ message: "Could not get module symbol", severity: "warning" }]
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
const exportedSymbols = typeChecker.getExportsOfModule(moduleSymbol);
|
|
503
|
-
const exportedIds = new Set;
|
|
504
|
-
for (const symbol of exportedSymbols) {
|
|
505
|
-
exportedIds.add(symbol.getName());
|
|
506
|
-
}
|
|
507
|
-
const ctx = createContext(program, sourceFile, { maxTypeDepth, resolveExternalTypes });
|
|
508
|
-
ctx.exportedIds = exportedIds;
|
|
509
|
-
for (const symbol of exportedSymbols) {
|
|
510
|
-
try {
|
|
511
|
-
const { declaration, targetSymbol } = resolveExportTarget(symbol, typeChecker);
|
|
512
|
-
if (!declaration)
|
|
513
|
-
continue;
|
|
514
|
-
const exportName = symbol.getName();
|
|
515
|
-
const exp = serializeDeclaration(declaration, symbol, targetSymbol, exportName, ctx);
|
|
516
|
-
if (exp)
|
|
517
|
-
exports.push(exp);
|
|
518
|
-
} catch (err) {
|
|
519
|
-
diagnostics.push({
|
|
520
|
-
message: `Failed to serialize ${symbol.getName()}: ${err}`,
|
|
521
|
-
severity: "warning"
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
const meta = await getPackageMeta(entryFile, baseDir);
|
|
526
|
-
const spec = {
|
|
527
|
-
openpkg: SCHEMA_VERSION,
|
|
528
|
-
meta,
|
|
529
|
-
exports,
|
|
530
|
-
types: ctx.typeRegistry.getAll(),
|
|
531
|
-
generation: {
|
|
532
|
-
generator: "@openpkg-ts/extract",
|
|
533
|
-
timestamp: new Date().toISOString()
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
return { spec, diagnostics };
|
|
537
|
-
}
|
|
538
|
-
function resolveExportTarget(symbol, checker) {
|
|
539
|
-
let targetSymbol = symbol;
|
|
540
|
-
if (symbol.flags & ts4.SymbolFlags.Alias) {
|
|
541
|
-
const aliasTarget = checker.getAliasedSymbol(symbol);
|
|
542
|
-
if (aliasTarget && aliasTarget !== symbol) {
|
|
543
|
-
targetSymbol = aliasTarget;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
const declarations = targetSymbol.declarations ?? [];
|
|
547
|
-
const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !== ts4.SyntaxKind.ExportSpecifier) || declarations[0];
|
|
548
|
-
return { declaration, targetSymbol };
|
|
549
|
-
}
|
|
550
|
-
function serializeDeclaration(declaration, exportSymbol, targetSymbol, exportName, ctx) {
|
|
551
|
-
let result = null;
|
|
552
|
-
if (ts4.isFunctionDeclaration(declaration)) {
|
|
553
|
-
result = serializeFunctionExport(declaration, ctx);
|
|
554
|
-
} else if (ts4.isClassDeclaration(declaration)) {
|
|
555
|
-
result = serializeClass(declaration, ctx);
|
|
556
|
-
} else if (ts4.isInterfaceDeclaration(declaration)) {
|
|
557
|
-
result = serializeInterface(declaration, ctx);
|
|
558
|
-
} else if (ts4.isTypeAliasDeclaration(declaration)) {
|
|
559
|
-
result = serializeTypeAlias(declaration, ctx);
|
|
560
|
-
} else if (ts4.isEnumDeclaration(declaration)) {
|
|
561
|
-
result = serializeEnum(declaration, ctx);
|
|
562
|
-
} else if (ts4.isVariableDeclaration(declaration)) {
|
|
563
|
-
const varStatement = declaration.parent?.parent;
|
|
564
|
-
if (varStatement && ts4.isVariableStatement(varStatement)) {
|
|
565
|
-
result = serializeVariable(declaration, varStatement, ctx);
|
|
566
|
-
}
|
|
567
|
-
} else if (ts4.isNamespaceExport(declaration) || ts4.isModuleDeclaration(declaration)) {
|
|
568
|
-
result = serializeNamespaceExport(exportSymbol, exportName, ctx);
|
|
569
|
-
} else if (ts4.isSourceFile(declaration)) {
|
|
570
|
-
result = serializeNamespaceExport(exportSymbol, exportName, ctx);
|
|
571
|
-
}
|
|
572
|
-
if (result) {
|
|
573
|
-
result = withExportName(result, exportName);
|
|
574
|
-
}
|
|
575
|
-
return result;
|
|
576
|
-
}
|
|
577
|
-
function serializeNamespaceExport(symbol, exportName, ctx) {
|
|
578
|
-
const { description, tags, examples } = getJSDocFromExportSymbol(symbol);
|
|
579
|
-
return {
|
|
580
|
-
id: exportName,
|
|
581
|
-
name: exportName,
|
|
582
|
-
kind: "namespace",
|
|
583
|
-
description,
|
|
584
|
-
tags,
|
|
585
|
-
...examples.length > 0 ? { examples } : {}
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
function getJSDocFromExportSymbol(symbol) {
|
|
589
|
-
const tags = [];
|
|
590
|
-
const examples = [];
|
|
591
|
-
const decl = symbol.declarations?.[0];
|
|
592
|
-
if (decl) {
|
|
593
|
-
const exportDecl = ts4.isNamespaceExport(decl) ? decl.parent : decl;
|
|
594
|
-
if (exportDecl && ts4.isExportDeclaration(exportDecl)) {
|
|
595
|
-
const jsDocs = ts4.getJSDocCommentsAndTags(exportDecl);
|
|
596
|
-
for (const doc of jsDocs) {
|
|
597
|
-
if (ts4.isJSDoc(doc) && doc.comment) {
|
|
598
|
-
const commentText = typeof doc.comment === "string" ? doc.comment : doc.comment.map((c) => ("text" in c) ? c.text : "").join("");
|
|
599
|
-
if (commentText) {
|
|
600
|
-
return {
|
|
601
|
-
description: commentText,
|
|
602
|
-
tags: extractJSDocTags(doc),
|
|
603
|
-
examples: extractExamples(doc)
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
const docComment = symbol.getDocumentationComment(undefined);
|
|
611
|
-
const description = docComment.map((c) => c.text).join(`
|
|
612
|
-
`) || undefined;
|
|
613
|
-
const jsTags = symbol.getJsDocTags();
|
|
614
|
-
for (const tag of jsTags) {
|
|
615
|
-
const text = tag.text?.map((t) => t.text).join("") ?? "";
|
|
616
|
-
if (tag.name === "example") {
|
|
617
|
-
examples.push(text);
|
|
618
|
-
} else {
|
|
619
|
-
tags.push({ name: tag.name, text });
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
return { description, tags, examples };
|
|
623
|
-
}
|
|
624
|
-
function extractJSDocTags(doc) {
|
|
625
|
-
const tags = [];
|
|
626
|
-
for (const tag of doc.tags ?? []) {
|
|
627
|
-
if (tag.tagName.text !== "example") {
|
|
628
|
-
const text = typeof tag.comment === "string" ? tag.comment : tag.comment?.map((c) => ("text" in c) ? c.text : "").join("") ?? "";
|
|
629
|
-
tags.push({ name: tag.tagName.text, text });
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
return tags;
|
|
633
|
-
}
|
|
634
|
-
function extractExamples(doc) {
|
|
635
|
-
const examples = [];
|
|
636
|
-
for (const tag of doc.tags ?? []) {
|
|
637
|
-
if (tag.tagName.text === "example") {
|
|
638
|
-
const text = typeof tag.comment === "string" ? tag.comment : tag.comment?.map((c) => ("text" in c) ? c.text : "").join("") ?? "";
|
|
639
|
-
if (text)
|
|
640
|
-
examples.push(text);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
return examples;
|
|
644
|
-
}
|
|
645
|
-
function withExportName(entry, exportName) {
|
|
646
|
-
if (entry.name === exportName) {
|
|
647
|
-
return entry;
|
|
648
|
-
}
|
|
649
|
-
return {
|
|
650
|
-
...entry,
|
|
651
|
-
id: exportName,
|
|
652
|
-
name: entry.name
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
function createEmptySpec(entryFile) {
|
|
656
|
-
return {
|
|
657
|
-
openpkg: SCHEMA_VERSION,
|
|
658
|
-
meta: { name: path2.basename(entryFile, path2.extname(entryFile)) },
|
|
659
|
-
exports: [],
|
|
660
|
-
generation: {
|
|
661
|
-
generator: "@openpkg-ts/extract",
|
|
662
|
-
timestamp: new Date().toISOString()
|
|
663
|
-
}
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
async function getPackageMeta(entryFile, baseDir) {
|
|
667
|
-
const searchDir = baseDir ?? path2.dirname(entryFile);
|
|
668
|
-
const pkgPath = path2.join(searchDir, "package.json");
|
|
669
|
-
try {
|
|
670
|
-
if (fs.existsSync(pkgPath)) {
|
|
671
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
672
|
-
return {
|
|
673
|
-
name: pkg.name ?? path2.basename(searchDir),
|
|
674
|
-
version: pkg.version,
|
|
675
|
-
description: pkg.description
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
} catch {}
|
|
679
|
-
return { name: path2.basename(searchDir) };
|
|
680
|
-
}
|
|
681
|
-
export { TypeRegistry, getJSDocComment, getSourceLocation, createProgram, serializeClass, serializeEnum, extractParameters, registerReferencedTypes, serializeFunctionExport, serializeInterface, serializeTypeAlias, serializeVariable, extract };
|