/**
* Per-declaration extractors for TypeScript functions and variables.
*
* Both `extractFunctionInfo` and `extractVariableInfo` mutate a
* `DeclarationJsonBuild` with rich metadata derived from the TypeScript
* checker. Called by `analyzeDeclaration` in `typescript-exports.ts` once
* kind dispatch is settled.
*
* @see `typescript-extract-shared.ts` for shared helpers (signatures,
* overloads, generics, reactivity)
*
* @module
*/
import ts from 'typescript';
import type {DeclarationJsonBuild} from './declaration-build.js';
import {type Diagnostic} from './diagnostics.js';
import type {TsdocParsedComment} from './tsdoc.js';
import {
detectReactivity,
getNodeLocation,
parseGenericParam,
populateCallableMember,
} from './typescript-extract-shared.js';
/**
* Extract function/method information including parameters
* with descriptions and default values.
*
* @internal Used by `analyzeDeclaration` — not part of the public barrel export.
*
* @param node - the declaration AST node
* @param symbol - the TypeScript symbol
* @param checker - TypeScript type checker
* @param declaration - the declaration to populate
* @param tsdoc - parsed TSDoc comment (if available)
* @param diagnostics - diagnostics collector for non-fatal issues
* @mutates declaration - adds typeSignature, returnType, returnDescription, parameters, genericParams, overloads (and `partial: true` on signature failure)
* @mutates diagnostics - adds `signature_analysis_failed` diagnostic on checker error
*/
export const extractFunctionInfo = (
node: ts.Node,
symbol: ts.Symbol,
checker: ts.TypeChecker,
declaration: DeclarationJsonBuild,
tsdoc: TsdocParsedComment | undefined,
diagnostics: Array<Diagnostic>,
): void => {
try {
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
const signatures = type.getCallSignatures();
populateCallableMember(declaration, signatures, checker, tsdoc, node, symbol.name, diagnostics);
} catch (err) {
declaration.partial = true;
const loc = getNodeLocation(node);
diagnostics.push({
kind: 'signature_analysis_failed',
file: loc.file,
line: loc.line,
column: loc.column,
message: `Failed to analyze signature for "${symbol.name}": ${err instanceof Error ? err.message : String(err)}`,
severity: 'warning',
functionName: symbol.name,
});
}
// Extract generic type parameters
let typeParameters: ts.NodeArray<ts.TypeParameterDeclaration> | undefined;
if (ts.isFunctionDeclaration(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
typeParameters = node.typeParameters;
} else if (ts.isVariableDeclaration(node)) {
if (node.type && ts.isFunctionTypeNode(node.type)) {
// `export const fn: <T>(x: T) => T = (x) => x` — generics on the type annotation.
// Annotation wins over initializer when both carry generics: it's the public type.
typeParameters = node.type.typeParameters;
} else if (
node.initializer &&
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))
) {
// `export const foo = <T>(x: T) => x` — generics live on the initializer, not the variable.
typeParameters = node.initializer.typeParameters;
}
}
if (typeParameters?.length) {
declaration.genericParams = typeParameters.map(parseGenericParam);
}
};
/**
* Extract variable information.
*
* @internal Used by `analyzeDeclaration` — not part of the public barrel export.
*
* @param node - the declaration AST node
* @param symbol - the TypeScript symbol
* @param checker - TypeScript type checker
* @param declaration - the declaration to populate
* @param diagnostics - diagnostics collector for non-fatal issues
* @mutates declaration - adds typeSignature, reactivity (when initialized with a Svelte rune)
*/
export const extractVariableInfo = (
node: ts.Node,
symbol: ts.Symbol,
checker: ts.TypeChecker,
declaration: DeclarationJsonBuild,
diagnostics: Array<Diagnostic>,
): void => {
try {
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
declaration.typeSignature = checker.typeToString(type);
} catch (err) {
declaration.partial = true;
const loc = getNodeLocation(node);
diagnostics.push({
kind: 'type_extraction_failed',
file: loc.file,
line: loc.line,
column: loc.column,
message: `Failed to extract type for variable "${symbol.name}": ${err instanceof Error ? err.message : String(err)}`,
severity: 'warning',
symbolName: symbol.name,
});
}
// Outside the try so reactivity is still captured if type extraction throws.
if (ts.isVariableDeclaration(node)) {
const reactivity = detectReactivity(node.initializer);
if (reactivity) declaration.reactivity = reactivity;
}
};
{
"path": "typescript-extract-function.ts",
"declarations": [
{
"name": "extractFunctionInfo",
"kind": "function",
"docComment": "Extract function/method information including parameters\nwith descriptions and default values.",
"typeSignature": "(node: Node, symbol: Symbol, checker: TypeChecker, declaration: DeclarationJsonBuild, tsdoc: TsdocParsedComment | undefined, diagnostics: ({ ...; } | ... 12 more ... | { ...; })[]): void",
"sourceLine": 42,
"mutates": {
"declaration": "adds typeSignature, returnType, returnDescription, parameters, genericParams, overloads (and `partial: true` on signature failure)",
"diagnostics": "adds `signature_analysis_failed` diagnostic on checker error"
},
"parameters": [
{
"name": "node",
"type": "Node",
"description": "the declaration AST node"
},
{
"name": "symbol",
"type": "Symbol",
"description": "the TypeScript symbol"
},
{
"name": "checker",
"type": "TypeChecker",
"description": "TypeScript type checker"
},
{
"name": "declaration",
"type": "DeclarationJsonBuild",
"description": "the declaration to populate"
},
{
"name": "tsdoc",
"type": "TsdocParsedComment | undefined",
"description": "parsed TSDoc comment (if available)"
},
{
"name": "diagnostics",
"type": "({ symbolName: string; file: string; message: string; severity: \"error\" | \"warning\"; kind: \"type_extraction_failed\"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 11 more ... | { ...; })[]",
"description": "diagnostics collector for non-fatal issues"
}
],
"returnType": "void"
},
{
"name": "extractVariableInfo",
"kind": "function",
"docComment": "Extract variable information.",
"typeSignature": "(node: Node, symbol: Symbol, checker: TypeChecker, declaration: DeclarationJsonBuild, diagnostics: ({ symbolName: string; file: string; ... 4 more ...; column?: number | undefined; } | ... 12 more ... | { ...; })[]): void",
"sourceLine": 102,
"mutates": {
"declaration": "adds typeSignature, reactivity (when initialized with a Svelte rune)"
},
"parameters": [
{
"name": "node",
"type": "Node",
"description": "the declaration AST node"
},
{
"name": "symbol",
"type": "Symbol",
"description": "the TypeScript symbol"
},
{
"name": "checker",
"type": "TypeChecker",
"description": "TypeScript type checker"
},
{
"name": "declaration",
"type": "DeclarationJsonBuild",
"description": "the declaration to populate"
},
{
"name": "diagnostics",
"type": "({ symbolName: string; file: string; message: string; severity: \"error\" | \"warning\"; kind: \"type_extraction_failed\"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 11 more ... | { ...; })[]",
"description": "diagnostics collector for non-fatal issues"
}
],
"returnType": "void"
}
],
"moduleComment": "Per-declaration extractors for TypeScript functions and variables.\n\nBoth `extractFunctionInfo` and `extractVariableInfo` mutate a\n`DeclarationJsonBuild` with rich metadata derived from the TypeScript\nchecker. Called by `analyzeDeclaration` in `typescript-exports.ts` once\nkind dispatch is settled.\n\n@see `typescript-extract-shared.ts` for shared helpers (signatures,\n overloads, generics, reactivity)",
"dependencies": [
"declaration-build.ts",
"diagnostics.ts",
"tsdoc.ts",
"typescript-extract-shared.ts"
],
"dependents": [
"typescript-exports.ts"
]
}