# core/parsers/definition.parser.ts

> 123 lines of code and 43 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-parsers-definition-parser-ts
Source text: https://banes-lab.com/source/patterns/core/parsers/definition.parser.ts.txt

Listed in [core/parsers](https://banes-lab.com/api/source/patterns/core/parsers.md), after [core/parsers/code.parser.ts](https://banes-lab.com/source/patterns/core/parsers/code.parser.ts.md) and before [core/parsers/dependency.parser.ts](https://banes-lab.com/source/patterns/core/parsers/dependency.parser.ts.md).

## Definitions

- `codeSymbols` (lexical_declaration, line 131, exported)
- `descendName` (lexical_declaration, line 42)
- `collectAliases` (lexical_declaration, line 64)
- `symbolWalk` (lexical_declaration, line 109)
- `lastSegment` (lexical_declaration, line 50)
- `isMemberCall` (lexical_declaration, line 79)
- `isCallableDef` (lexical_declaration, line 84)
- `symbolAt` (lexical_declaration, line 92)
- `nameOf` (lexical_declaration, line 55)
- `NAME_FIELD` (lexical_declaration, line 8)
- `ALIAS_FIELD` (lexical_declaration, line 9)
- `CALL_FUNCTION_FIELD` (lexical_declaration, line 10)
- `VALUE_FIELD` (lexical_declaration, line 11)
- `IMPORT_SPECIFIER` (lexical_declaration, line 12)
- `EXPORT_TOKEN` (lexical_declaration, line 13)
- `EXPORT_SPECIFIER` (lexical_declaration, line 14)
- `SEGMENT` (lexical_declaration, line 15)
- `NAME_FIELD_BY_ROLE` (lexical_declaration, line 17)
- `MEMBER_CALL_TYPES` (lexical_declaration, line 21)
- `FUNCTION_KIND_TOKENS` (lexical_declaration, line 27)
- `FUNCTION_VALUE_TYPES` (lexical_declaration, line 28)
- `SymbolSite` (interface_declaration, line 36)
- `dot` (lexical_declaration, line 51)
- `field` (lexical_declaration, line 56)
- `direct` (lexical_declaration, line 60)
- `aliases` (lexical_declaration, line 65)
- `imported` (lexical_declaration, line 66)
- `local` (lexical_declaration, line 67)
- `fn` (lexical_declaration, line 80)
- `lower` (lexical_declaration, line 85)
- `hasFunctionValue` (lexical_declaration, line 86)
- `role` (lexical_declaration, line 114)
- `found` (lexical_declaration, line 115)
- `name` (lexical_declaration, line 116)
- `here` (lexical_declaration, line 117)
- `childEnclosing` (lexical_declaration, line 118)
- `exportNamesAt` (lexical_declaration, line 122)
- `active` (lexical_declaration, line 123)
- `isDefinition` (lexical_declaration, line 124)
- `defName` (lexical_declaration, line 125)
- `specName` (lexical_declaration, line 126)
- `nested` (lexical_declaration, line 127)
- `exported` (lexical_declaration, line 132, exported)

## Contained in

- [core/parsers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-parsers.md)

## Uses

- [core/converters/code.converter.ts](https://banes-lab.com/source/patterns/core/converters/code.converter.ts.md)
- [core/selectors/syntax.selector.ts](https://banes-lab.com/source/patterns/core/selectors/syntax.selector.ts.md)

## Used by

- [core/parsers/code.parser.ts](https://banes-lab.com/source/patterns/core/parsers/code.parser.ts.md)

## Linked from

- [core/converters](https://banes-lab.com/anatomy/patterns/folder-patterns-core-converters.md)
- [core/parsers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-parsers.md)
- [core/selectors](https://banes-lab.com/anatomy/patterns/folder-patterns-core-selectors.md)

## Source

```typescript
import { ROLE_CALL, ROLE_DEFINITION, ROLE_NODE, roleOf } from "#core/classifiers/syntax.classifier";
import { childNodes, fieldText, fieldType, lineOf } from "#core/selectors/syntax.selector";
import type { CodeSymbol } from "#types/code.types";
import type { CstNode } from "@govlab/code-parse";
import { MODULE_SCOPE } from "#configuration/constants/report.constants";
import { bodyHash } from "#core/converters/code.converter";

const NAME_FIELD = "name";
const ALIAS_FIELD = "alias";
const CALL_FUNCTION_FIELD = "function";
const VALUE_FIELD = "value";
const IMPORT_SPECIFIER = "import_specifier";
const EXPORT_TOKEN = "export";
const EXPORT_SPECIFIER = "export_specifier";
const SEGMENT = ".";

const NAME_FIELD_BY_ROLE: ReadonlyMap<string, string> = new Map([
    [ROLE_DEFINITION, NAME_FIELD],
    [ROLE_CALL, CALL_FUNCTION_FIELD],
]);
const MEMBER_CALL_TYPES: ReadonlySet<string> = new Set([
    "member_expression",
    "selector_expression",
    "attribute",
    "field_expression",
]);
const FUNCTION_KIND_TOKENS: readonly string[] = ["function", "method"];
const FUNCTION_VALUE_TYPES: ReadonlySet<string> = new Set([
    "arrow_function",
    "function",
    "function_expression",
    "generator_function",
    "function_definition",
]);

interface SymbolSite {
    enclosing: string;
    name: string;
    role: string;
}

const descendName = function descendName(node: CstNode): string {
    return (
        childNodes(node)
            .map((child) => fieldText(child, NAME_FIELD))
            .find((nested) => nested.length > 0) ?? ""
    );
};

const lastSegment = function lastSegment(name: string): string {
    const dot = name.lastIndexOf(SEGMENT);
    return dot === -1 ? name : name.slice(dot + 1);
};

const nameOf = function nameOf(node: CstNode, role: string): string {
    const field = NAME_FIELD_BY_ROLE.get(role);
    if (field === undefined) {
        return "";
    }
    const direct = fieldText(node, field);
    return lastSegment(direct.length > 0 ? direct : descendName(node));
};

const collectAliases = function collectAliases(node: CstNode): Map<string, string> {
    const aliases = new Map<string, string>();
    const imported = node.type === IMPORT_SPECIFIER ? fieldText(node, NAME_FIELD) : "";
    const local = node.type === IMPORT_SPECIFIER ? fieldText(node, ALIAS_FIELD) : "";
    if (imported.length > 0 && local.length > 0) {
        aliases.set(local, imported);
    }
    for (const child of childNodes(node)) {
        for (const [key, value] of collectAliases(child)) {
            aliases.set(key, value);
        }
    }
    return aliases;
};

const isMemberCall = function isMemberCall(node: CstNode): boolean {
    const fn = fieldType(node, CALL_FUNCTION_FIELD);
    return fn !== undefined && MEMBER_CALL_TYPES.has(fn);
};

const isCallableDef = function isCallableDef(node: CstNode): boolean {
    const lower = node.type.toLowerCase();
    const hasFunctionValue = childNodes(node).some((child) =>
        FUNCTION_VALUE_TYPES.has(fieldType(child, VALUE_FIELD) ?? ""),
    );
    return FUNCTION_KIND_TOKENS.some((token) => lower.includes(token)) || hasFunctionValue;
};

const symbolAt = function symbolAt(node: CstNode, site: SymbolSite): CodeSymbol {
    const isDefinition = site.role === ROLE_DEFINITION;
    return {
        callable: isDefinition && isCallableDef(node),
        enclosing: site.enclosing,
        exported: false,
        file: "",
        hash: isDefinition ? bodyHash(node.text ?? "") : "",
        kind: node.type,
        line: lineOf(node),
        member: site.role === ROLE_CALL && isMemberCall(node),
        name: site.name,
        role: site.role,
        size: isDefinition ? (node.text ?? "").length : 0,
    };
};

const symbolWalk = function symbolWalk(
    node: CstNode,
    enclosing: string,
    aliases: ReadonlyMap<string, string>,
): CodeSymbol[] {
    const role = node.isNamed ? roleOf(node.type) : ROLE_NODE;
    const found = nameOf(node, role);
    const name = role === ROLE_CALL ? (aliases.get(found) ?? found) : found;
    const here = name.length > 0 && role !== ROLE_NODE ? [symbolAt(node, { enclosing, name, role })] : [];
    const childEnclosing = role === ROLE_DEFINITION && name.length > 0 ? name : enclosing;
    return [...here, ...childNodes(node).flatMap((child) => symbolWalk(child, childEnclosing, aliases))];
};

const exportNamesAt = function exportNamesAt(node: CstNode, underExport: boolean): string[] {
    const active = underExport || node.type.includes(EXPORT_TOKEN);
    const isDefinition = active && node.isNamed && roleOf(node.type) === ROLE_DEFINITION;
    const defName = isDefinition ? nameOf(node, ROLE_DEFINITION) : "";
    const specName = node.type === EXPORT_SPECIFIER ? fieldText(node, NAME_FIELD) : "";
    const nested = childNodes(node).flatMap((child) => exportNamesAt(child, active));
    return [defName, specName, ...nested].filter((name) => name.length > 0);
};

export const codeSymbols = function codeSymbols(root: CstNode): CodeSymbol[] {
    const exported = new Set(exportNamesAt(root, false));
    return symbolWalk(root, MODULE_SCOPE, collectAliases(root)).map((symbol) =>
        symbol.role === ROLE_DEFINITION && exported.has(symbol.name) ? { ...symbol, exported: true } : symbol,
    );
};
```
