# shared/selectors/syntax.selector.ts

> 149 lines of code and 38 definitions.

Tree: Governance tree
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-selectors-syntax-selector-ts
Source text: https://banes-lab.com/source/governance/shared/selectors/syntax.selector.ts.txt

Listed in [shared/selectors](https://banes-lab.com/api/source/governance/shared/selectors.md), after [shared/selectors/specifier.selector.ts](https://banes-lab.com/source/governance/shared/selectors/specifier.selector.ts.md).

## Definitions

- `isType` (lexical_declaration, line 64, exported)
- `nodeAt` (lexical_declaration, line 19, exported)
- `nameOf` (lexical_declaration, line 90, exported)
- `nodesAt` (lexical_declaration, line 23, exported)
- `locOf` (lexical_declaration, line 3, exported)
- `recordAt` (lexical_declaration, line 37, exported)
- `stringIn` (lexical_declaration, line 42, exported)
- `literalString` (lexical_declaration, line 68, exported)
- `stringAt` (lexical_declaration, line 28, exported)
- `argumentAt` (lexical_declaration, line 115, exported)
- `exportedNamesOf` (lexical_declaration, line 137, exported)
- `propertyKeyName` (lexical_declaration, line 111, exported)
- `staticTextOf` (lexical_declaration, line 76, exported)
- `asNode` (lexical_declaration, line 15, exported)
- `childrenOf` (lexical_declaration, line 158)
- `isAstNode` (lexical_declaration, line 7, exported)
- `isRecord` (lexical_declaration, line 33)
- `numberAt` (lexical_declaration, line 47, exported)
- `membersOf` (lexical_declaration, line 150)
- `declaredNames` (lexical_declaration, line 121)
- `calleeName` (lexical_declaration, line 100, exported)
- `walk` (lexical_declaration, line 164, exported)
- `candidate` (lexical_declaration, line 11, exported)
- `booleanAt` (lexical_declaration, line 52, exported)
- `handlerKey` (lexical_declaration, line 56, exported)
- `typeOf` (lexical_declaration, line 60, exported)
- `value` (lexical_declaration, line 72, exported)
- `[part]` (lexical_declaration, line 86, exported)
- `callee` (lexical_declaration, line 101, exported)
- `NAMED_DECLARATIONS` (lexical_declaration, line 119)
- `id` (lexical_declaration, line 130)
- `names` (lexical_declaration, line 138, exported)
- `exported` (lexical_declaration, line 140, exported)
- `BACK_REFERENCES` (lexical_declaration, line 148)
- `child` (lexical_declaration, line 154)
- `seen` (lexical_declaration, line 165, exported)
- `stack` (lexical_declaration, line 166, exported)
- `node` (lexical_declaration, line 168, exported)

## Used by

- [rules/eslint/closure-barrel-must-be-glob.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-barrel-must-be-glob.eslint.rule.ts.md)
- [rules/eslint/closure-discriminated-union-must-be-exhaustive.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-discriminated-union-must-be-exhaustive.eslint.rule.ts.md)
- [rules/eslint/closure-event-system-has-trajectory.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-event-system-has-trajectory.eslint.rule.ts.md)
- [rules/eslint/closure-lifecycle-handle-must-be-stored.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-lifecycle-handle-must-be-stored.eslint.rule.ts.md)
- [rules/eslint/closure-no-business-inheritance.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-business-inheritance.eslint.rule.ts.md)
- [rules/eslint/closure-no-cross-member-relative.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-cross-member-relative.eslint.rule.ts.md)
- [rules/eslint/closure-no-double-cast.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-double-cast.eslint.rule.ts.md)
- [rules/eslint/closure-no-module-singleton.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-module-singleton.eslint.rule.ts.md)
- [rules/eslint/closure-no-null-managed-instance.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-null-managed-instance.eslint.rule.ts.md)
- [rules/eslint/closure-no-reachable-dead-exports.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-reachable-dead-exports.eslint.rule.ts.md)
- [rules/eslint/closure-paths-via-ssot.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-paths-via-ssot.eslint.rule.ts.md)
- [rules/eslint/closure-register-needs-consumer.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-register-needs-consumer.eslint.rule.ts.md)
- [rules/eslint/closure-register-verb-reserved.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-register-verb-reserved.eslint.rule.ts.md)
- [rules/eslint/closure-registry-consumer-naming.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-registry-consumer-naming.eslint.rule.ts.md)
- [rules/eslint/closure-rule-shape.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-rule-shape.eslint.rule.ts.md)
- [rules/eslint/closure-show-flag-polarity.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-show-flag-polarity.eslint.rule.ts.md)
- [rules/eslint/closure-transitive-liveness.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-transitive-liveness.eslint.rule.ts.md)
- [rules/eslint/factory-owned-value.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/factory-owned-value.eslint.rule.ts.md)
- [rules/eslint/no-exclusion-membership.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-exclusion-membership.eslint.rule.ts.md)
- [rules/eslint/no-hardcoded-exclusions.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-hardcoded-exclusions.eslint.rule.ts.md)
- [rules/eslint/no-inline-asset-path.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-inline-asset-path.eslint.rule.ts.md)
- [rules/eslint/no-inline-icon-options.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-inline-icon-options.eslint.rule.ts.md)
- [rules/eslint/no-inline-style-write.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-inline-style-write.eslint.rule.ts.md)
- [rules/eslint/no-linear-scan-lookup.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-linear-scan-lookup.eslint.rule.ts.md)
- [rules/eslint/no-literal-strings.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-literal-strings.eslint.rule.ts.md)
- [rules/eslint/no-plaintext-transport.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-plaintext-transport.eslint.rule.ts.md)
- [rules/eslint/no-port-literal.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-port-literal.eslint.rule.ts.md)
- [rules/eslint/no-raw-dom.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-raw-dom.eslint.rule.ts.md)
- [rules/eslint/no-string-literal-ids.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-string-literal-ids.eslint.rule.ts.md)
- [rules/eslint/no-swallowed-failure.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-swallowed-failure.eslint.rule.ts.md)
- [rules/eslint/require-cache-coverage.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/require-cache-coverage.eslint.rule.ts.md)
- [rules/eslint/require-component.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/require-component.eslint.rule.ts.md)
- [rules/eslint/require-pool-registration.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/require-pool-registration.eslint.rule.ts.md)
- [rules/eslint/require-self-registration.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/require-self-registration.eslint.rule.ts.md)
- [rules/eslint/require-snapshot-coverage.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/require-snapshot-coverage.eslint.rule.ts.md)
- [rules/eslint/retired-collection-id.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/retired-collection-id.eslint.rule.ts.md)
- [rules/eslint/strings-composition.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-composition.eslint.rule.ts.md)
- [rules/eslint/strings-derived-numbering.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-derived-numbering.eslint.rule.ts.md)
- [rules/eslint/strings-diagram-identifiers.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-diagram-identifiers.eslint.rule.ts.md)
- [rules/eslint/strings-harness-neutral.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-harness-neutral.eslint.rule.ts.md)
- [rules/eslint/strings-lesson-fields.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-lesson-fields.eslint.rule.ts.md)
- [rules/eslint/strings-link-construction.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-link-construction.eslint.rule.ts.md)
- [rules/eslint/strings-punctuation.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-punctuation.eslint.rule.ts.md)
- [rules/eslint/strings-sentence-shape.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-sentence-shape.eslint.rule.ts.md)
- [rules/eslint/strings-terminology.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-terminology.eslint.rule.ts.md)
- [rules/eslint/strings-vocabulary.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-vocabulary.eslint.rule.ts.md)
- [rules/eslint/validated-construction.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/validated-construction.eslint.rule.ts.md)
- [shared/analyzers/location.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/location.analyzer.ts.md)
- [shared/analyzers/rule.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/rule.analyzer.ts.md)
- [shared/analyzers/segment.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/segment.analyzer.ts.md)
- [shared/predicates/location.predicate.ts](https://banes-lab.com/source/governance/shared/predicates/location.predicate.ts.md)
- [shared/selectors/literal.selector.ts](https://banes-lab.com/source/governance/shared/selectors/literal.selector.ts.md)
- [shared/selectors/sink.selector.ts](https://banes-lab.com/source/governance/shared/selectors/sink.selector.ts.md)

## Source

```typescript
import type { AstNode, ExportedName, SourceLoc } from "../../types/syntax.types.ts";

export const locOf = function locOf(node: AstNode): SourceLoc {
    return node.loc;
};

export const isAstNode = function isAstNode(value: unknown): value is AstNode {
    if (typeof value !== "object" || value === null) {
        return false;
    }
    const candidate = value as { type?: unknown; loc?: unknown };
    return typeof candidate.type === "string" && typeof candidate.loc === "object" && candidate.loc !== null;
};

export const asNode = function asNode(value: unknown): AstNode | null {
    return isAstNode(value) ? value : null;
};

export const nodeAt = function nodeAt(node: AstNode | null, key: string): AstNode | null {
    return node === null ? null : asNode(node[key]);
};

export const nodesAt = function nodesAt(node: AstNode | null, key: string): AstNode[] {
    const value = node === null ? undefined : node[key];
    return Array.isArray(value) ? value.filter(isAstNode) : [];
};

export const stringAt = function stringAt(node: AstNode | null, key: string): string {
    const value = node === null ? undefined : node[key];
    return typeof value === "string" ? value : "";
};

const isRecord = function isRecord(value: unknown): value is Record<string, unknown> {
    return typeof value === "object" && value !== null;
};

export const recordAt = function recordAt(node: AstNode | null, key: string): Record<string, unknown> | null {
    const value = node === null ? undefined : node[key];
    return isRecord(value) ? value : null;
};

export const stringIn = function stringIn(record: Record<string, unknown> | null, key: string): string {
    const value = record === null ? undefined : record[key];
    return typeof value === "string" ? value : "";
};

export const numberAt = function numberAt(node: AstNode | null, key: string): number | null {
    const value = node === null ? undefined : node[key];
    return typeof value === "number" ? value : null;
};

export const booleanAt = function booleanAt(node: AstNode | null, key: string): boolean {
    return node?.[key] === true;
};

export const handlerKey = function handlerKey(type: string): string {
    return type.slice(0, 1).toLowerCase() + type.slice(1);
};

export const typeOf = function typeOf(node: AstNode | null): string {
    return node === null ? "" : node.type;
};

export const isType = function isType(node: AstNode | null, type: string): boolean {
    return node?.type === type;
};

export const literalString = function literalString(node: AstNode | null): string | null {
    if (!isType(node, "Literal")) {
        return null;
    }
    const value = node === null ? undefined : node["value"];
    return typeof value === "string" ? value : null;
};

export const staticTextOf = function staticTextOf(node: AstNode | null): string | null {
    if (node === null) {
        return null;
    }
    if (node.type === "Literal") {
        return literalString(node);
    }
    if (node.type !== "TemplateLiteral" || nodesAt(node, "expressions").length > 0) {
        return null;
    }
    const [part] = nodesAt(node, "quasis");
    return part === undefined ? null : stringIn(recordAt(part, "value"), "cooked");
};

export const nameOf = function nameOf(node: AstNode | null): string {
    if (node === null) {
        return "";
    }
    if (node.type === "Identifier" || node.type === "PrivateIdentifier") {
        return stringAt(node, "name");
    }
    return literalString(node) ?? "";
};

export const calleeName = function calleeName(node: AstNode | null): string {
    const callee = nodeAt(node, "callee");
    if (callee === null) {
        return "";
    }
    if (callee.type === "MemberExpression") {
        return nameOf(nodeAt(callee, "property"));
    }
    return nameOf(callee);
};

export const propertyKeyName = function propertyKeyName(node: AstNode | null): string {
    return nameOf(nodeAt(node, "key"));
};

export const argumentAt = function argumentAt(node: AstNode | null, index: number): AstNode | null {
    return nodesAt(node, "arguments")[index] ?? null;
};

const NAMED_DECLARATIONS = new Set(["FunctionDeclaration", "ClassDeclaration"]);

const declaredNames = function declaredNames(declaration: AstNode | null): ExportedName[] {
    if (declaration === null) {
        return [];
    }
    if (declaration.type === "VariableDeclaration") {
        return nodesAt(declaration, "declarations")
            .filter((d) => isType(nodeAt(d, "id"), "Identifier"))
            .map((d) => ({ name: nameOf(nodeAt(d, "id")), target: d }));
    }
    const id = nodeAt(declaration, "id");
    if (NAMED_DECLARATIONS.has(declaration.type) && isType(id, "Identifier")) {
        return [{ name: nameOf(id), target: declaration }];
    }
    return [];
};

export const exportedNamesOf = function exportedNamesOf(node: AstNode): ExportedName[] {
    const names = declaredNames(nodeAt(node, "declaration"));
    for (const spec of nodesAt(node, "specifiers")) {
        const exported = nodeAt(spec, "exported");
        if (isType(exported, "Identifier")) {
            names.push({ name: nameOf(exported), target: spec });
        }
    }
    return names;
};

const BACK_REFERENCES = new Set(["parent"]);

const membersOf = function membersOf(value: unknown): AstNode[] {
    if (Array.isArray(value)) {
        return value.filter(isAstNode);
    }
    const child = asNode(value);
    return child === null ? [] : [child];
};

const childrenOf = function childrenOf(node: AstNode): AstNode[] {
    return Object.entries(node)
        .filter(([key]) => !BACK_REFERENCES.has(key))
        .flatMap(([, value]) => membersOf(value));
};

export const walk = function walk(root: AstNode, visit: (node: AstNode) => void): void {
    const seen = new Set<AstNode>();
    const stack: AstNode[] = [root];
    while (stack.length > 0) {
        const node = stack.pop();
        if (node === undefined || seen.has(node)) {
            continue;
        }
        seen.add(node);
        visit(node);
        stack.push(...childrenOf(node));
    }
};
```
