svelte-docinfo 0.1.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.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +349 -0
  3. package/dist/analyze-core.d.ts +1386 -0
  4. package/dist/analyze-core.d.ts.map +1 -0
  5. package/dist/analyze-core.js +324 -0
  6. package/dist/analyze.d.ts +137 -0
  7. package/dist/analyze.d.ts.map +1 -0
  8. package/dist/analyze.js +148 -0
  9. package/dist/cli.d.ts +102 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +206 -0
  12. package/dist/concurrency.d.ts +38 -0
  13. package/dist/concurrency.d.ts.map +1 -0
  14. package/dist/concurrency.js +57 -0
  15. package/dist/declaration-build.d.ts +164 -0
  16. package/dist/declaration-build.d.ts.map +1 -0
  17. package/dist/declaration-build.js +15 -0
  18. package/dist/declaration-helpers.d.ts +169 -0
  19. package/dist/declaration-helpers.d.ts.map +1 -0
  20. package/dist/declaration-helpers.js +239 -0
  21. package/dist/dep-resolver.d.ts +166 -0
  22. package/dist/dep-resolver.d.ts.map +1 -0
  23. package/dist/dep-resolver.js +144 -0
  24. package/dist/diagnostics.d.ts +650 -0
  25. package/dist/diagnostics.d.ts.map +1 -0
  26. package/dist/diagnostics.js +377 -0
  27. package/dist/discovery.d.ts +115 -0
  28. package/dist/discovery.d.ts.map +1 -0
  29. package/dist/discovery.js +126 -0
  30. package/dist/exports.d.ts +105 -0
  31. package/dist/exports.d.ts.map +1 -0
  32. package/dist/exports.js +274 -0
  33. package/dist/files.d.ts +77 -0
  34. package/dist/files.d.ts.map +1 -0
  35. package/dist/files.js +95 -0
  36. package/dist/index.d.ts +32 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +30 -0
  39. package/dist/log.d.ts +32 -0
  40. package/dist/log.d.ts.map +1 -0
  41. package/dist/log.js +11 -0
  42. package/dist/logo.d.ts +8 -0
  43. package/dist/logo.d.ts.map +1 -0
  44. package/dist/logo.js +9 -0
  45. package/dist/main.d.ts +11 -0
  46. package/dist/main.d.ts.map +1 -0
  47. package/dist/main.js +18 -0
  48. package/dist/paths.d.ts +35 -0
  49. package/dist/paths.d.ts.map +1 -0
  50. package/dist/paths.js +34 -0
  51. package/dist/postprocess.d.ts +145 -0
  52. package/dist/postprocess.d.ts.map +1 -0
  53. package/dist/postprocess.js +305 -0
  54. package/dist/session.d.ts +248 -0
  55. package/dist/session.d.ts.map +1 -0
  56. package/dist/session.js +496 -0
  57. package/dist/source-config.d.ts +330 -0
  58. package/dist/source-config.d.ts.map +1 -0
  59. package/dist/source-config.js +356 -0
  60. package/dist/source.d.ts +126 -0
  61. package/dist/source.d.ts.map +1 -0
  62. package/dist/source.js +68 -0
  63. package/dist/svelte.d.ts +179 -0
  64. package/dist/svelte.d.ts.map +1 -0
  65. package/dist/svelte.js +898 -0
  66. package/dist/tsdoc.d.ts +145 -0
  67. package/dist/tsdoc.d.ts.map +1 -0
  68. package/dist/tsdoc.js +262 -0
  69. package/dist/types.d.ts +3381 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +588 -0
  72. package/dist/typescript-exports.d.ts +91 -0
  73. package/dist/typescript-exports.d.ts.map +1 -0
  74. package/dist/typescript-exports.js +656 -0
  75. package/dist/typescript-extract-class.d.ts +29 -0
  76. package/dist/typescript-extract-class.d.ts.map +1 -0
  77. package/dist/typescript-extract-class.js +251 -0
  78. package/dist/typescript-extract-function.d.ts +47 -0
  79. package/dist/typescript-extract-function.d.ts.map +1 -0
  80. package/dist/typescript-extract-function.js +108 -0
  81. package/dist/typescript-extract-shared.d.ts +166 -0
  82. package/dist/typescript-extract-shared.d.ts.map +1 -0
  83. package/dist/typescript-extract-shared.js +528 -0
  84. package/dist/typescript-extract-type-properties.d.ts +34 -0
  85. package/dist/typescript-extract-type-properties.d.ts.map +1 -0
  86. package/dist/typescript-extract-type-properties.js +241 -0
  87. package/dist/typescript-extract-type.d.ts +48 -0
  88. package/dist/typescript-extract-type.d.ts.map +1 -0
  89. package/dist/typescript-extract-type.js +238 -0
  90. package/dist/typescript-program.d.ts +211 -0
  91. package/dist/typescript-program.d.ts.map +1 -0
  92. package/dist/typescript-program.js +290 -0
  93. package/dist/vite.d.ts +116 -0
  94. package/dist/vite.d.ts.map +1 -0
  95. package/dist/vite.js +418 -0
  96. package/package.json +131 -0
  97. package/virtual-svelte-docinfo.d.ts +49 -0
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Property extraction for TypeScript type aliases.
3
+ *
4
+ * `extractTypeAliasProperties` walks the resolved type and emits members for
5
+ * named properties, index signatures, call signatures, and construct
6
+ * signatures. Handles object literals, intersections, mapped types
7
+ * (Partial, Pick, Readonly, etc.), type references, and function types.
8
+ * Called by `extractTypeInfo` in `typescript-extract-type.ts` for type
9
+ * aliases (interfaces use a different path that walks `node.members`
10
+ * directly).
11
+ *
12
+ * @see `typescript-extract-shared.ts` for shared helpers
13
+ * @see `typescript-extract-type.ts` for the dispatcher
14
+ *
15
+ * @module
16
+ */
17
+ import ts from 'typescript';
18
+ import {} from './diagnostics.js';
19
+ import { parseComment, applyToDeclaration } from './tsdoc.js';
20
+ import {} from './typescript-program.js';
21
+ import { emitCallOrConstructSignature, filterIntersectionProperties, getNodeLocation, isExternalIntersectionBranch, populateCallableMember, resolveIntersectionTypeNode, } from './typescript-extract-shared.js';
22
+ /**
23
+ * Check whether a resolved type has properties worth extracting for documentation.
24
+ *
25
+ * Returns `true` for object-like types (object literals, intersections, mapped types,
26
+ * type references, function types). Returns `false` for types where `getProperties()`
27
+ * would return prototype methods or ambiguous results (unions, primitives, tuples,
28
+ * generic type references like `Array<T>`).
29
+ */
30
+ const hasExtractableProperties = (type) => {
31
+ // Intersections: checker merges properties from all branches
32
+ if (type.isIntersection())
33
+ return true;
34
+ // Unions: ambiguous property set (different branches have different shapes)
35
+ if (type.isUnion())
36
+ return false;
37
+ // Must be an object type
38
+ if (!(type.flags & ts.TypeFlags.Object))
39
+ return false;
40
+ const objFlags = type.objectFlags;
41
+ // Tuples give array prototype methods — not useful
42
+ if (objFlags & ts.ObjectFlags.Tuple)
43
+ return false;
44
+ // Generic type references (Array<T>, Promise<T>, Set<T>) give prototype methods.
45
+ // Mapped types can also have Reference when instantiated (Partial<T>, Pick<T,K>),
46
+ // so allow Reference when Mapped is also set.
47
+ if (objFlags & ts.ObjectFlags.Reference && !(objFlags & ts.ObjectFlags.Mapped))
48
+ return false;
49
+ return true;
50
+ };
51
+ /**
52
+ * Get the index type for a type, filtering out external branches in intersections.
53
+ *
54
+ * For non-intersection types, this delegates to `checker.getIndexTypeOfType`.
55
+ * For intersections, it walks each branch and returns the index type from the
56
+ * first local branch (skipping branches whose declarations are all in external
57
+ * files). Without this filtering, `getIndexTypeOfType` on the merged type would
58
+ * surface index signatures contributed only by external branches like
59
+ * `HTMLAttributes<HTMLDivElement>`, which is wrong for a library's own type.
60
+ *
61
+ * The "first local branch" simplification is conservative: multiple local
62
+ * branches contributing index signatures of the same kind would normally be
63
+ * intersected by the checker, but the merged result can also pull in external
64
+ * contributions through inheritance — so we prefer the simpler local path. This
65
+ * case is exceedingly rare in practice.
66
+ */
67
+ const extractLocalIndexType = (nodeType, typeNode, checker, isExternalFile, indexKind) => {
68
+ if (!nodeType.isIntersection()) {
69
+ return checker.getIndexTypeOfType(nodeType, indexKind);
70
+ }
71
+ const intersectionNode = resolveIntersectionTypeNode(nodeType, typeNode);
72
+ if (!intersectionNode) {
73
+ // Cannot determine branches — fall back to merged type. Conservative:
74
+ // preserves prior behavior for the rare synthesized-intersection case.
75
+ return checker.getIndexTypeOfType(nodeType, indexKind);
76
+ }
77
+ for (const branch of intersectionNode.types) {
78
+ if (isExternalIntersectionBranch(branch, checker, isExternalFile))
79
+ continue;
80
+ const branchType = checker.getTypeAtLocation(branch);
81
+ const branchIndex = checker.getIndexTypeOfType(branchType, indexKind);
82
+ if (branchIndex)
83
+ return branchIndex;
84
+ }
85
+ return undefined;
86
+ };
87
+ /**
88
+ * Resolve, emit, and diagnose a local index signature for a type alias.
89
+ *
90
+ * Wraps `extractLocalIndexType` with the boilerplate shared by string and
91
+ * number kinds: push a `[key: string]` / `[key: number]` member when found,
92
+ * flip `partial: true` and add a `type_extraction_failed` diagnostic on
93
+ * checker errors. Pulled out of the call site to avoid copy-paste drift
94
+ * between the two kinds.
95
+ *
96
+ * @mutates declaration - appends a member when an index sig is present
97
+ * @mutates diagnostics - adds a `type_extraction_failed` diagnostic on checker error
98
+ */
99
+ const emitLocalIndexSignature = (declaration, nodeType, node, checker, diagnostics, isExternalFile, kind) => {
100
+ const indexKind = kind === 'string' ? ts.IndexKind.String : ts.IndexKind.Number;
101
+ try {
102
+ const indexType = extractLocalIndexType(nodeType, node.type, checker, isExternalFile, indexKind);
103
+ if (indexType) {
104
+ (declaration.members ??= []).push({
105
+ name: `[key: ${kind}]`,
106
+ kind: 'variable',
107
+ typeSignature: checker.typeToString(indexType),
108
+ });
109
+ }
110
+ }
111
+ catch (err) {
112
+ declaration.partial = true;
113
+ const loc = getNodeLocation(node);
114
+ diagnostics.push({
115
+ kind: 'type_extraction_failed',
116
+ file: loc.file,
117
+ line: loc.line,
118
+ column: loc.column,
119
+ message: `Failed to extract ${kind} index signature for type "${declaration.name ?? '<default export>'}": ${err instanceof Error ? err.message : String(err)}`,
120
+ severity: 'warning',
121
+ symbolName: declaration.name ?? '<default export>',
122
+ });
123
+ }
124
+ };
125
+ /**
126
+ * Detect whether a property symbol is readonly.
127
+ *
128
+ * Two-layer detection:
129
+ * 1. Check property declarations for `readonly` modifier (works for object literals,
130
+ * intersections, type references)
131
+ * 2. For mapped types with a `readonly` token (e.g., `Readonly<T>`,
132
+ * `{ readonly [K in ...]: ... }`), all properties are readonly regardless
133
+ * of the original declaration
134
+ */
135
+ const isReadonlyProperty = (prop, mappedReadonly) => {
136
+ const decls = prop.getDeclarations();
137
+ if (decls) {
138
+ for (const decl of decls) {
139
+ if (ts.canHaveModifiers(decl)) {
140
+ const mods = ts.getModifiers(decl);
141
+ if (mods?.some((m) => m.kind === ts.SyntaxKind.ReadonlyKeyword))
142
+ return true;
143
+ }
144
+ }
145
+ }
146
+ return mappedReadonly;
147
+ };
148
+ /**
149
+ * Extract properties from a type alias via the TypeScript checker API.
150
+ *
151
+ * Handles object literals, intersections, mapped types (Partial, Pick, Readonly, etc.),
152
+ * type references, and function types. Extracts:
153
+ * - Named properties (with readonly/optional detection, TSDoc from declarations)
154
+ * - Index signatures (string/number)
155
+ * - Call signatures (`(call)`)
156
+ * - Construct signatures (`(construct)`)
157
+ *
158
+ * @mutates declaration - adds members
159
+ */
160
+ export const extractTypeAliasProperties = (node, nodeType, checker, declaration, diagnostics, isExternalFile) => {
161
+ if (!hasExtractableProperties(nodeType))
162
+ return;
163
+ // For intersection types, filter out properties from external sources (node_modules)
164
+ // and extract type names for the `intersects` field
165
+ const intersectionResult = filterIntersectionProperties(nodeType, node.type, checker, isExternalFile);
166
+ if (intersectionResult?.intersectionTypes.length) {
167
+ declaration.intersects = intersectionResult.intersectionTypes;
168
+ }
169
+ // Detect mapped-type-level readonly (e.g., Readonly<T>, { readonly [K in ...]: ... })
170
+ let mappedReadonly = false;
171
+ if (nodeType.flags & ts.TypeFlags.Object &&
172
+ nodeType.objectFlags & ts.ObjectFlags.Mapped) {
173
+ // ts.MappedType is not in the public API, but the `declaration` property
174
+ // exists at runtime on mapped types and holds the MappedTypeNode AST node
175
+ const mappedDecl = nodeType.declaration;
176
+ if (mappedDecl?.readonlyToken) {
177
+ mappedReadonly = true;
178
+ }
179
+ }
180
+ // Extract named properties (filtered for intersections, full for other types)
181
+ const properties = intersectionResult?.properties ?? nodeType.getProperties();
182
+ for (const prop of properties) {
183
+ // Skip internal TypeScript symbols
184
+ if (prop.getName().startsWith('__@'))
185
+ continue;
186
+ const optional = (prop.flags & ts.SymbolFlags.Optional) !== 0;
187
+ const readonly = isReadonlyProperty(prop, mappedReadonly);
188
+ // Determine kind: function (has call signatures) vs variable (property)
189
+ let propType;
190
+ try {
191
+ propType = checker.getTypeOfSymbolAtLocation(prop, node);
192
+ }
193
+ catch {
194
+ continue;
195
+ }
196
+ const callSigs = propType.getCallSignatures();
197
+ const kind = callSigs.length > 0 ? 'function' : 'variable';
198
+ const member = {
199
+ name: prop.getName(),
200
+ kind,
201
+ };
202
+ if (optional)
203
+ member.optional = true;
204
+ // Modifiers
205
+ const modifiers = [];
206
+ if (readonly)
207
+ modifiers.push('readonly');
208
+ if (modifiers.length > 0)
209
+ member.modifiers = modifiers;
210
+ // Extract TSDoc from the property's declaration if available
211
+ const decls = prop.getDeclarations();
212
+ let propTsdoc = undefined;
213
+ if (decls && decls.length > 0) {
214
+ propTsdoc = parseComment(decls[0], decls[0].getSourceFile());
215
+ applyToDeclaration(member, propTsdoc);
216
+ }
217
+ // Type signature and function-specific fields
218
+ if (kind === 'function' && callSigs.length > 0) {
219
+ populateCallableMember(member, callSigs, checker, propTsdoc, decls?.[0] ?? node, prop.getName(), diagnostics);
220
+ }
221
+ else {
222
+ member.typeSignature = checker.typeToString(propType);
223
+ if (optional) {
224
+ // Strip trailing " | undefined" that the checker adds for optional props
225
+ member.typeSignature = checker.typeToString(checker.getNonNullableType(propType));
226
+ }
227
+ }
228
+ (declaration.members ??= []).push(member);
229
+ }
230
+ // Extract index signatures. For intersections, only emit signatures contributed
231
+ // by local branches — external branches like `HTMLAttributes<HTMLDivElement>`
232
+ // otherwise leak their string index signature onto the consuming type.
233
+ emitLocalIndexSignature(declaration, nodeType, node, checker, diagnostics, isExternalFile, 'string');
234
+ emitLocalIndexSignature(declaration, nodeType, node, checker, diagnostics, isExternalFile, 'number');
235
+ // Extract call and construct signatures. TSDoc resolves through the
236
+ // signature's own declaration — for type aliases, that's typically the
237
+ // inline call/construct signature node the user wrote.
238
+ const errorContext = { node, kindLabel: 'type' };
239
+ emitCallOrConstructSignature(() => nodeType.getCallSignatures(), 'call', (sig) => sig.getDeclaration(), node, declaration, checker, diagnostics, errorContext);
240
+ emitCallOrConstructSignature(() => nodeType.getConstructSignatures(), 'construct', (sig) => sig.getDeclaration(), node, declaration, checker, diagnostics, errorContext);
241
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Per-declaration extractors for TypeScript type aliases, interfaces, and enums.
3
+ *
4
+ * `extractTypeInfo` handles type aliases and interfaces (including index/method/
5
+ * call/construct signatures and intersection filtering). `extractEnumInfo`
6
+ * handles regular and const enums. Both mutate a `DeclarationJsonBuild` with
7
+ * rich metadata derived from the TypeScript checker. Called by
8
+ * `analyzeDeclaration` in `typescript-exports.ts` once kind dispatch is
9
+ * settled.
10
+ *
11
+ * Type-alias property walking (named properties, index/call/construct
12
+ * signatures) lives in `typescript-extract-type-properties.ts` to keep this
13
+ * module focused on top-level dispatch.
14
+ *
15
+ * @see `typescript-extract-shared.ts` for shared helpers
16
+ * @see `typescript-extract-type-properties.ts` for type-alias property walking
17
+ *
18
+ * @module
19
+ */
20
+ import ts from 'typescript';
21
+ import type { DeclarationJsonBuild } from './declaration-build.js';
22
+ import { type Diagnostic } from './diagnostics.js';
23
+ import { type IsExternalFile } from './typescript-program.js';
24
+ /**
25
+ * Extract type/interface information with rich property metadata.
26
+ *
27
+ * @internal Used by `analyzeDeclaration` — not part of the public barrel export.
28
+ *
29
+ * @param node - the declaration AST node
30
+ * @param checker - TypeScript type checker
31
+ * @param declaration - the declaration to populate
32
+ * @param diagnostics - diagnostics collector for non-fatal issues
33
+ * @mutates declaration - adds typeSignature, genericParams, extends, intersects, members (and `partial: true` on extraction failure)
34
+ * @mutates diagnostics - adds `type_extraction_failed` / `signature_analysis_failed` diagnostics on checker errors
35
+ */
36
+ export declare const extractTypeInfo: (node: ts.Node, checker: ts.TypeChecker, declaration: DeclarationJsonBuild, diagnostics: Array<Diagnostic>, isExternalFile: IsExternalFile) => void;
37
+ /**
38
+ * Extract enum member information from an enum declaration.
39
+ *
40
+ * Iterates `node.members` to extract each enum member's name, initializer value,
41
+ * type, and JSDoc. Members are represented as `MemberJson` with kind `'variable'`.
42
+ *
43
+ * @internal Used by `analyzeDeclaration` — not part of the public barrel export.
44
+ *
45
+ * @mutates declaration - adds members and typeSignature
46
+ */
47
+ export declare const extractEnumInfo: (node: ts.Node, checker: ts.TypeChecker, declaration: DeclarationJsonBuild, diagnostics: Array<Diagnostic>) => void;
48
+ //# sourceMappingURL=typescript-extract-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-extract-type.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/typescript-extract-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAAC,oBAAoB,EAAkB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAC,KAAK,cAAc,EAAC,MAAM,yBAAyB,CAAC;AAU5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAC3B,MAAM,EAAE,CAAC,IAAI,EACb,SAAS,EAAE,CAAC,WAAW,EACvB,aAAa,oBAAoB,EACjC,aAAa,KAAK,CAAC,UAAU,CAAC,EAC9B,gBAAgB,cAAc,KAC5B,IA4KF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAC3B,MAAM,EAAE,CAAC,IAAI,EACb,SAAS,EAAE,CAAC,WAAW,EACvB,aAAa,oBAAoB,EACjC,aAAa,KAAK,CAAC,UAAU,CAAC,KAC5B,IA6DF,CAAC"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Per-declaration extractors for TypeScript type aliases, interfaces, and enums.
3
+ *
4
+ * `extractTypeInfo` handles type aliases and interfaces (including index/method/
5
+ * call/construct signatures and intersection filtering). `extractEnumInfo`
6
+ * handles regular and const enums. Both mutate a `DeclarationJsonBuild` with
7
+ * rich metadata derived from the TypeScript checker. Called by
8
+ * `analyzeDeclaration` in `typescript-exports.ts` once kind dispatch is
9
+ * settled.
10
+ *
11
+ * Type-alias property walking (named properties, index/call/construct
12
+ * signatures) lives in `typescript-extract-type-properties.ts` to keep this
13
+ * module focused on top-level dispatch.
14
+ *
15
+ * @see `typescript-extract-shared.ts` for shared helpers
16
+ * @see `typescript-extract-type-properties.ts` for type-alias property walking
17
+ *
18
+ * @module
19
+ */
20
+ import ts from 'typescript';
21
+ import {} from './diagnostics.js';
22
+ import { parseComment, applyToDeclaration } from './tsdoc.js';
23
+ import {} from './typescript-program.js';
24
+ import { emitCallOrConstructSignature, extractModifiers, getNodeLocation, parseGenericParam, populateCallableMember, } from './typescript-extract-shared.js';
25
+ import { extractTypeAliasProperties } from './typescript-extract-type-properties.js';
26
+ /**
27
+ * Extract type/interface information with rich property metadata.
28
+ *
29
+ * @internal Used by `analyzeDeclaration` — not part of the public barrel export.
30
+ *
31
+ * @param node - the declaration AST node
32
+ * @param checker - TypeScript type checker
33
+ * @param declaration - the declaration to populate
34
+ * @param diagnostics - diagnostics collector for non-fatal issues
35
+ * @mutates declaration - adds typeSignature, genericParams, extends, intersects, members (and `partial: true` on extraction failure)
36
+ * @mutates diagnostics - adds `type_extraction_failed` / `signature_analysis_failed` diagnostics on checker errors
37
+ */
38
+ export const extractTypeInfo = (node, checker, declaration, diagnostics, isExternalFile) => {
39
+ let nodeType;
40
+ try {
41
+ nodeType = checker.getTypeAtLocation(node);
42
+ declaration.typeSignature = checker.typeToString(nodeType);
43
+ }
44
+ catch (err) {
45
+ declaration.partial = true;
46
+ const loc = getNodeLocation(node);
47
+ diagnostics.push({
48
+ kind: 'type_extraction_failed',
49
+ file: loc.file,
50
+ line: loc.line,
51
+ column: loc.column,
52
+ message: `Failed to extract type for "${declaration.name}": ${err instanceof Error ? err.message : String(err)}`,
53
+ severity: 'warning',
54
+ symbolName: declaration.name ?? '<default export>',
55
+ });
56
+ }
57
+ if (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) {
58
+ if (node.typeParameters?.length) {
59
+ declaration.genericParams = node.typeParameters.map(parseGenericParam);
60
+ }
61
+ }
62
+ if (ts.isTypeAliasDeclaration(node) && nodeType) {
63
+ extractTypeAliasProperties(node, nodeType, checker, declaration, diagnostics, isExternalFile);
64
+ }
65
+ if (ts.isInterfaceDeclaration(node)) {
66
+ if (node.heritageClauses) {
67
+ declaration.extends = node.heritageClauses
68
+ .filter((hc) => hc.token === ts.SyntaxKind.ExtendsKeyword)
69
+ .flatMap((hc) => hc.types.map((t) => t.getText()));
70
+ }
71
+ // Extract properties and method signatures with full metadata
72
+ const processedMethods = new Set();
73
+ for (const member of node.members) {
74
+ if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
75
+ const propName = member.name.text;
76
+ const propDeclaration = {
77
+ name: propName,
78
+ kind: 'variable',
79
+ };
80
+ if (member.questionToken) {
81
+ propDeclaration.optional = true;
82
+ }
83
+ // Extract modifiers
84
+ const modifierFlags = extractModifiers(ts.getModifiers(member));
85
+ if (modifierFlags.length > 0) {
86
+ propDeclaration.modifiers = modifierFlags;
87
+ }
88
+ // Extract type
89
+ if (member.type) {
90
+ propDeclaration.typeSignature = member.type.getText();
91
+ }
92
+ // Extract TSDoc (applies docComment, examples, deprecated, seeAlso, since)
93
+ const propTsdoc = parseComment(member, node.getSourceFile());
94
+ applyToDeclaration(propDeclaration, propTsdoc);
95
+ (declaration.members ??= []).push(propDeclaration);
96
+ }
97
+ else if (ts.isMethodSignature(member) && member.name) {
98
+ const methodName = ts.isIdentifier(member.name) ? member.name.text : member.name.getText();
99
+ if (!methodName || processedMethods.has(methodName))
100
+ continue;
101
+ processedMethods.add(methodName);
102
+ const methodDeclaration = {
103
+ name: methodName,
104
+ kind: 'function',
105
+ };
106
+ if (member.questionToken) {
107
+ methodDeclaration.optional = true;
108
+ }
109
+ // Extract modifiers
110
+ const modifierFlags = extractModifiers(ts.getModifiers(member));
111
+ if (modifierFlags.length > 0) {
112
+ methodDeclaration.modifiers = modifierFlags;
113
+ }
114
+ // Extract generic type parameters
115
+ if (member.typeParameters?.length) {
116
+ methodDeclaration.genericParams = member.typeParameters.map(parseGenericParam);
117
+ }
118
+ // Extract TSDoc
119
+ const methodTsdoc = parseComment(member, node.getSourceFile());
120
+ applyToDeclaration(methodDeclaration, methodTsdoc);
121
+ // Extract signatures via type checker
122
+ try {
123
+ const memberSymbol = checker.getSymbolAtLocation(member.name);
124
+ if (memberSymbol) {
125
+ const memberType = checker.getTypeOfSymbolAtLocation(memberSymbol, member);
126
+ populateCallableMember(methodDeclaration, memberType.getCallSignatures(), checker, methodTsdoc, member, methodName, diagnostics);
127
+ }
128
+ }
129
+ catch (err) {
130
+ methodDeclaration.partial = true;
131
+ const loc = getNodeLocation(member);
132
+ diagnostics.push({
133
+ kind: 'signature_analysis_failed',
134
+ file: loc.file,
135
+ line: loc.line,
136
+ column: loc.column,
137
+ message: `Failed to analyze interface method "${methodName}": ${err instanceof Error ? err.message : String(err)}`,
138
+ severity: 'warning',
139
+ functionName: methodName,
140
+ });
141
+ }
142
+ (declaration.members ??= []).push(methodDeclaration);
143
+ }
144
+ else if (ts.isIndexSignatureDeclaration(member)) {
145
+ const param = member.parameters[0];
146
+ if (param && ts.isIdentifier(param.name) && param.type) {
147
+ const keyType = param.type.getText();
148
+ const name = `[${param.name.text}: ${keyType}]`;
149
+ const indexDeclaration = { name, kind: 'variable' };
150
+ if (member.type) {
151
+ indexDeclaration.typeSignature = member.type.getText();
152
+ }
153
+ const indexTsdoc = parseComment(member, node.getSourceFile());
154
+ applyToDeclaration(indexDeclaration, indexTsdoc);
155
+ (declaration.members ??= []).push(indexDeclaration);
156
+ }
157
+ }
158
+ }
159
+ // Extract call and construct signatures from interface type. TSDoc comes
160
+ // from inline signature declarations on this interface — inherited
161
+ // signatures resolve through `getCallSignatures()` but their docs are
162
+ // intentionally not surfaced here.
163
+ const interfaceType = nodeType ?? checker.getTypeAtLocation(node);
164
+ const errorContext = { node, kindLabel: 'interface' };
165
+ emitCallOrConstructSignature(() => interfaceType.getCallSignatures(), 'call', () => node.members.find(ts.isCallSignatureDeclaration), node, declaration, checker, diagnostics, errorContext);
166
+ emitCallOrConstructSignature(() => interfaceType.getConstructSignatures(), 'construct', () => node.members.find(ts.isConstructSignatureDeclaration), node, declaration, checker, diagnostics, errorContext);
167
+ }
168
+ };
169
+ /**
170
+ * Extract enum member information from an enum declaration.
171
+ *
172
+ * Iterates `node.members` to extract each enum member's name, initializer value,
173
+ * type, and JSDoc. Members are represented as `MemberJson` with kind `'variable'`.
174
+ *
175
+ * @internal Used by `analyzeDeclaration` — not part of the public barrel export.
176
+ *
177
+ * @mutates declaration - adds members and typeSignature
178
+ */
179
+ export const extractEnumInfo = (node, checker, declaration, diagnostics) => {
180
+ // Extract type signature
181
+ try {
182
+ const nodeType = checker.getTypeAtLocation(node);
183
+ declaration.typeSignature = checker.typeToString(nodeType);
184
+ }
185
+ catch (err) {
186
+ declaration.partial = true;
187
+ const loc = getNodeLocation(node);
188
+ diagnostics.push({
189
+ kind: 'type_extraction_failed',
190
+ file: loc.file,
191
+ line: loc.line,
192
+ column: loc.column,
193
+ message: `Failed to extract type for "${declaration.name}": ${err instanceof Error ? err.message : String(err)}`,
194
+ severity: 'warning',
195
+ symbolName: declaration.name ?? '<default export>',
196
+ });
197
+ }
198
+ if (!ts.isEnumDeclaration(node))
199
+ return;
200
+ for (const member of node.members) {
201
+ const memberName = ts.isIdentifier(member.name)
202
+ ? member.name.text
203
+ : ts.isStringLiteral(member.name)
204
+ ? member.name.text
205
+ : member.name.getText();
206
+ if (!memberName)
207
+ continue;
208
+ const memberDeclaration = {
209
+ name: memberName,
210
+ kind: 'variable',
211
+ };
212
+ // Extract TSDoc
213
+ const memberTsdoc = parseComment(member, node.getSourceFile());
214
+ applyToDeclaration(memberDeclaration, memberTsdoc);
215
+ // Extract type and value via the checker
216
+ try {
217
+ const memberSymbol = checker.getSymbolAtLocation(member.name);
218
+ if (memberSymbol) {
219
+ const memberType = checker.getTypeOfSymbolAtLocation(memberSymbol, member);
220
+ memberDeclaration.typeSignature = checker.typeToString(memberType);
221
+ }
222
+ }
223
+ catch (err) {
224
+ memberDeclaration.partial = true;
225
+ const loc = getNodeLocation(member);
226
+ diagnostics.push({
227
+ kind: 'type_extraction_failed',
228
+ file: loc.file,
229
+ line: loc.line,
230
+ column: loc.column,
231
+ message: `Failed to extract type for enum member "${memberName}" in "${declaration.name}": ${err instanceof Error ? err.message : String(err)}`,
232
+ severity: 'warning',
233
+ symbolName: memberName,
234
+ });
235
+ }
236
+ (declaration.members ??= []).push(memberDeclaration);
237
+ }
238
+ };