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.
- package/LICENSE +21 -0
- package/README.md +349 -0
- package/dist/analyze-core.d.ts +1386 -0
- package/dist/analyze-core.d.ts.map +1 -0
- package/dist/analyze-core.js +324 -0
- package/dist/analyze.d.ts +137 -0
- package/dist/analyze.d.ts.map +1 -0
- package/dist/analyze.js +148 -0
- package/dist/cli.d.ts +102 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +206 -0
- package/dist/concurrency.d.ts +38 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +57 -0
- package/dist/declaration-build.d.ts +164 -0
- package/dist/declaration-build.d.ts.map +1 -0
- package/dist/declaration-build.js +15 -0
- package/dist/declaration-helpers.d.ts +169 -0
- package/dist/declaration-helpers.d.ts.map +1 -0
- package/dist/declaration-helpers.js +239 -0
- package/dist/dep-resolver.d.ts +166 -0
- package/dist/dep-resolver.d.ts.map +1 -0
- package/dist/dep-resolver.js +144 -0
- package/dist/diagnostics.d.ts +650 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +377 -0
- package/dist/discovery.d.ts +115 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +126 -0
- package/dist/exports.d.ts +105 -0
- package/dist/exports.d.ts.map +1 -0
- package/dist/exports.js +274 -0
- package/dist/files.d.ts +77 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +95 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/log.d.ts +32 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +11 -0
- package/dist/logo.d.ts +8 -0
- package/dist/logo.d.ts.map +1 -0
- package/dist/logo.js +9 -0
- package/dist/main.d.ts +11 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +18 -0
- package/dist/paths.d.ts +35 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +34 -0
- package/dist/postprocess.d.ts +145 -0
- package/dist/postprocess.d.ts.map +1 -0
- package/dist/postprocess.js +305 -0
- package/dist/session.d.ts +248 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +496 -0
- package/dist/source-config.d.ts +330 -0
- package/dist/source-config.d.ts.map +1 -0
- package/dist/source-config.js +356 -0
- package/dist/source.d.ts +126 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +68 -0
- package/dist/svelte.d.ts +179 -0
- package/dist/svelte.d.ts.map +1 -0
- package/dist/svelte.js +898 -0
- package/dist/tsdoc.d.ts +145 -0
- package/dist/tsdoc.d.ts.map +1 -0
- package/dist/tsdoc.js +262 -0
- package/dist/types.d.ts +3381 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +588 -0
- package/dist/typescript-exports.d.ts +91 -0
- package/dist/typescript-exports.d.ts.map +1 -0
- package/dist/typescript-exports.js +656 -0
- package/dist/typescript-extract-class.d.ts +29 -0
- package/dist/typescript-extract-class.d.ts.map +1 -0
- package/dist/typescript-extract-class.js +251 -0
- package/dist/typescript-extract-function.d.ts +47 -0
- package/dist/typescript-extract-function.d.ts.map +1 -0
- package/dist/typescript-extract-function.js +108 -0
- package/dist/typescript-extract-shared.d.ts +166 -0
- package/dist/typescript-extract-shared.d.ts.map +1 -0
- package/dist/typescript-extract-shared.js +528 -0
- package/dist/typescript-extract-type-properties.d.ts +34 -0
- package/dist/typescript-extract-type-properties.d.ts.map +1 -0
- package/dist/typescript-extract-type-properties.js +241 -0
- package/dist/typescript-extract-type.d.ts +48 -0
- package/dist/typescript-extract-type.d.ts.map +1 -0
- package/dist/typescript-extract-type.js +238 -0
- package/dist/typescript-program.d.ts +211 -0
- package/dist/typescript-program.d.ts.map +1 -0
- package/dist/typescript-program.js +290 -0
- package/dist/vite.d.ts +116 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +418 -0
- package/package.json +131 -0
- 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
|
+
};
|