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,528 @@
1
+ /**
2
+ * Shared utilities for the per-declaration extractors in `typescript-extract-*.ts`.
3
+ *
4
+ * Holds helpers used across function, type, and class extraction: signature
5
+ * parameter extraction, overload detection, generic parsing, modifier
6
+ * extraction, location reporting, intersection-property filtering, and runes
7
+ * detection.
8
+ *
9
+ * @see `typescript-extract-function.ts`, `typescript-extract-type.ts`,
10
+ * `typescript-extract-class.ts` for the per-declaration extractors that
11
+ * build on these helpers
12
+ *
13
+ * @module
14
+ */
15
+ import ts from 'typescript';
16
+ import {} from './diagnostics.js';
17
+ import { applyToDeclaration, parseComment } from './tsdoc.js';
18
+ import {} from './typescript-program.js';
19
+ /**
20
+ * Infer declaration kind from symbol and node.
21
+ *
22
+ * Maps TypeScript constructs to `DeclarationKind`:
23
+ * - Classes → `'class'`
24
+ * - Functions (declarations, expressions, arrows) → `'function'`
25
+ * - Interfaces → `'interface'`
26
+ * - Type aliases → `'type'`
27
+ * - Enums (regular and const) → `'enum'`
28
+ * - Variables → `'variable'` (unless function-valued → `'function'`)
29
+ *
30
+ * Note: namespace re-exports (`export * as ns from './x'`) have no inline
31
+ * declaration form in TypeScript and are caught upstream in `analyzeExports`
32
+ * via `classifyNamespaceReExport`. They never reach this function. A direct
33
+ * call here on a `ValueModule` symbol would fall through to `'variable'` and
34
+ * leak `typeof import("/abs/path")` into the output — keep the namespace
35
+ * dispatch in `analyzeExports`.
36
+ */
37
+ export const inferDeclarationKind = (symbol, node) => {
38
+ // Check symbol flags
39
+ if (symbol.flags & ts.SymbolFlags.Class)
40
+ return 'class';
41
+ if (symbol.flags & ts.SymbolFlags.Function)
42
+ return 'function';
43
+ if (symbol.flags & ts.SymbolFlags.Interface)
44
+ return 'interface';
45
+ if (symbol.flags & ts.SymbolFlags.TypeAlias)
46
+ return 'type';
47
+ if (symbol.flags & ts.SymbolFlags.Enum)
48
+ return 'enum';
49
+ if (symbol.flags & ts.SymbolFlags.ConstEnum)
50
+ return 'enum';
51
+ // Check node kind
52
+ if (ts.isFunctionDeclaration(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node))
53
+ return 'function';
54
+ if (ts.isClassDeclaration(node))
55
+ return 'class';
56
+ if (ts.isInterfaceDeclaration(node))
57
+ return 'interface';
58
+ if (ts.isTypeAliasDeclaration(node))
59
+ return 'type';
60
+ if (ts.isEnumDeclaration(node))
61
+ return 'enum';
62
+ if (ts.isVariableDeclaration(node)) {
63
+ // Check if it's a function-valued variable
64
+ const init = node.initializer;
65
+ if (init && (ts.isArrowFunction(init) || ts.isFunctionExpression(init))) {
66
+ return 'function';
67
+ }
68
+ return 'variable';
69
+ }
70
+ return 'variable';
71
+ };
72
+ /**
73
+ * Extract parameters from a TypeScript signature with TSDoc descriptions and default values.
74
+ *
75
+ * Shared helper for extracting parameter information from both standalone functions
76
+ * and class methods/constructors.
77
+ *
78
+ * @param sig - the TypeScript signature to extract parameters from
79
+ * @param checker - TypeScript type checker for type resolution
80
+ * @param tsdocParams - record of parameter names to TSDoc descriptions (from `TsdocParsedComment.params`)
81
+ * @returns array of `ParameterJson` objects
82
+ */
83
+ export const extractSignatureParameters = (sig, checker, tsdocParams) => {
84
+ return sig.parameters.map((param) => {
85
+ const paramDecl = param.valueDeclaration;
86
+ // Get type - use declaration location if available, otherwise get declared type
87
+ let typeString = 'unknown';
88
+ if (paramDecl) {
89
+ const paramType = checker.getTypeOfSymbolAtLocation(param, paramDecl);
90
+ typeString = checker.typeToString(paramType);
91
+ }
92
+ else {
93
+ const paramType = checker.getDeclaredTypeOfSymbol(param);
94
+ typeString = checker.typeToString(paramType);
95
+ }
96
+ // Get TSDoc description for this parameter
97
+ const description = tsdocParams?.[param.name];
98
+ // Extract default value from AST
99
+ let defaultValue;
100
+ if (paramDecl && ts.isParameter(paramDecl) && paramDecl.initializer) {
101
+ defaultValue = paramDecl.initializer.getText();
102
+ }
103
+ const optional = !!(paramDecl && ts.isParameter(paramDecl) && paramDecl.questionToken);
104
+ const rest = !!(paramDecl && ts.isParameter(paramDecl) && paramDecl.dotDotDotToken);
105
+ return {
106
+ name: param.name,
107
+ type: typeString,
108
+ optional,
109
+ rest,
110
+ description,
111
+ defaultValue,
112
+ };
113
+ });
114
+ };
115
+ /**
116
+ * Emit `unknown_param` warnings for `@param` keys that don't reference a real
117
+ * parameter. Catches typos (`@param argz` for `args`) and stale doc after a
118
+ * rename. The description is dropped silently by `extractSignatureParameters`;
119
+ * this surfaces the drop without halting.
120
+ *
121
+ * @internal Helper for `extractOverloads` and other `@param`-extracting sites.
122
+ */
123
+ const validateParamKeys = (tsdocParams, parameters, declNode, functionName, diagnostics) => {
124
+ if (!tsdocParams)
125
+ return;
126
+ const known = new Set(parameters.map((p) => p.name));
127
+ for (const key of Object.keys(tsdocParams)) {
128
+ if (!known.has(key)) {
129
+ const loc = getNodeLocation(declNode);
130
+ diagnostics.push({
131
+ kind: 'unknown_param',
132
+ file: loc.file,
133
+ line: loc.line,
134
+ column: loc.column,
135
+ message: `@param "${key}" on "${functionName}" doesn't match any parameter (typo or stale doc?)`,
136
+ severity: 'warning',
137
+ paramName: key,
138
+ functionName,
139
+ });
140
+ }
141
+ }
142
+ };
143
+ /**
144
+ * Collect symbol-scope JSDoc tags present on a parsed comment.
145
+ *
146
+ * Symbol-scope tags describe the function as a whole and belong on the
147
+ * primary signature's JSDoc (which feeds the parent declaration). Used by
148
+ * `extractOverloads` to detect tags misplaced on non-primary overloads.
149
+ *
150
+ * @internal
151
+ */
152
+ const collectSymbolScopeTags = (tsdoc) => {
153
+ const found = [];
154
+ if (tsdoc.examples?.length)
155
+ found.push('example');
156
+ if (tsdoc.deprecatedMessage !== undefined)
157
+ found.push('deprecated');
158
+ if (tsdoc.since)
159
+ found.push('since');
160
+ if (tsdoc.seeAlso?.length)
161
+ found.push('see');
162
+ if (tsdoc.throws?.length)
163
+ found.push('throws');
164
+ if (tsdoc.mutates && Object.keys(tsdoc.mutates).length > 0)
165
+ found.push('mutates');
166
+ if (tsdoc.defaultValue !== undefined)
167
+ found.push('default');
168
+ if (tsdoc.nodocs)
169
+ found.push('nodocs');
170
+ return found;
171
+ };
172
+ /**
173
+ * Extract all public overload signatures for a function.
174
+ *
175
+ * Each overload gets its own typeSignature, parameters, returnType, and
176
+ * per-overload JSDoc if available. The implementation signature is excluded
177
+ * (TypeScript's `getCallSignatures()` already omits it).
178
+ *
179
+ * Per-overload `@param` descriptions flow through to that overload's
180
+ * `parameters[i].description`. Per-overload `@returns` populates
181
+ * `returnDescription`. These are signature-scope: each overload may
182
+ * describe its own parameters and return value distinctly.
183
+ *
184
+ * Symbol-scope tags (`@example`, `@deprecated`, `@since`, `@see`, `@throws`,
185
+ * `@mutates`) describe the function as a whole and belong on the parent
186
+ * declaration. The primary overload — the one whose JSDoc text matches the
187
+ * parent's `docComment` — already feeds the parent's symbol-level extraction,
188
+ * so its symbol-scope tags reach the parent through that path. On non-primary
189
+ * overloads, symbol-scope tags would otherwise be silently dropped from
190
+ * output; this function emits a `misplaced_tag` warning instead, pointing the
191
+ * author at the primary signature.
192
+ *
193
+ * @param signatures - all call signatures from the type checker
194
+ * @param checker - TypeScript type checker
195
+ * @param parentTsdoc - parsed JSDoc of the parent declaration (for primary-signature detection)
196
+ * @param parentName - parent function/method name (for diagnostic messages)
197
+ * @param diagnostics - diagnostics collector for non-fatal issues
198
+ * @returns array of overload info objects
199
+ */
200
+ const extractOverloads = (signatures, checker, parentTsdoc, parentName, diagnostics) => {
201
+ return signatures.map((sig) => {
202
+ const decl = sig.getDeclaration();
203
+ const sourceFile = decl.getSourceFile();
204
+ const tsdoc = parseComment(decl, sourceFile);
205
+ const typeSignature = checker.signatureToString(sig);
206
+ const returnType = checker.typeToString(checker.getReturnTypeOfSignature(sig));
207
+ const parameters = extractSignatureParameters(sig, checker, tsdoc?.params);
208
+ validateParamKeys(tsdoc?.params, parameters, decl, parentName, diagnostics);
209
+ const overload = { typeSignature, parameters, returnType };
210
+ if (tsdoc?.text) {
211
+ overload.docComment = tsdoc.text;
212
+ }
213
+ if (tsdoc?.returns) {
214
+ overload.returnDescription = tsdoc.returns;
215
+ }
216
+ // Extract per-overload generic type parameters
217
+ if (ts.isFunctionLike(decl) && decl.typeParameters?.length) {
218
+ overload.genericParams = decl.typeParameters.map(parseGenericParam);
219
+ }
220
+ // Detect primary overload by matching JSDoc text against the parent's.
221
+ // The TS API resolves the parent declaration's JSDoc by walking from the
222
+ // implementation node to the first overload signature with JSDoc; that
223
+ // signature is the "primary" — its symbol-scope tags already reach the
224
+ // parent through symbol-level extraction. Non-primary overloads with
225
+ // symbol-scope tags would silently lose them; surface as warnings instead.
226
+ const isPrimary = tsdoc?.text !== undefined &&
227
+ parentTsdoc?.text !== undefined &&
228
+ tsdoc.text === parentTsdoc.text;
229
+ if (!isPrimary && tsdoc) {
230
+ const misplaced = collectSymbolScopeTags(tsdoc);
231
+ if (misplaced.length > 0) {
232
+ const loc = getNodeLocation(decl);
233
+ for (const tagName of misplaced) {
234
+ diagnostics.push({
235
+ kind: 'misplaced_tag',
236
+ file: loc.file,
237
+ line: loc.line,
238
+ column: loc.column,
239
+ message: `@${tagName} on non-primary overload of "${parentName}" — place it on the primary signature's JSDoc instead (symbol-scope tags describe the function as a whole)`,
240
+ severity: 'warning',
241
+ tagName,
242
+ functionName: parentName,
243
+ });
244
+ }
245
+ }
246
+ }
247
+ return overload;
248
+ });
249
+ };
250
+ /**
251
+ * Populate the callable fields of a declaration or member from its call/construct
252
+ * signatures: `typeSignature`, `parameters`, `overloads`, and (unless
253
+ * `includeReturn` is false) `returnType` / `returnDescription`.
254
+ *
255
+ * The shared core of every named-callable extractor — standalone functions,
256
+ * interface methods, class methods/constructors, and type-alias function
257
+ * properties. Callers differ in how they obtain `signatures` (symbol type,
258
+ * constructor declarations, property call signatures) and in their own
259
+ * try/catch + diagnostic kind, so those stay at the callsite; this captures
260
+ * only the identical projection from a resolved signature list onto the build
261
+ * target. No-op when `signatures` is empty.
262
+ *
263
+ * @param target - declaration or member build object (mutated)
264
+ * @param signatures - public call/construct signatures (`signatures[0]` is primary)
265
+ * @param tsdoc - parsed TSDoc for the target (supplies `@param`/`@returns`)
266
+ * @param paramValidationNode - node `validateParamKeys` reports `unknown_param` against
267
+ * @param name - target name, for diagnostic messages
268
+ * @param includeReturn - set `false` for constructors (no return type/description)
269
+ * @mutates target - sets typeSignature, parameters, overloads, returnType, returnDescription
270
+ * @mutates diagnostics - via `validateParamKeys` / `extractOverloads`
271
+ */
272
+ export const populateCallableMember = (target, signatures, checker, tsdoc, paramValidationNode, name, diagnostics, includeReturn = true) => {
273
+ if (signatures.length === 0)
274
+ return;
275
+ const sig = signatures[0];
276
+ target.typeSignature = checker.signatureToString(sig);
277
+ if (includeReturn) {
278
+ target.returnType = checker.typeToString(checker.getReturnTypeOfSignature(sig));
279
+ if (tsdoc?.returns)
280
+ target.returnDescription = tsdoc.returns;
281
+ }
282
+ target.parameters = extractSignatureParameters(sig, checker, tsdoc?.params);
283
+ validateParamKeys(tsdoc?.params, target.parameters, paramValidationNode, name, diagnostics);
284
+ if (signatures.length > 1) {
285
+ target.overloads = extractOverloads(signatures, checker, tsdoc, name, diagnostics);
286
+ }
287
+ };
288
+ /**
289
+ * Check whether all declarations of a property symbol are in external source files.
290
+ * Properties with no declarations (synthesized) are considered non-external.
291
+ */
292
+ const isExternalProperty = (prop, isExternalFile) => {
293
+ const decls = prop.getDeclarations();
294
+ if (!decls?.length)
295
+ return false;
296
+ return decls.every((d) => isExternalFile(d.getSourceFile()));
297
+ };
298
+ /**
299
+ * Determine whether an intersection branch refers to a type whose declarations
300
+ * all live in external files (e.g., `HTMLAttributes<HTMLDivElement>` from svelte's
301
+ * d.ts). Inline object-literal branches and unrecognized node shapes return false
302
+ * (treated as local).
303
+ */
304
+ export const isExternalIntersectionBranch = (branchNode, checker, isExternalFile) => {
305
+ if (!ts.isTypeReferenceNode(branchNode) && !ts.isIndexedAccessTypeNode(branchNode)) {
306
+ return false;
307
+ }
308
+ const branchType = checker.getTypeAtLocation(branchNode);
309
+ const sym = branchType.aliasSymbol ?? branchType.symbol;
310
+ const decls = sym.getDeclarations();
311
+ if (!decls?.length)
312
+ return false;
313
+ return decls.every((d) => isExternalFile(d.getSourceFile()));
314
+ };
315
+ /**
316
+ * Resolve the `IntersectionTypeNode` for a type node, walking through type
317
+ * alias indirection. Returns `undefined` when the underlying type is an
318
+ * intersection but no AST node is recoverable (rare — synthesized intersections).
319
+ */
320
+ export const resolveIntersectionTypeNode = (type, typeNode) => {
321
+ if (ts.isIntersectionTypeNode(typeNode))
322
+ return typeNode;
323
+ const aliasDecl = type.aliasSymbol?.declarations?.[0];
324
+ if (aliasDecl &&
325
+ ts.isTypeAliasDeclaration(aliasDecl) &&
326
+ ts.isIntersectionTypeNode(aliasDecl.type)) {
327
+ return aliasDecl.type;
328
+ }
329
+ return undefined;
330
+ };
331
+ /**
332
+ * Filter intersection type properties to exclude those from external sources,
333
+ * and extract type names for branches whose properties are all external.
334
+ *
335
+ * Returns `null` for non-intersection types (caller should use unfiltered properties).
336
+ * For intersection types, returns filtered properties and external type names.
337
+ *
338
+ * Per-property filtering works through utility types (Partial, Pick, Readonly)
339
+ * because TypeScript preserves original declaration sources on mapped type properties.
340
+ */
341
+ export const filterIntersectionProperties = (type, typeNode, checker, isExternalFile) => {
342
+ if (!type.isIntersection())
343
+ return null;
344
+ const properties = type
345
+ .getProperties()
346
+ .filter((prop) => !isExternalProperty(prop, isExternalFile));
347
+ // Resolve through type aliases (e.g., svelte2tsx $$ComponentProps) to find
348
+ // the IntersectionTypeNode for `intersects` extraction.
349
+ const intersectionNode = resolveIntersectionTypeNode(type, typeNode);
350
+ const intersectionTypes = [];
351
+ if (intersectionNode) {
352
+ for (const branch of intersectionNode.types) {
353
+ if (ts.isTypeReferenceNode(branch) || ts.isIndexedAccessTypeNode(branch)) {
354
+ const branchType = checker.getTypeAtLocation(branch);
355
+ const branchProps = branchType.getProperties();
356
+ if (branchProps.length > 0 &&
357
+ branchProps.every((p) => isExternalProperty(p, isExternalFile))) {
358
+ intersectionTypes.push(branch.getText());
359
+ }
360
+ }
361
+ }
362
+ }
363
+ return { properties, intersectionTypes };
364
+ };
365
+ /**
366
+ * Detect a Svelte 5 reactivity rune from a variable or property initializer.
367
+ *
368
+ * Inspects the AST since runes erase to their inner type after type-checking.
369
+ * Returns `undefined` for any non-rune expression. See the `Reactivity` enum
370
+ * in `types.ts` for the rationale on running this on every file regardless of
371
+ * extension.
372
+ */
373
+ export const detectReactivity = (initializer) => {
374
+ // Unwrap type-only wrappers so e.g. `$state(0) as Foo` and `($state(0))` are
375
+ // still detected. Runtime semantics are unchanged by these wrappers.
376
+ let expr = initializer;
377
+ while (expr &&
378
+ (ts.isParenthesizedExpression(expr) ||
379
+ ts.isAsExpression(expr) ||
380
+ ts.isSatisfiesExpression(expr) ||
381
+ ts.isNonNullExpression(expr) ||
382
+ ts.isTypeAssertionExpression(expr))) {
383
+ expr = expr.expression;
384
+ }
385
+ if (!expr || !ts.isCallExpression(expr))
386
+ return undefined;
387
+ const callee = expr.expression;
388
+ if (ts.isIdentifier(callee)) {
389
+ if (callee.text === '$state')
390
+ return '$state';
391
+ if (callee.text === '$derived')
392
+ return '$derived';
393
+ return undefined;
394
+ }
395
+ if (ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.expression)) {
396
+ const base = callee.expression.text;
397
+ const prop = callee.name.text;
398
+ if (base === '$state' && prop === 'raw')
399
+ return '$state.raw';
400
+ if (base === '$derived' && prop === 'by')
401
+ return '$derived.by';
402
+ }
403
+ return undefined;
404
+ };
405
+ /**
406
+ * Extract line and column from a TypeScript node.
407
+ * Returns 1-based line and column numbers.
408
+ */
409
+ export const getNodeLocation = (node) => {
410
+ const sourceFile = node.getSourceFile();
411
+ const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
412
+ return {
413
+ file: sourceFile.fileName,
414
+ line: line + 1, // Convert to 1-based
415
+ column: character + 1, // Convert to 1-based
416
+ };
417
+ };
418
+ /**
419
+ * Parse a TypeScript generic type parameter declaration into structured info.
420
+ *
421
+ * @param param - the TypeScript type parameter declaration node
422
+ * @returns structured `GenericParamJson` with name, constraint, and default type
423
+ */
424
+ export const parseGenericParam = (param) => {
425
+ const result = {
426
+ name: param.name.text,
427
+ };
428
+ if (param.constraint) {
429
+ result.constraint = param.constraint.getText();
430
+ }
431
+ if (param.default) {
432
+ result.defaultType = param.default.getText();
433
+ }
434
+ return result;
435
+ };
436
+ /**
437
+ * Extract modifier keywords from a node's modifiers.
438
+ *
439
+ * Returns an array of modifier strings like `['public', 'readonly', 'static']`.
440
+ */
441
+ export const extractModifiers = (modifiers) => {
442
+ const modifierFlags = [];
443
+ if (!modifiers)
444
+ return modifierFlags;
445
+ for (const mod of modifiers) {
446
+ if (mod.kind === ts.SyntaxKind.PublicKeyword)
447
+ modifierFlags.push('public');
448
+ else if (mod.kind === ts.SyntaxKind.ProtectedKeyword)
449
+ modifierFlags.push('protected');
450
+ else if (mod.kind === ts.SyntaxKind.ReadonlyKeyword)
451
+ modifierFlags.push('readonly');
452
+ else if (mod.kind === ts.SyntaxKind.StaticKeyword)
453
+ modifierFlags.push('static');
454
+ else if (mod.kind === ts.SyntaxKind.AbstractKeyword)
455
+ modifierFlags.push('abstract');
456
+ }
457
+ return modifierFlags;
458
+ };
459
+ /**
460
+ * Append a `(call)` or `(construct)` signature member to a declaration.
461
+ *
462
+ * Captures the extraction logic shared by interface processing
463
+ * (`extractTypeInfo`) and type-alias property processing
464
+ * (`extractTypeAliasProperties`): type signature, parameters, generics,
465
+ * overloads, and TSDoc. The TSDoc source node is supplied by the caller —
466
+ * interfaces look it up in `node.members` (skipping TSDoc when no inline
467
+ * signature is declared, even if one is inherited), type aliases use
468
+ * `sig.getDeclaration()`.
469
+ *
470
+ * @param getSignatures - thunk to retrieve `getCallSignatures()` /
471
+ * `getConstructSignatures()`; called inside the try so checker errors are
472
+ * captured as diagnostics
473
+ * @param signatureKind - `'call'` (member kind: function, includes returnType)
474
+ * or `'construct'` (member kind: constructor, no returnType)
475
+ * @param resolveTsdocNode - callback returning the AST node to parse TSDoc
476
+ * from, or `undefined` to skip TSDoc resolution
477
+ * @param paramValidationFallbackNode - location used by `validateParamKeys`
478
+ * when `resolveTsdocNode` returns `undefined`
479
+ * @param declaration - parent declaration (mutated; appended to `members`)
480
+ * @param errorContext.node - parent node used to locate diagnostics
481
+ * @param errorContext.kindLabel - `'interface'` or `'type'`, included in the
482
+ * diagnostic message
483
+ *
484
+ * @mutates declaration - appends a member when signatures are present;
485
+ * sets `partial: true` on extraction failure
486
+ * @mutates diagnostics - adds `signature_analysis_failed` on checker error
487
+ */
488
+ export const emitCallOrConstructSignature = (getSignatures, signatureKind, resolveTsdocNode, paramValidationFallbackNode, declaration, checker, diagnostics, errorContext) => {
489
+ try {
490
+ const signatures = getSignatures();
491
+ if (signatures.length === 0)
492
+ return;
493
+ const memberName = signatureKind === 'call' ? '(call)' : '(construct)';
494
+ const memberKind = signatureKind === 'call' ? 'function' : 'constructor';
495
+ const member = { name: memberName, kind: memberKind };
496
+ const sig = signatures[0];
497
+ member.typeSignature = checker.signatureToString(sig);
498
+ if (signatureKind === 'call') {
499
+ member.returnType = checker.typeToString(checker.getReturnTypeOfSignature(sig));
500
+ }
501
+ const tsdocNode = resolveTsdocNode(sig);
502
+ const tsdoc = tsdocNode ? parseComment(tsdocNode, tsdocNode.getSourceFile()) : undefined;
503
+ applyToDeclaration(member, tsdoc);
504
+ member.parameters = extractSignatureParameters(sig, checker, tsdoc?.params);
505
+ validateParamKeys(tsdoc?.params, member.parameters, tsdocNode ?? paramValidationFallbackNode, memberName, diagnostics);
506
+ const sigDecl = sig.getDeclaration();
507
+ if (ts.isFunctionLike(sigDecl) && sigDecl.typeParameters?.length) {
508
+ member.genericParams = sigDecl.typeParameters.map(parseGenericParam);
509
+ }
510
+ if (signatures.length > 1) {
511
+ member.overloads = extractOverloads(signatures, checker, tsdoc, memberName, diagnostics);
512
+ }
513
+ (declaration.members ??= []).push(member);
514
+ }
515
+ catch (err) {
516
+ declaration.partial = true;
517
+ const loc = getNodeLocation(errorContext.node);
518
+ diagnostics.push({
519
+ kind: 'signature_analysis_failed',
520
+ file: loc.file,
521
+ line: loc.line,
522
+ column: loc.column,
523
+ message: `Failed to analyze ${signatureKind} signatures for ${errorContext.kindLabel} "${declaration.name}": ${err instanceof Error ? err.message : String(err)}`,
524
+ severity: 'warning',
525
+ functionName: declaration.name ?? '<default export>',
526
+ });
527
+ }
528
+ };
@@ -0,0 +1,34 @@
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 type { DeclarationJsonBuild } from './declaration-build.js';
19
+ import { type Diagnostic } from './diagnostics.js';
20
+ import { type IsExternalFile } from './typescript-program.js';
21
+ /**
22
+ * Extract properties from a type alias via the TypeScript checker API.
23
+ *
24
+ * Handles object literals, intersections, mapped types (Partial, Pick, Readonly, etc.),
25
+ * type references, and function types. Extracts:
26
+ * - Named properties (with readonly/optional detection, TSDoc from declarations)
27
+ * - Index signatures (string/number)
28
+ * - Call signatures (`(call)`)
29
+ * - Construct signatures (`(construct)`)
30
+ *
31
+ * @mutates declaration - adds members
32
+ */
33
+ export declare const extractTypeAliasProperties: (node: ts.TypeAliasDeclaration, nodeType: ts.Type, checker: ts.TypeChecker, declaration: DeclarationJsonBuild, diagnostics: Array<Diagnostic>, isExternalFile: IsExternalFile) => void;
34
+ //# sourceMappingURL=typescript-extract-type-properties.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-extract-type-properties.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/typescript-extract-type-properties.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,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;AA+J5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,EAAE,CAAC,oBAAoB,EAC7B,UAAU,EAAE,CAAC,IAAI,EACjB,SAAS,EAAE,CAAC,WAAW,EACvB,aAAa,oBAAoB,EACjC,aAAa,KAAK,CAAC,UAAU,CAAC,EAC9B,gBAAgB,cAAc,KAC5B,IA2IF,CAAC"}