# core/analyzers/reference.analyzer.ts

> 116 lines of code and 32 definitions.

Tree: GovLab Docs
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-analyzers-reference-analyzer-ts
Source text: https://banes-lab.com/source/docs/core/analyzers/reference.analyzer.ts.txt

Listed in [core/analyzers](https://banes-lab.com/api/source/docs/core/analyzers.md), after [core/analyzers/package.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/package.analyzer.ts.md) and before [core/analyzers/surface.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/surface.analyzer.ts.md).

## Definitions

- `extensionOf` (lexical_declaration, line 19)
- `firstSegment` (lexical_declaration, line 24)
- `existsBySuffix` (lexical_declaration, line 29)
- `existsFromAncestors` (lexical_declaration, line 35)
- `isCheckable` (lexical_declaration, line 56)
- `targetFound` (lexical_declaration, line 66)
- `lineFileRefs` (lexical_declaration, line 96)
- `tokenFound` (lexical_declaration, line 88)
- `isOutOfScope` (lexical_declaration, line 49)
- `brokenPaths` (lexical_declaration, line 81, exported)
- `frontmatterFileRefs` (lexical_declaration, line 112, exported)
- `brokenRef` (lexical_declaration, line 73)
- `FM_LINE_OFFSET` (lexical_declaration, line 9)
- `FENCE` (lexical_declaration, line 10)
- `FENCE_CLOSE` (lexical_declaration, line 11)
- `KEY_SEPARATOR` (lexical_declaration, line 12)
- `WILDCARD` (lexical_declaration, line 13)
- `HOME_MARK` (lexical_declaration, line 14)
- `ROOT_MARK` (lexical_declaration, line 15)
- `DEPENDENCY_DIR` (lexical_declaration, line 16)
- `FM_PROSE_FIELDS` (lexical_declaration, line 17)
- `dot` (lexical_declaration, line 20)
- `slash` (lexical_declaration, line 25)
- `suffix` (lexical_declaration, line 30)
- `candidates` (lexical_declaration, line 31)
- `dir` (lexical_declaration, line 36)
- `parent` (lexical_declaration, line 41)
- `target` (lexical_declaration, line 74)
- `broken` (lexical_declaration, line 83, exported)
- `colon` (lexical_declaration, line 102)
- `firstBreak` (lexical_declaration, line 120, exported)
- `end` (lexical_declaration, line 121, exported)

## Contained in

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

## Uses

- [core/parsers/location.parser.ts](https://banes-lab.com/source/docs/core/parsers/location.parser.ts.md)
- [core/parsers/metadata.parser.ts](https://banes-lab.com/source/docs/core/parsers/metadata.parser.ts.md)
- [core/predicates/reference.predicate.ts](https://banes-lab.com/source/docs/core/predicates/reference.predicate.ts.md)

## Used by

- [core/analyzers/document.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/document.analyzer.ts.md)

## Linked from

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

## Source

```typescript
import type { BrokenRef, PathRef, PathsCtx } from "#types/reference.types";
import { dirname, join } from "node:path";
import { existsSync } from "node:fs";
import { fileTokens } from "#core/parsers/metadata.parser";
import { isRelativeTarget } from "#core/predicates/reference.predicate";
import { normalizeTarget } from "#core/normalizers/reference.normalizer";
import { pathReferences } from "#core/parsers/location.parser";

const FM_LINE_OFFSET = 2;
const FENCE = "---";
const FENCE_CLOSE = "\n---";
const KEY_SEPARATOR = ":";
const WILDCARD = "*";
const HOME_MARK = "~";
const ROOT_MARK = "/";
const DEPENDENCY_DIR = "node_modules";
const FM_PROSE_FIELDS: ReadonlySet<string> = new Set(["description", "summary", "name", "title"]);

const extensionOf = function extensionOf(path: string): string {
    const dot = path.lastIndexOf(".");
    return dot === -1 ? "" : path.slice(dot);
};

const firstSegment = function firstSegment(path: string): string {
    const slash = path.indexOf("/");
    return slash === -1 ? path : path.slice(0, slash);
};

const existsBySuffix = function existsBySuffix(fileIndex: ReadonlyMap<string, string[]>, target: string): boolean {
    const suffix = `/${target}`;
    const candidates = fileIndex.get(target.slice(target.lastIndexOf("/") + 1)) ?? [];
    return candidates.some((relPath) => relPath === target || relPath.endsWith(suffix));
};

const existsFromAncestors = function existsFromAncestors(root: string, docPath: string, target: string): boolean {
    let dir = dirname(docPath);
    for (;;) {
        if (existsSync(join(dir, target))) {
            return true;
        }
        const parent = dirname(dir);
        if (dir === root || parent === dir) {
            return false;
        }
        dir = parent;
    }
};

const isOutOfScope = function isOutOfScope(context: PathsCtx, target: string): boolean {
    if (target === "" || target.startsWith(ROOT_MARK) || target.startsWith(HOME_MARK)) {
        return true;
    }
    return firstSegment(target) === DEPENDENCY_DIR || context.runtimeRoots.some((root) => target.startsWith(root));
};

const isCheckable = function isCheckable(context: PathsCtx, docPath: string, target: string): boolean {
    if (isOutOfScope(context, target)) {
        return false;
    }
    if (isRelativeTarget(target)) {
        return join(dirname(docPath), target).startsWith(context.root);
    }
    return context.codeExtensions.has(extensionOf(target)) || context.topLevel.has(firstSegment(target));
};

const targetFound = function targetFound(context: PathsCtx, docPath: string, target: string): boolean {
    if (isRelativeTarget(target)) {
        return existsSync(join(dirname(docPath), target));
    }
    return existsFromAncestors(context.root, docPath, target) || existsBySuffix(context.fileIndex, target);
};

const brokenRef = function brokenRef(context: PathsCtx, docPath: string, ref: PathRef): BrokenRef | null {
    const target = normalizeTarget(ref.path);
    if (!isCheckable(context, docPath, target) || targetFound(context, docPath, target)) {
        return null;
    }
    return { col: ref.col, line: ref.line, path: target };
};

export const brokenPaths = function brokenPaths(context: PathsCtx, docPath: string, source: string): BrokenRef[] {
    return pathReferences(source).flatMap((ref) => {
        const broken = brokenRef(context, docPath, ref);
        return broken === null ? [] : [broken];
    });
};

const tokenFound = function tokenFound(context: PathsCtx, docPath: string, token: string): boolean {
    return (
        existsSync(join(dirname(docPath), token)) ||
        existsBySuffix(context.fileIndex, token) ||
        existsFromAncestors(context.root, docPath, token)
    );
};

const lineFileRefs = function lineFileRefs(
    context: PathsCtx,
    docPath: string,
    line: string,
    index: number,
): BrokenRef[] {
    const colon = line.indexOf(KEY_SEPARATOR);
    if (colon !== -1 && FM_PROSE_FIELDS.has(line.slice(0, colon).trim())) {
        return [];
    }
    return fileTokens(line)
        .filter((token) => !token.includes(WILDCARD) && context.codeExtensions.has(extensionOf(token)))
        .filter((token) => !tokenFound(context, docPath, token))
        .map((token) => ({ col: 1, line: index + FM_LINE_OFFSET, path: token }));
};

export const frontmatterFileRefs = function frontmatterFileRefs(
    context: PathsCtx,
    docPath: string,
    source: string,
): BrokenRef[] {
    if (!source.startsWith(FENCE)) {
        return [];
    }
    const firstBreak = source.indexOf("\n");
    const end = firstBreak === -1 ? -1 : source.indexOf(FENCE_CLOSE, firstBreak);
    if (end === -1) {
        return [];
    }
    return source
        .slice(firstBreak + 1, end)
        .split("\n")
        .flatMap((line, index) => lineFileRefs(context, docPath, line, index));
};
```
