# core/validators/manifest.prose.validator.ts

> 121 lines of code and 17 definitions.

Tree: GovLab Docs
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-validators-manifest-prose-validator-ts
Source text: https://banes-lab.com/source/docs/core/validators/manifest.prose.validator.ts.txt

Listed in [core/validators](https://banes-lab.com/api/source/docs/core/validators.md), after [core/validators/location.validator.ts](https://banes-lab.com/source/docs/core/validators/location.validator.ts.md) and before [core/validators/manifest.validator.ts](https://banes-lab.com/source/docs/core/validators/manifest.validator.ts.md).

## Definitions

- `pathFindings` (lexical_declaration, line 46)
- `textFindings` (lexical_declaration, line 39)
- `brokenPathsIn` (lexical_declaration, line 23, exported)
- `sectionFindings` (lexical_declaration, line 80)
- `governManifest` (lexical_declaration, line 59, exported)
- `docFindings` (lexical_declaration, line 109)
- `governDeclaredDocs` (lexical_declaration, line 125, exported)
- `hostCouplingIn` (lexical_declaration, line 33, exported)
- `safeDocLocation` (lexical_declaration, line 93)
- `{ docs }` (lexical_declaration, line 64, exported)
- `coupled` (lexical_declaration, line 70, exported)
- `fragment` (lexical_declaration, line 85, exported)
- `docDirOf` (lexical_declaration, line 104)
- `location` (lexical_declaration, line 105)
- `docDir` (lexical_declaration, line 110)
- `lead` (lexical_declaration, line 114)
- `{ documents }` (lexical_declaration, line 129, exported)

## Contained in

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

## Uses

- [configuration/strings/governance.strings.ts](https://banes-lab.com/source/docs/configuration/strings/governance.strings.ts.md)
- [configuration/strings/reference.strings.ts](https://banes-lab.com/source/docs/configuration/strings/reference.strings.ts.md)
- [core/analyzers/drift.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/drift.analyzer.ts.md)
- [core/analyzers/markdown.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/markdown.analyzer.ts.md)
- [core/analyzers/text.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/text.analyzer.ts.md)
- [core/analyzers/vocabulary.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/vocabulary.analyzer.ts.md)
- [core/normalizers/reference.normalizer.ts](https://banes-lab.com/source/docs/core/normalizers/reference.normalizer.ts.md)
- [core/parsers/location.parser.ts](https://banes-lab.com/source/docs/core/parsers/location.parser.ts.md)
- [core/predicates/reference.predicate.ts](https://banes-lab.com/source/docs/core/predicates/reference.predicate.ts.md)
- [core/resolvers/location.resolver.ts](https://banes-lab.com/source/docs/core/resolvers/location.resolver.ts.md)
- [core/resolvers/source.resolver.ts](https://banes-lab.com/source/docs/core/resolvers/source.resolver.ts.md)

## Used by

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

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/docs/folder-docs-configuration-strings.md)
- [core/analyzers](https://banes-lab.com/anatomy/docs/folder-docs-core-analyzers.md)
- [core/factories](https://banes-lab.com/anatomy/docs/folder-docs-core-factories.md)
- [core/normalizers](https://banes-lab.com/anatomy/docs/folder-docs-core-normalizers.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)
- [core/resolvers](https://banes-lab.com/anatomy/docs/folder-docs-core-resolvers.md)
- [core/validators](https://banes-lab.com/anatomy/docs/folder-docs-core-validators.md)

## Source

```typescript
import type {
    DeclaredDocFinding,
    DeclaredGovernContext,
    DiscoveredModule,
    ManifestFinding,
    ProseFinding,
} from "#types/readme.types";
import { LEAD_HEADING, hardcodedCount, historySmell } from "#configuration/strings/governance.strings";
import { cleanTarget, unquote } from "#core/normalizers/reference.normalizer";
import { dirname, resolve } from "node:path";
import { hostCoupling, missingTarget, symbolDrift } from "#configuration/strings/reference.strings";
import { renderFieldFragment, renderRenderable } from "#core/formatters/markdown.formatter";
import type { DocumentDecl } from "#types/document.types";
import { bannedLanguage } from "#core/analyzers/vocabulary.analyzer";
import { conventionHits } from "#core/analyzers/markdown.analyzer";
import { declaredDocLocation } from "#core/resolvers/location.resolver";
import { hardcodedCounts } from "#core/analyzers/text.analyzer";
import { isExternalTarget } from "#core/predicates/reference.predicate";
import { pathReferences } from "#core/parsers/location.parser";
import { symbolDriftIn } from "#core/analyzers/drift.analyzer";
import { targetExists } from "#core/resolvers/source.resolver";

export const brokenPathsIn = function brokenPathsIn(
    fragment: string,
    moduleDir: string,
    consumerRoot: string,
): string[] {
    return pathReferences(fragment)
        .map((ref) => cleanTarget(ref.path))
        .filter((target) => !isExternalTarget(target) && !targetExists(target, moduleDir, consumerRoot));
};

export const hostCouplingIn = function hostCouplingIn(fragment: string, hostTokens: readonly string[]): string[] {
    return pathReferences(fragment)
        .map((ref) => unquote(ref.path))
        .filter((target) => hostTokens.some((token) => target.startsWith(token)));
};

const textFindings = function textFindings(fragment: string): ProseFinding[] {
    return [
        ...bannedLanguage(fragment).map((hit) => ({ axis: "history-smell", detail: historySmell(hit.term) })),
        ...conventionHits(fragment).map((hit) => ({ axis: hit.code, detail: hit.token })),
    ];
};

const pathFindings = function pathFindings(fragment: string, dir: string, consumerRoot: string): ProseFinding[] {
    return [
        ...brokenPathsIn(fragment, dir, consumerRoot).map((target) => ({
            axis: "broken-path",
            detail: missingTarget(target),
        })),
        ...symbolDriftIn(fragment, dir, consumerRoot).map((detail) => ({
            axis: "symbol-drift",
            detail: symbolDrift(detail),
        })),
    ];
};

export const governManifest = function governManifest(
    module: DiscoveredModule,
    consumerRoot: string,
    hostTokens: readonly string[],
): ManifestFinding[] {
    const { docs } = module.manifest;
    if (!docs || typeof docs !== "object") {
        return [];
    }
    return Object.entries(docs).flatMap(([key, value]) => {
        const fragment = renderFieldFragment(key, value);
        const coupled = hostCouplingIn(fragment, hostTokens).map((target) => ({
            axis: "host-coupling",
            detail: hostCoupling(target),
        }));
        return [...textFindings(fragment), ...pathFindings(fragment, module.dir, consumerRoot), ...coupled].map(
            (finding): ManifestFinding => ({ ...finding, field: key }),
        );
    });
};

const sectionFindings = function sectionFindings(
    docDir: string,
    consumerRoot: string,
    content: unknown,
): ProseFinding[] {
    const fragment = renderRenderable(content);
    return [
        ...textFindings(fragment),
        ...pathFindings(fragment, docDir, consumerRoot),
        ...hardcodedCounts(fragment).map((count) => ({ axis: "magic-number", detail: hardcodedCount(count) })),
    ];
};

const safeDocLocation = function safeDocLocation(doc: DocumentDecl, context: DeclaredGovernContext): string | null {
    try {
        return declaredDocLocation(doc, context.registries, context.options);
    } catch (error) {
        if (!(error instanceof Error)) {
            throw error;
        }
        return null;
    }
};

const docDirOf = function docDirOf(doc: DocumentDecl, context: DeclaredGovernContext): string | null {
    const location = safeDocLocation(doc, context);
    return location === null ? null : dirname(resolve(context.consumerRoot, location));
};

const docFindings = function docFindings(doc: DocumentDecl, context: DeclaredGovernContext): DeclaredDocFinding[] {
    const docDir = Array.isArray(doc.body) ? docDirOf(doc, context) : null;
    if (docDir === null) {
        return [];
    }
    const lead =
        typeof doc.lead === "string" || Array.isArray(doc.lead) ? [{ content: doc.lead, heading: LEAD_HEADING }] : [];
    return [...lead, ...doc.body].flatMap((section) =>
        sectionFindings(docDir, context.consumerRoot, section.content).map((finding): DeclaredDocFinding => ({
            ...finding,
            doc: doc.name,
            heading: section.heading,
        })),
    );
};

export const governDeclaredDocs = function governDeclaredDocs(
    module: DiscoveredModule,
    context: DeclaredGovernContext,
): DeclaredDocFinding[] {
    const { documents } = module.manifest;
    return Array.isArray(documents) ? documents.flatMap((doc) => docFindings(doc, context)) : [];
};
```
