# rules/eslint/closure-rule-declarations-resolve.eslint.rule.ts

> 95 lines of code and 19 definitions.

Tree: Governance tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/governance#file-governance-rules-eslint-closure-rule-declarations-resolve-eslint-rule-ts
Source text: https://banes-lab.com/source/governance/rules/eslint/closure-rule-declarations-resolve.eslint.rule.ts.txt

Listed in [rules/eslint](https://banes-lab.com/api/source/governance/rules/eslint.md), after [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) and before [rules/eslint/closure-rule-shape.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-rule-shape.eslint.rule.ts.md).

## Definitions

- `declaringFiles` (lexical_declaration, line 43)
- `unresolvedIn` (lexical_declaration, line 51)
- `declaresPaths` (lexical_declaration, line 31)
- `namesIn` (lexical_declaration, line 39)
- `findUnresolved` (lexical_declaration, line 70)
- `isUnresolved` (lexical_declaration, line 56)
- `create` (method_definition, line 78, exported)
- `ANCHOR_SUFFIX` (lexical_declaration, line 26)
- `SELF` (lexical_declaration, line 27)
- `DECLARING_ROOTS` (lexical_declaration, line 29)
- `text` (lexical_declaration, line 52)
- `hints` (lexical_declaration, line 53)
- `joined` (lexical_declaration, line 54)
- `seen` (lexical_declaration, line 55)
- `shaped` (lexical_declaration, line 60)
- `index` (lexical_declaration, line 71)
- `UNRESOLVED` (lexical_declaration, line 75)
- `program` (method_definition, line 83, exported)
- `payload` (lexical_declaration, line 85, exported)

## Uses

- [shared/factories/listener.factory.ts](https://banes-lab.com/source/governance/shared/factories/listener.factory.ts.md)
- [shared/loaders/index.loader.ts](https://banes-lab.com/source/governance/shared/loaders/index.loader.ts.md)
- [shared/matchers/location.matcher.ts](https://banes-lab.com/source/governance/shared/matchers/location.matcher.ts.md)

## Records this file checks

- [Single Source of Truth](https://banes-lab.com/records/architecture/single-source-of-truth.md)

## Source

```typescript
import {
    BUILD_SCRIPT_ROOT,
    FOUNDATION_SUBJECT,
    RULE_HOST,
    SCRIPT_ROOT,
    normalizePath,
    projectFiles,
} from "../../shared/resolvers/anchor.resolver.ts";
import type { LocalRule, RuleContext, RuleListener } from "../../types/rule.types.ts";
import type { TreeIndex, UnresolvedDeclaration } from "../../types/location.types.ts";
import { buildTreeIndex, resolvesAgainst } from "../../shared/loaders/index.loader.ts";
import {
    hintLiteralsOf,
    isPathLike,
    isSegmentLike,
    joinedPathsOf,
    stringLiteralsOf,
    stripComments,
} from "../../shared/matchers/location.matcher.ts";
import { readFileSync, readdirSync } from "node:fs";
import { defineCheck } from "@govlab/context/check";
import { join } from "node:path";
import { listener } from "../../shared/factories/listener.factory.ts";
import { resolveFile } from "../../shared/matchers/filename.matcher.ts";

const ANCHOR_SUFFIX = `/${resolveFile(FOUNDATION_SUBJECT, "registry", projectFiles())}`;
const SELF = "closure-rule-declarations-resolve.eslint.rule.ts";

const DECLARING_ROOTS = [RULE_HOST, SCRIPT_ROOT, BUILD_SCRIPT_ROOT];

const declaresPaths = function declaresPaths(dir: string, name: string): boolean {
    if (dir !== RULE_HOST) {
        return true;
    }
    const text = readFileSync(join(dir, name), "utf8");
    return text.includes("meta:") && text.includes("create(");
};

const namesIn = function namesIn(dir: string): string[] {
    return readdirSync(dir);
};

const declaringFiles = function declaringFiles(): { dir: string; name: string }[] {
    return DECLARING_ROOTS.flatMap((dir) =>
        namesIn(dir)
            .filter((name) => name.endsWith(".ts") && name !== SELF && declaresPaths(dir, name))
            .map((name) => ({ dir, name })),
    );
};

const unresolvedIn = function unresolvedIn(index: TreeIndex, dir: string, file: string): UnresolvedDeclaration[] {
    const text = stripComments(readFileSync(join(dir, file), "utf8"));
    const hints = hintLiteralsOf(text);
    const joined = new Set(joinedPathsOf(text));
    const seen = new Set<string>();
    const isUnresolved = function isUnresolved(value: string): boolean {
        if (seen.has(value) || hints.has(value)) {
            return false;
        }
        const shaped = joined.has(value) ? isSegmentLike(value) : isPathLike(value);
        if (!shaped || resolvesAgainst(index, value)) {
            return false;
        }
        seen.add(value);
        return true;
    };
    return [...stringLiteralsOf(text), ...joined].filter(isUnresolved).map((value) => ({ file, value }));
};

const findUnresolved = function findUnresolved(): UnresolvedDeclaration[] {
    const index = buildTreeIndex();
    return declaringFiles().flatMap(({ dir, name }) => unresolvedIn(index, dir, name));
};

const UNRESOLVED = findUnresolved();

export default {
    create(context: RuleContext): RuleListener {
        if (!normalizePath(context.filename).endsWith(ANCHOR_SUFFIX)) {
            return {};
        }
        return listener({
            program(_view, node) {
                for (const entry of UNRESOLVED) {
                    const payload = { file: entry.file, value: entry.value };
                    context.report({ data: payload, messageId: "unresolved", node });
                }
            },
        });
    },
    meta: {
        docs: {
            checks: defineCheck({ detects: [], enforces: ["architecture:single-source-of-truth"] }),
            description:
                "Every path, folder fragment, and basename a lint rule or workspace script declares must resolve against the real tree, and every literal segment handed to a path join is treated as a path. Workspace locations belong in the paths SSOT (`@ssot/paths`); this rule covers the literals that never went through it. An absolute path under an operating-system root is a host location rather than a tree path and is outside the check. Anchored on the verify runner.",
        },
        messages: {
            unresolved:
                "`{{ file }}` declares `{{ value }}` but nothing in the tree matches it. Repoint it at the current path, or delete the declaration.",
        },
        schema: [],
        type: "problem",
    },
} satisfies LocalRule;
```
