# shared/analyzers/taxonomy.analyzer.ts

> 115 lines of code and 20 definitions.

Tree: Governance tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-analyzers-taxonomy-analyzer-ts
Source text: https://banes-lab.com/source/governance/shared/analyzers/taxonomy.analyzer.ts.txt

Listed in [shared/analyzers](https://banes-lab.com/api/source/governance/shared/analyzers.md), after [shared/analyzers/specifier.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/specifier.analyzer.ts.md) and before [shared/analyzers/taxonomy.coverage.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/taxonomy.coverage.analyzer.ts.md).

## Definitions

- `testFinding` (lexical_declaration, line 79, exported)
- `namingFinding` (lexical_declaration, line 97, exported)
- `mirroredFinding` (lexical_declaration, line 60)
- `placementFinding` (lexical_declaration, line 37, exported)
- `markerFinding` (lexical_declaration, line 23)
- `PATH_SEPARATOR` (lexical_declaration, line 21)
- `expected` (lexical_declaration, line 28)
- `parent` (lexical_declaration, line 29)
- `path` (lexical_declaration, line 30)
- `misplaced` (lexical_declaration, line 42, exported)
- `subjectNameOf` (lexical_declaration, line 55)
- `segments` (lexical_declaration, line 56)
- `detail` (lexical_declaration, line 66, exported)
- `subject` (lexical_declaration, line 73)
- `marker` (lexical_declaration, line 84, exported)
- `source` (lexical_declaration, line 85, exported)
- `parsed` (lexical_declaration, line 105, exported)
- `container` (lexical_declaration, line 112, exported)
- `folder` (lexical_declaration, line 113, exported)
- `folderTags` (lexical_declaration, line 114, exported)

## Uses

- [shared/manifests/taxonomy.manifest.ts](https://banes-lab.com/source/governance/shared/manifests/taxonomy.manifest.ts.md)
- [shared/matchers/filename.matcher.ts](https://banes-lab.com/source/governance/shared/matchers/filename.matcher.ts.md)

## Used by

- [shared/analyzers/taxonomy.tree.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/taxonomy.tree.analyzer.ts.md)

## Source

```typescript
import { TAG_ALTERNATIVE, TAG_LIST } from "../strings/taxonomy.strings.ts";
import {
    fixtureMarkerOf,
    isMarkerFolder,
    isNameExempt,
    isSpecialContainer,
    markerFolderOf,
    mirrorSourceOf,
    tagsForFolder,
    testMarkerOf,
    testMarkers,
    vocabularyFor,
} from "../manifests/taxonomy.manifest.ts";
import { isExempt, isParsed, parseFilename } from "../matchers/filename.matcher.ts";
import { WORKSPACE_ROOT } from "../resolvers/anchor.resolver.ts";
import { existsSync } from "node:fs";
import { join } from "node:path";
import type { PlacementFinding } from "../../types/taxonomy.types.ts";
import { folderPathError } from "../matchers/folder.matcher.ts";

const PATH_SEPARATOR = "/";

const markerFinding = function markerFinding(
    basename: string,
    folders: readonly string[],
    root: string,
): PlacementFinding | null {
    const expected = markerFolderOf(basename, root);
    const parent = folders.at(-1) ?? "";
    const path = [root, ...folders].join(PATH_SEPARATOR);
    if (expected === undefined) {
        return isMarkerFolder(parent, root) ? { data: { basename, path }, messageId: "markerFolderIntruder" } : null;
    }
    return parent === expected ? null : { data: { basename, expected, path }, messageId: "markerMisplaced" };
};

export const placementFinding = function placementFinding(
    basename: string,
    folders: readonly string[],
    root: string,
): PlacementFinding | null {
    const misplaced = markerFinding(basename, folders, root);
    if (misplaced !== null) {
        return misplaced;
    }
    if (markerFolderOf(basename, root) === undefined && isNameExempt(basename, root)) {
        return null;
    }
    const detail = folderPathError(root, folders);
    return detail === undefined
        ? null
        : { data: { detail, path: folders.join(PATH_SEPARATOR) }, messageId: "badShape" };
};

const subjectNameOf = function subjectNameOf(basename: string, separator: string): string {
    const segments = basename.split(separator);
    return [...segments.slice(0, -2), ...segments.slice(-1)].join(separator);
};

const mirroredFinding = function mirroredFinding(
    basename: string,
    folders: readonly string[],
    root: string,
    source: string,
): PlacementFinding | null {
    const detail = folderPathError(root, folders);
    if (detail !== undefined) {
        return { data: { detail, path: folders.join(PATH_SEPARATOR) }, messageId: "badShape" };
    }
    if (testMarkerOf(basename) === undefined) {
        return null;
    }
    const subject = subjectNameOf(basename, vocabularyFor(root).separator);
    return existsSync(join(WORKSPACE_ROOT, source, ...folders, subject))
        ? null
        : { data: { basename, source, subject }, messageId: "unmirroredTest" };
};

export const testFinding = function testFinding(
    basename: string,
    folders: readonly string[],
    root: string,
): PlacementFinding | null {
    const marker = testMarkerOf(basename) ?? fixtureMarkerOf(basename, root);
    const source = mirrorSourceOf(root);
    if (source === undefined) {
        return marker === undefined ? null : { data: { basename, marker }, messageId: "misplacedTest" };
    }
    if (marker === undefined) {
        return isNameExempt(basename, root)
            ? null
            : { data: { basename, markers: testMarkers().join(TAG_LIST) }, messageId: "nonTestInMirror" };
    }
    return mirroredFinding(basename, folders, root, source);
};

export const namingFinding = function namingFinding(
    basename: string,
    folders: readonly string[],
    root: string,
): PlacementFinding | null {
    if (isNameExempt(basename, root)) {
        return null;
    }
    const parsed = parseFilename(basename, root);
    if (isExempt(parsed)) {
        return null;
    }
    if (!isParsed(parsed)) {
        return { data: { basename, reason: parsed.reason, word: parsed.word }, messageId: "unparsable" };
    }
    const container = folders[0] ?? "";
    const folder = isSpecialContainer(root, container) ? container : (folders.at(-1) ?? "");
    const folderTags = tagsForFolder(folder, root);
    if (folderTags.length === 0 || folderTags.includes(parsed.concern)) {
        return null;
    }
    return {
        data: { basename, folder, folderTag: folderTags.join(TAG_ALTERNATIVE), tag: parsed.concern },
        messageId: "concernMismatch",
    };
};
```
