@openpkg-ts/extract 0.11.3 → 0.12.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.
@@ -2,10 +2,17 @@ import { SpecType } from "@openpkg-ts/spec";
2
2
  import ts from "typescript";
3
3
  declare class TypeRegistry {
4
4
  private types;
5
+ private processing;
5
6
  add(type: SpecType): void;
6
7
  get(id: string): SpecType | undefined;
7
8
  has(id: string): boolean;
8
9
  getAll(): SpecType[];
10
+ /**
11
+ * Register a type from a ts.Type, extracting its structure.
12
+ * Returns the type ID if registered, undefined if skipped.
13
+ */
14
+ registerType(type: ts.Type, checker: ts.TypeChecker, exportedIds: Set<string>): string | undefined;
15
+ private buildSpecType;
9
16
  registerFromSymbol(symbol: ts.Symbol, checker: ts.TypeChecker): SpecType | undefined;
10
17
  }
11
18
  import { SpecExample, SpecSource, SpecTag } from "@openpkg-ts/spec";
@@ -17,14 +24,12 @@ declare function getJSDocComment(node: ts2.Node): {
17
24
  };
18
25
  declare function getSourceLocation(node: ts2.Node, sourceFile: ts2.SourceFile): SpecSource;
19
26
  import { OpenPkg } from "@openpkg-ts/spec";
20
- import { TypeChecker as TypeChecker_vexmldwuwp } from "typescript";
21
- import { Program as Program_lhgjafqgga } from "typescript";
22
- import { SourceFile as SourceFile_sameteuoys } from "typescript";
23
27
  interface ExtractOptions {
24
28
  entryFile: string;
25
29
  baseDir?: string;
26
30
  content?: string;
27
31
  maxTypeDepth?: number;
32
+ maxExternalTypeDepth?: number;
28
33
  resolveExternalTypes?: boolean;
29
34
  schemaExtraction?: "static" | "hybrid";
30
35
  }
@@ -41,13 +46,6 @@ interface Diagnostic {
41
46
  column?: number;
42
47
  };
43
48
  }
44
- interface SerializerContext {
45
- typeChecker: TypeChecker_vexmldwuwp;
46
- program: Program_lhgjafqgga;
47
- sourceFile: SourceFile_sameteuoys;
48
- maxTypeDepth: number;
49
- resolveExternalTypes: boolean;
50
- }
51
49
  declare function extract(options: ExtractOptions): Promise<ExtractResult>;
52
50
  import ts3 from "typescript";
53
51
  interface ProgramOptions {
@@ -88,40 +86,66 @@ declare function extractStandardSchemas(_program: ts5.Program, _entryFile: strin
88
86
  import { SpecExport } from "@openpkg-ts/spec";
89
87
  import ts7 from "typescript";
90
88
  import ts6 from "typescript";
91
- interface SerializerContext2 {
89
+ interface SerializerContext {
92
90
  typeChecker: ts6.TypeChecker;
93
91
  program: ts6.Program;
94
92
  sourceFile: ts6.SourceFile;
95
93
  maxTypeDepth: number;
94
+ maxExternalTypeDepth: number;
95
+ currentDepth: number;
96
96
  resolveExternalTypes: boolean;
97
97
  typeRegistry: TypeRegistry;
98
+ exportedIds: Set<string>;
99
+ /** Track visited types to prevent infinite recursion */
100
+ visitedTypes: Set<ts6.Type>;
98
101
  }
99
- declare function serializeClass(node: ts7.ClassDeclaration, ctx: SerializerContext2): SpecExport | null;
102
+ declare function serializeClass(node: ts7.ClassDeclaration, ctx: SerializerContext): SpecExport | null;
100
103
  import { SpecExport as SpecExport2 } from "@openpkg-ts/spec";
101
104
  import ts8 from "typescript";
102
- declare function serializeEnum(node: ts8.EnumDeclaration, ctx: SerializerContext2): SpecExport2 | null;
105
+ declare function serializeEnum(node: ts8.EnumDeclaration, ctx: SerializerContext): SpecExport2 | null;
103
106
  import { SpecExport as SpecExport3 } from "@openpkg-ts/spec";
104
107
  import ts9 from "typescript";
105
- declare function serializeFunctionExport(node: ts9.FunctionDeclaration | ts9.ArrowFunction, ctx: SerializerContext2): SpecExport3 | null;
108
+ declare function serializeFunctionExport(node: ts9.FunctionDeclaration | ts9.ArrowFunction, ctx: SerializerContext): SpecExport3 | null;
106
109
  import { SpecExport as SpecExport4 } from "@openpkg-ts/spec";
107
110
  import ts10 from "typescript";
108
- declare function serializeInterface(node: ts10.InterfaceDeclaration, ctx: SerializerContext2): SpecExport4 | null;
111
+ declare function serializeInterface(node: ts10.InterfaceDeclaration, ctx: SerializerContext): SpecExport4 | null;
109
112
  import { SpecExport as SpecExport5 } from "@openpkg-ts/spec";
110
113
  import ts11 from "typescript";
111
- declare function serializeTypeAlias(node: ts11.TypeAliasDeclaration, ctx: SerializerContext2): SpecExport5 | null;
114
+ declare function serializeTypeAlias(node: ts11.TypeAliasDeclaration, ctx: SerializerContext): SpecExport5 | null;
112
115
  import { SpecExport as SpecExport6 } from "@openpkg-ts/spec";
113
116
  import ts12 from "typescript";
114
- declare function serializeVariable(node: ts12.VariableDeclaration, statement: ts12.VariableStatement, ctx: SerializerContext2): SpecExport6 | null;
117
+ declare function serializeVariable(node: ts12.VariableDeclaration, statement: ts12.VariableStatement, ctx: SerializerContext): SpecExport6 | null;
115
118
  import ts13 from "typescript";
116
119
  declare function formatTypeReference(type: ts13.Type, checker: ts13.TypeChecker): string;
117
120
  declare function collectReferencedTypes(type: ts13.Type, checker: ts13.TypeChecker, visited?: Set<string>): string[];
118
121
  import { SpecSignatureParameter } from "@openpkg-ts/spec";
119
122
  import ts14 from "typescript";
120
- declare function extractParameters(signature: ts14.Signature, checker: ts14.TypeChecker): SpecSignatureParameter[];
123
+ declare function extractParameters(signature: ts14.Signature, ctx: SerializerContext): SpecSignatureParameter[];
124
+ /**
125
+ * Recursively register types referenced by a ts.Type.
126
+ * Uses ctx.visitedTypes to prevent infinite recursion on circular types.
127
+ */
128
+ declare function registerReferencedTypes(type: ts14.Type, ctx: SerializerContext): void;
121
129
  import { SpecSchema as SpecSchema3 } from "@openpkg-ts/spec";
122
130
  import ts15 from "typescript";
123
- declare function buildSchema(type: ts15.Type, checker: ts15.TypeChecker, depth?: number): SpecSchema3;
131
+ /**
132
+ * Check if a name is a primitive type
133
+ */
134
+ declare function isPrimitiveName(name: string): boolean;
135
+ /**
136
+ * Check if a name is a built-in generic type
137
+ */
138
+ declare function isBuiltinGeneric(name: string): boolean;
139
+ /**
140
+ * Check if a type is anonymous (no meaningful symbol name)
141
+ */
142
+ declare function isAnonymous(type: ts15.Type): boolean;
143
+ /**
144
+ * Build a structured SpecSchema from a TypeScript type.
145
+ * Uses $ref for named types and typeArguments for generics.
146
+ */
147
+ declare function buildSchema(type: ts15.Type, checker: ts15.TypeChecker, ctx?: SerializerContext, _depth?: number): SpecSchema3;
124
148
  import ts16 from "typescript";
125
149
  declare function isExported(node: ts16.Node): boolean;
126
150
  declare function getNodeName(node: ts16.Node): string | undefined;
127
- export { zodAdapter, valibotAdapter, typeboxAdapter, serializeVariable, serializeTypeAlias, serializeInterface, serializeFunctionExport, serializeEnum, serializeClass, registerAdapter, isSchemaType, isExported, getSourceLocation, getNodeName, getJSDocComment, formatTypeReference, findAdapter, extractStandardSchemas, extractSchemaType, extractParameters, extract, createProgram, collectReferencedTypes, buildSchema, arktypeAdapter, TypeRegistry, StandardSchemaResult, SerializerContext, SchemaAdapter, ProgramResult, ProgramOptions, ExtractResult, ExtractOptions, Diagnostic };
151
+ export { zodAdapter, valibotAdapter, typeboxAdapter, serializeVariable, serializeTypeAlias, serializeInterface, serializeFunctionExport, serializeEnum, serializeClass, registerReferencedTypes, registerAdapter, isSchemaType, isPrimitiveName, isExported, isBuiltinGeneric, isAnonymous, getSourceLocation, getNodeName, getJSDocComment, formatTypeReference, findAdapter, extractStandardSchemas, extractSchemaType, extractParameters, extract, createProgram, collectReferencedTypes, buildSchema, arktypeAdapter, TypeRegistry, StandardSchemaResult, SerializerContext, SchemaAdapter, ProgramResult, ProgramOptions, ExtractResult, ExtractOptions, Diagnostic };
package/dist/src/index.js CHANGED
@@ -1,17 +1,22 @@
1
1
  import {
2
2
  TypeRegistry,
3
+ buildSchema,
3
4
  createProgram,
4
5
  extract,
5
6
  extractParameters,
6
7
  getJSDocComment,
7
8
  getSourceLocation,
9
+ isAnonymous,
10
+ isBuiltinGeneric,
11
+ isPrimitiveName,
12
+ registerReferencedTypes,
8
13
  serializeClass,
9
14
  serializeEnum,
10
15
  serializeFunctionExport,
11
16
  serializeInterface,
12
17
  serializeTypeAlias,
13
18
  serializeVariable
14
- } from "../shared/chunk-9x67bae3.js";
19
+ } from "../shared/chunk-wddga8ye.js";
15
20
  // src/schema/adapters/arktype.ts
16
21
  var arktypeAdapter = {
17
22
  name: "arktype",
@@ -69,32 +74,6 @@ function collectReferencedTypes(type, checker, visited = new Set) {
69
74
  visited.add(name);
70
75
  return [name];
71
76
  }
72
- // src/types/schema-builder.ts
73
- function buildSchema(type, checker, depth = 0) {
74
- if (depth > 10) {
75
- return { type: checker.typeToString(type) };
76
- }
77
- const typeString = checker.typeToString(type);
78
- if (type.flags & 4)
79
- return { type: "string" };
80
- if (type.flags & 8)
81
- return { type: "number" };
82
- if (type.flags & 16)
83
- return { type: "boolean" };
84
- if (type.flags & 32768)
85
- return { type: "undefined" };
86
- if (type.flags & 65536)
87
- return { type: "null" };
88
- if (type.flags & 16384)
89
- return { type: "void" };
90
- if (type.flags & 1)
91
- return { type: "any" };
92
- if (type.flags & 2)
93
- return { type: "unknown" };
94
- if (type.flags & 131072)
95
- return { type: "never" };
96
- return { type: typeString };
97
- }
98
77
  // src/types/utils.ts
99
78
  function isExported(node) {
100
79
  const modifiers = node.modifiers;
@@ -119,9 +98,13 @@ export {
119
98
  serializeFunctionExport,
120
99
  serializeEnum,
121
100
  serializeClass,
101
+ registerReferencedTypes,
122
102
  registerAdapter,
123
103
  isSchemaType,
104
+ isPrimitiveName,
124
105
  isExported,
106
+ isBuiltinGeneric,
107
+ isAnonymous,
125
108
  getSourceLocation,
126
109
  getNodeName,
127
110
  getJSDocComment,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpkg-ts/extract",
3
- "version": "0.11.3",
3
+ "version": "0.12.0",
4
4
  "description": "TypeScript export extraction to OpenPkg spec",
5
5
  "keywords": [
6
6
  "openpkg",
@@ -40,7 +40,7 @@
40
40
  "format": "biome format --write src/"
41
41
  },
42
42
  "dependencies": {
43
- "@openpkg-ts/spec": "^0.11.1",
43
+ "@openpkg-ts/spec": "^0.12.0",
44
44
  "commander": "^12.0.0",
45
45
  "typescript": "^5.0.0"
46
46
  },
@@ -1,431 +0,0 @@
1
- // src/ast/registry.ts
2
- class TypeRegistry {
3
- types = new Map;
4
- add(type) {
5
- this.types.set(type.id, type);
6
- }
7
- get(id) {
8
- return this.types.get(id);
9
- }
10
- has(id) {
11
- return this.types.has(id);
12
- }
13
- getAll() {
14
- return Array.from(this.types.values());
15
- }
16
- registerFromSymbol(symbol, checker) {
17
- const name = symbol.getName();
18
- if (this.has(name))
19
- return this.get(name);
20
- const type = {
21
- id: name,
22
- name,
23
- kind: "type"
24
- };
25
- this.add(type);
26
- return type;
27
- }
28
- }
29
-
30
- // src/ast/utils.ts
31
- import ts from "typescript";
32
- function parseExamplesFromTags(tags) {
33
- const examples = [];
34
- for (const tag of tags) {
35
- if (tag.name !== "example")
36
- continue;
37
- const text = tag.text.trim();
38
- const fenceMatch = text.match(/^```(\w*)\n([\s\S]*?)\n?```$/);
39
- if (fenceMatch) {
40
- const lang = fenceMatch[1] || undefined;
41
- const code = fenceMatch[2].trim();
42
- const example = { code };
43
- if (lang && ["ts", "js", "tsx", "jsx", "shell", "json"].includes(lang)) {
44
- example.language = lang;
45
- }
46
- examples.push(example);
47
- } else if (text) {
48
- examples.push({ code: text });
49
- }
50
- }
51
- return examples;
52
- }
53
- function getJSDocComment(node) {
54
- const jsDocTags = ts.getJSDocTags(node);
55
- const tags = jsDocTags.map((tag) => ({
56
- name: tag.tagName.text,
57
- text: typeof tag.comment === "string" ? tag.comment : ts.getTextOfJSDocComment(tag.comment) ?? ""
58
- }));
59
- const jsDocComments = node.jsDoc;
60
- let description;
61
- if (jsDocComments && jsDocComments.length > 0) {
62
- const firstDoc = jsDocComments[0];
63
- if (firstDoc.comment) {
64
- description = typeof firstDoc.comment === "string" ? firstDoc.comment : ts.getTextOfJSDocComment(firstDoc.comment);
65
- }
66
- }
67
- const examples = parseExamplesFromTags(tags);
68
- return { description, tags, examples };
69
- }
70
- function getSourceLocation(node, sourceFile) {
71
- const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
72
- return {
73
- file: sourceFile.fileName,
74
- line: line + 1
75
- };
76
- }
77
-
78
- // src/compiler/program.ts
79
- import * as path from "node:path";
80
- import ts2 from "typescript";
81
- var DEFAULT_COMPILER_OPTIONS = {
82
- target: ts2.ScriptTarget.Latest,
83
- module: ts2.ModuleKind.CommonJS,
84
- lib: ["lib.es2021.d.ts"],
85
- declaration: true,
86
- moduleResolution: ts2.ModuleResolutionKind.NodeJs
87
- };
88
- function createProgram({
89
- entryFile,
90
- baseDir = path.dirname(entryFile),
91
- content
92
- }) {
93
- const configPath = ts2.findConfigFile(baseDir, ts2.sys.fileExists, "tsconfig.json");
94
- let compilerOptions = { ...DEFAULT_COMPILER_OPTIONS };
95
- if (configPath) {
96
- const configFile = ts2.readConfigFile(configPath, ts2.sys.readFile);
97
- const parsedConfig = ts2.parseJsonConfigFileContent(configFile.config, ts2.sys, path.dirname(configPath));
98
- compilerOptions = { ...compilerOptions, ...parsedConfig.options };
99
- }
100
- const allowJsVal = compilerOptions.allowJs;
101
- if (typeof allowJsVal === "boolean" && allowJsVal) {
102
- compilerOptions = { ...compilerOptions, allowJs: false, checkJs: false };
103
- }
104
- const compilerHost = ts2.createCompilerHost(compilerOptions, true);
105
- let inMemorySource;
106
- if (content !== undefined) {
107
- inMemorySource = ts2.createSourceFile(entryFile, content, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TS);
108
- const originalGetSourceFile = compilerHost.getSourceFile.bind(compilerHost);
109
- compilerHost.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
110
- if (fileName === entryFile) {
111
- return inMemorySource;
112
- }
113
- return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
114
- };
115
- }
116
- const program = ts2.createProgram([entryFile], compilerOptions, compilerHost);
117
- const sourceFile = inMemorySource ?? program.getSourceFile(entryFile);
118
- return {
119
- program,
120
- compilerHost,
121
- compilerOptions,
122
- sourceFile,
123
- configPath
124
- };
125
- }
126
-
127
- // src/serializers/classes.ts
128
- function serializeClass(node, ctx) {
129
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
130
- const name = symbol?.getName() ?? node.name?.getText();
131
- if (!name)
132
- return null;
133
- const declSourceFile = node.getSourceFile();
134
- const { description, tags, examples } = getJSDocComment(node);
135
- const source = getSourceLocation(node, declSourceFile);
136
- return {
137
- id: name,
138
- name,
139
- kind: "class",
140
- description,
141
- tags,
142
- source,
143
- members: [],
144
- ...examples.length > 0 ? { examples } : {}
145
- };
146
- }
147
-
148
- // src/serializers/enums.ts
149
- function serializeEnum(node, ctx) {
150
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
151
- const name = symbol?.getName() ?? node.name?.getText();
152
- if (!name)
153
- return null;
154
- const declSourceFile = node.getSourceFile();
155
- const { description, tags, examples } = getJSDocComment(node);
156
- const source = getSourceLocation(node, declSourceFile);
157
- const members = node.members.map((member) => {
158
- const memberSymbol = ctx.typeChecker.getSymbolAtLocation(member.name);
159
- const memberName = memberSymbol?.getName() ?? member.name.getText();
160
- return {
161
- id: memberName,
162
- name: memberName,
163
- kind: "enum-member"
164
- };
165
- });
166
- return {
167
- id: name,
168
- name,
169
- kind: "enum",
170
- description,
171
- tags,
172
- source,
173
- members,
174
- ...examples.length > 0 ? { examples } : {}
175
- };
176
- }
177
-
178
- // src/types/parameters.ts
179
- function extractParameters(signature, checker) {
180
- return signature.getParameters().map((param) => {
181
- const type = checker.getTypeOfSymbolAtLocation(param, param.valueDeclaration);
182
- return {
183
- name: param.getName(),
184
- schema: { type: checker.typeToString(type) },
185
- required: !(param.flags & 16777216)
186
- };
187
- });
188
- }
189
-
190
- // src/serializers/functions.ts
191
- function serializeFunctionExport(node, ctx) {
192
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
193
- const name = symbol?.getName() ?? node.name?.getText();
194
- if (!name)
195
- return null;
196
- const declSourceFile = node.getSourceFile();
197
- const { description, tags, examples } = getJSDocComment(node);
198
- const source = getSourceLocation(node, declSourceFile);
199
- const type = ctx.typeChecker.getTypeAtLocation(node);
200
- const callSignatures = type.getCallSignatures();
201
- const signatures = callSignatures.map((sig) => {
202
- const params = extractParameters(sig, ctx.typeChecker);
203
- const returnType = ctx.typeChecker.getReturnTypeOfSignature(sig);
204
- return {
205
- parameters: params,
206
- returns: {
207
- schema: { type: ctx.typeChecker.typeToString(returnType) }
208
- }
209
- };
210
- });
211
- return {
212
- id: name,
213
- name,
214
- kind: "function",
215
- description,
216
- tags,
217
- source,
218
- signatures,
219
- ...examples.length > 0 ? { examples } : {}
220
- };
221
- }
222
-
223
- // src/serializers/interfaces.ts
224
- function serializeInterface(node, ctx) {
225
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
226
- const name = symbol?.getName() ?? node.name?.getText();
227
- if (!name)
228
- return null;
229
- const declSourceFile = node.getSourceFile();
230
- const { description, tags, examples } = getJSDocComment(node);
231
- const source = getSourceLocation(node, declSourceFile);
232
- return {
233
- id: name,
234
- name,
235
- kind: "interface",
236
- description,
237
- tags,
238
- source,
239
- members: [],
240
- ...examples.length > 0 ? { examples } : {}
241
- };
242
- }
243
-
244
- // src/serializers/type-aliases.ts
245
- function serializeTypeAlias(node, ctx) {
246
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
247
- const name = symbol?.getName() ?? node.name?.getText();
248
- if (!name)
249
- return null;
250
- const declSourceFile = node.getSourceFile();
251
- const { description, tags, examples } = getJSDocComment(node);
252
- const source = getSourceLocation(node, declSourceFile);
253
- const type = ctx.typeChecker.getTypeAtLocation(node);
254
- const typeString = ctx.typeChecker.typeToString(type);
255
- return {
256
- id: name,
257
- name,
258
- kind: "type",
259
- description,
260
- tags,
261
- source,
262
- ...typeString !== name ? { type: typeString } : {},
263
- ...examples.length > 0 ? { examples } : {}
264
- };
265
- }
266
-
267
- // src/serializers/variables.ts
268
- function serializeVariable(node, statement, ctx) {
269
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name);
270
- const name = symbol?.getName() ?? node.name.getText();
271
- if (!name)
272
- return null;
273
- const declSourceFile = node.getSourceFile();
274
- const { description, tags, examples } = getJSDocComment(statement);
275
- const source = getSourceLocation(node, declSourceFile);
276
- const type = ctx.typeChecker.getTypeAtLocation(node);
277
- const typeString = ctx.typeChecker.typeToString(type);
278
- return {
279
- id: name,
280
- name,
281
- kind: "variable",
282
- description,
283
- tags,
284
- source,
285
- ...typeString && typeString !== name ? { type: typeString } : {},
286
- ...examples.length > 0 ? { examples } : {}
287
- };
288
- }
289
-
290
- // src/builder/spec-builder.ts
291
- import * as fs from "node:fs";
292
- import * as path2 from "node:path";
293
- import { SCHEMA_VERSION } from "@openpkg-ts/spec";
294
- import ts3 from "typescript";
295
-
296
- // src/serializers/context.ts
297
- function createContext(program, sourceFile, options = {}) {
298
- return {
299
- typeChecker: program.getTypeChecker(),
300
- program,
301
- sourceFile,
302
- maxTypeDepth: options.maxTypeDepth ?? 20,
303
- resolveExternalTypes: options.resolveExternalTypes ?? true,
304
- typeRegistry: new TypeRegistry
305
- };
306
- }
307
-
308
- // src/builder/spec-builder.ts
309
- async function extract(options) {
310
- const { entryFile, baseDir, content, maxTypeDepth, resolveExternalTypes } = options;
311
- const diagnostics = [];
312
- const exports = [];
313
- const result = createProgram({ entryFile, baseDir, content });
314
- const { program, sourceFile } = result;
315
- if (!sourceFile) {
316
- return {
317
- spec: createEmptySpec(entryFile),
318
- diagnostics: [{ message: `Could not load source file: ${entryFile}`, severity: "error" }]
319
- };
320
- }
321
- const ctx = createContext(program, sourceFile, { maxTypeDepth, resolveExternalTypes });
322
- const typeChecker = program.getTypeChecker();
323
- const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
324
- if (!moduleSymbol) {
325
- return {
326
- spec: createEmptySpec(entryFile),
327
- diagnostics: [{ message: "Could not get module symbol", severity: "warning" }]
328
- };
329
- }
330
- const exportedSymbols = typeChecker.getExportsOfModule(moduleSymbol);
331
- for (const symbol of exportedSymbols) {
332
- try {
333
- const { declaration, targetSymbol } = resolveExportTarget(symbol, typeChecker);
334
- if (!declaration)
335
- continue;
336
- const exportName = symbol.getName();
337
- const exp = serializeDeclaration(declaration, targetSymbol, exportName, ctx);
338
- if (exp)
339
- exports.push(exp);
340
- } catch (err) {
341
- diagnostics.push({
342
- message: `Failed to serialize ${symbol.getName()}: ${err}`,
343
- severity: "warning"
344
- });
345
- }
346
- }
347
- const meta = await getPackageMeta(entryFile, baseDir);
348
- const spec = {
349
- openpkg: SCHEMA_VERSION,
350
- meta,
351
- exports,
352
- types: ctx.typeRegistry.getAll(),
353
- generation: {
354
- generator: "@openpkg-ts/extract",
355
- timestamp: new Date().toISOString()
356
- }
357
- };
358
- return { spec, diagnostics };
359
- }
360
- function resolveExportTarget(symbol, checker) {
361
- let targetSymbol = symbol;
362
- if (symbol.flags & ts3.SymbolFlags.Alias) {
363
- const aliasTarget = checker.getAliasedSymbol(symbol);
364
- if (aliasTarget && aliasTarget !== symbol) {
365
- targetSymbol = aliasTarget;
366
- }
367
- }
368
- const declarations = targetSymbol.declarations ?? [];
369
- const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !== ts3.SyntaxKind.ExportSpecifier) || declarations[0];
370
- return { declaration, targetSymbol };
371
- }
372
- function serializeDeclaration(declaration, symbol, exportName, ctx) {
373
- let result = null;
374
- if (ts3.isFunctionDeclaration(declaration)) {
375
- result = serializeFunctionExport(declaration, ctx);
376
- } else if (ts3.isClassDeclaration(declaration)) {
377
- result = serializeClass(declaration, ctx);
378
- } else if (ts3.isInterfaceDeclaration(declaration)) {
379
- result = serializeInterface(declaration, ctx);
380
- } else if (ts3.isTypeAliasDeclaration(declaration)) {
381
- result = serializeTypeAlias(declaration, ctx);
382
- } else if (ts3.isEnumDeclaration(declaration)) {
383
- result = serializeEnum(declaration, ctx);
384
- } else if (ts3.isVariableDeclaration(declaration)) {
385
- const varStatement = declaration.parent?.parent;
386
- if (varStatement && ts3.isVariableStatement(varStatement)) {
387
- result = serializeVariable(declaration, varStatement, ctx);
388
- }
389
- }
390
- if (result) {
391
- result = withExportName(result, exportName);
392
- }
393
- return result;
394
- }
395
- function withExportName(entry, exportName) {
396
- if (entry.name === exportName) {
397
- return entry;
398
- }
399
- return {
400
- ...entry,
401
- id: exportName,
402
- name: entry.name
403
- };
404
- }
405
- function createEmptySpec(entryFile) {
406
- return {
407
- openpkg: SCHEMA_VERSION,
408
- meta: { name: path2.basename(entryFile, path2.extname(entryFile)) },
409
- exports: [],
410
- generation: {
411
- generator: "@openpkg-ts/extract",
412
- timestamp: new Date().toISOString()
413
- }
414
- };
415
- }
416
- async function getPackageMeta(entryFile, baseDir) {
417
- const searchDir = baseDir ?? path2.dirname(entryFile);
418
- const pkgPath = path2.join(searchDir, "package.json");
419
- try {
420
- if (fs.existsSync(pkgPath)) {
421
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
422
- return {
423
- name: pkg.name ?? path2.basename(searchDir),
424
- version: pkg.version,
425
- description: pkg.description
426
- };
427
- }
428
- } catch {}
429
- return { name: path2.basename(searchDir) };
430
- }
431
- export { TypeRegistry, getJSDocComment, getSourceLocation, createProgram, serializeClass, serializeEnum, extractParameters, serializeFunctionExport, serializeInterface, serializeTypeAlias, serializeVariable, extract };