# core/analyzers/barrel.analyzer.ts

> 71 lines of code and 19 definitions.

Tree: Project Scripts
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/scripts#file-scripts-core-analyzers-barrel-analyzer-ts
Source text: https://banes-lab.com/source/scripts/core/analyzers/barrel.analyzer.ts.txt

Listed in [core/analyzers](https://banes-lab.com/api/source/scripts/core/analyzers.md), before [core/analyzers/closure.analyzer.ts](https://banes-lab.com/source/scripts/core/analyzers/closure.analyzer.ts.md).

## Definitions

- `collectGlobEdges` (lexical_declaration, line 70, exported)
- `visit` (lexical_declaration, line 23)
- `globArgumentsOf` (lexical_declaration, line 21)
- `resolvePattern` (lexical_declaration, line 34)
- `SEPARATOR` (lexical_declaration, line 10)
- `HERE` (lexical_declaration, line 11)
- `patternOf` (lexical_declaration, line 13)
- `argument` (lexical_declaration, line 17)
- `found` (lexical_declaration, line 22)
- `own` (lexical_declaration, line 24)
- `segments` (lexical_declaration, line 42)
- `matches` (lexical_declaration, line 43)
- `resolveFile` (lexical_declaration, line 56)
- `text` (lexical_declaration, line 57)
- `sourceFile` (lexical_declaration, line 61)
- `barrelDir` (lexical_declaration, line 62)
- `barrelRel` (lexical_declaration, line 63)
- `resolved` (lexical_declaration, line 71, exported)
- `unmatched` (lexical_declaration, line 72, exported)

## Contained in

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

## Uses

- [configuration/strings/closure.strings.ts](https://banes-lab.com/source/scripts/configuration/strings/closure.strings.ts.md)

## Used by

- [core/coordinators/closure.coordinator.ts](https://banes-lab.com/source/scripts/core/coordinators/closure.coordinator.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/scripts/folder-scripts-configuration-strings.md)
- [core/analyzers](https://banes-lab.com/anatomy/scripts/folder-scripts-core-analyzers.md)
- [core/coordinators](https://banes-lab.com/anatomy/scripts/folder-scripts-core-coordinators.md)

## Source

```typescript
import { GLOB_CALLEE, RELATIVE_MARK } from "#configuration/constants/closure.constants";
import type { PatternResolution, SideEffectEntry } from "#types/closure.types";
import { dirname, relative } from "node:path";
import { unmatchedBarrels, unmatchedPattern } from "#configuration/strings/closure.strings";
import { expandPattern } from "#core/resolvers/pattern.resolver";
import { normalizePath } from "@ssot/govlab/shared/resolvers/anchor.resolver.ts";
import { readFileSync } from "node:fs";
import ts from "typescript";

const SEPARATOR = "/";
const HERE = "./";

const patternOf = function patternOf(node: ts.Node, sourceFile: ts.SourceFile): string | null {
    if (!ts.isCallExpression(node) || node.expression.getText(sourceFile) !== GLOB_CALLEE) {
        return null;
    }
    const argument = node.arguments.at(0);
    return argument !== undefined && ts.isStringLiteral(argument) ? argument.text : null;
};

const globArgumentsOf = function globArgumentsOf(node: ts.Node, sourceFile: ts.SourceFile): string[] {
    const found: string[] = [];
    const visit = function visit(child: ts.Node): void {
        const own = patternOf(child, sourceFile);
        if (own !== null) {
            found.push(own);
        }
        ts.forEachChild(child, visit);
    };
    visit(node);
    return found;
};

const resolvePattern = function resolvePattern(
    pattern: string,
    barrelDir: string,
    barrelRel: string,
): PatternResolution {
    if (!pattern.startsWith(RELATIVE_MARK)) {
        return { edges: [], unmatched: [] };
    }
    const segments = pattern.split(SEPARATOR).filter((segment) => segment !== RELATIVE_MARK && segment.length > 0);
    const matches = expandPattern(barrelDir, segments);
    if (matches.length === 0) {
        return { edges: [], unmatched: [unmatchedPattern(barrelRel, pattern)] };
    }
    return {
        edges: matches.map((match) => ({
            file: barrelRel,
            from: `${HERE}${normalizePath(relative(barrelDir, match))}`,
        })),
        unmatched: [],
    };
};

const resolveFile = function resolveFile(filePath: string, root: string): PatternResolution {
    const text = readFileSync(filePath, "utf8");
    if (!text.includes(GLOB_CALLEE)) {
        return { edges: [], unmatched: [] };
    }
    const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true);
    const barrelDir = dirname(filePath);
    const barrelRel = normalizePath(relative(root, filePath));
    const resolved = globArgumentsOf(sourceFile, sourceFile).map((pattern) =>
        resolvePattern(pattern, barrelDir, barrelRel),
    );
    return { edges: resolved.flatMap((entry) => entry.edges), unmatched: resolved.flatMap((entry) => entry.unmatched) };
};

export const collectGlobEdges = function collectGlobEdges(root: string, files: readonly string[]): SideEffectEntry[] {
    const resolved = files.map((filePath) => resolveFile(filePath, root));
    const unmatched = resolved.flatMap((entry) => entry.unmatched);
    if (unmatched.length > 0) {
        throw new Error(unmatchedBarrels(unmatched));
    }
    return resolved.flatMap((entry) => entry.edges);
};
```
