# core/factories/document.factory.ts

> 96 lines of code and 18 definitions.

Tree: GovLab Docs
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-factories-document-factory-ts
Source text: https://banes-lab.com/source/docs/core/factories/document.factory.ts.txt

Listed in [core/factories](https://banes-lab.com/api/source/docs/core/factories.md), after [core/factories/diagram.factory.ts](https://banes-lab.com/source/docs/core/factories/diagram.factory.ts.md) and before [core/factories/document.output.factory.ts](https://banes-lab.com/source/docs/core/factories/document.output.factory.ts.md).

## Definitions

- `buildContext` (lexical_declaration, line 55)
- `repoMetricsFor` (lexical_declaration, line 43)
- `resolvePrinciples` (lexical_declaration, line 29)
- `resolveConcepts` (lexical_declaration, line 34)
- `derive` (lexical_declaration, line 48)
- `createModuleDocs` (lexical_declaration, line 76, exported)
- `REPO_METRICS_KEY` (lexical_declaration, line 27)
- `declared` (lexical_declaration, line 35)
- `ids` (lexical_declaration, line 36)
- `concept` (lexical_declaration, line 38)
- `manifest` (lexical_declaration, line 56)
- `pkg` (lexical_declaration, line 57)
- `name` (lexical_declaration, line 58)
- `{ archRelations, conceptMap, consumerRoot, hostTokens }` (lexical_declaration, line 77, exported)
- `registries` (lexical_declaration, line 78, exported)
- `locationOptions` (lexical_declaration, line 83, exported)
- `declaredContext` (lexical_declaration, line 84, exported)
- `contextDeps` (lexical_declaration, line 85, exported)

## Contained in

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

## Uses

- [core/analyzers/surface.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/surface.analyzer.ts.md)
- [core/formatters/readme.formatter.ts](https://banes-lab.com/source/docs/core/formatters/readme.formatter.ts.md)
- [core/resolvers/location.resolver.ts](https://banes-lab.com/source/docs/core/resolvers/location.resolver.ts.md)
- [core/validators/governance.validator.ts](https://banes-lab.com/source/docs/core/validators/governance.validator.ts.md)
- [core/validators/manifest.prose.validator.ts](https://banes-lab.com/source/docs/core/validators/manifest.prose.validator.ts.md)

## Linked from

- [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/formatters](https://banes-lab.com/anatomy/docs/folder-docs-core-formatters.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 {
    ArchPort,
    ConceptRecord,
    ContextDeps,
    Manifest,
    ModuleDocs,
    ModuleDocsOptions,
    PrincipleRecord,
    RenderContext,
    RepoMetrics,
} from "#types/readme.types";
import type { DocRegistries, LocationOptions } from "#types/location.types";
import { basename, resolve } from "node:path";
import { governConcepts, governPrinciples, ontologyDuplicateFindings } from "#core/validators/governance.validator";
import { governDeclaredDocs, governManifest } from "#core/validators/manifest.prose.validator";
import { readManifest, readPackageJson } from "#core/loaders/manifest.loader";
import { DEFAULT_ROOT_PREFIX } from "#configuration/constants/document.constants";
import { DOC_FORMS } from "#configuration/constants/form.constants";
import { PACKAGE_SCOPE } from "#configuration/constants/manifest.constants";
import { VCS_DIRECTORY } from "#configuration/constants/vcs.constants";
import { declaredDocLocation } from "#core/resolvers/location.resolver";
import { existsSync } from "node:fs";
import { generateModuleDoc } from "#core/formatters/readme.formatter";
import { readPublicSurface } from "#core/analyzers/surface.analyzer";
import { renderDeclaredDoc } from "#core/formatters/document.formatter";

const REPO_METRICS_KEY = "repoMetrics";

const resolvePrinciples = function resolvePrinciples(manifest: Manifest, archRelations: ArchPort): PrincipleRecord[] {
    const ids = manifest.governance?.principles;
    return Array.isArray(ids) && ids.length > 0 ? archRelations.resolve(ids).principles : [];
};

const resolveConcepts = function resolveConcepts(manifest: Manifest, slug: string, deps: ContextDeps): ConceptRecord[] {
    const declared = Array.isArray(manifest.governedBy) && manifest.governedBy.length > 0 ? manifest.governedBy : null;
    const ids = declared ?? deps.deriveGovernedBy(slug) ?? [];
    return ids.flatMap((id) => {
        const concept = deps.conceptMap.get(id);
        return concept === undefined ? [] : [{ dimension: concept.dimension, id: concept.id }];
    });
};

const repoMetricsFor = function repoMetricsFor(
    manifest: Manifest,
    moduleDir: string,
    deps: ContextDeps,
): RepoMetrics | null {
    const derive = deps.deriveRepoMetrics;
    if (derive === undefined || manifest[REPO_METRICS_KEY] !== true || !existsSync(resolve(moduleDir, VCS_DIRECTORY))) {
        return null;
    }
    return derive(moduleDir);
};

const buildContext = function buildContext(moduleDir: string, hasCharts: boolean, deps: ContextDeps): RenderContext {
    const manifest = readManifest(moduleDir);
    const pkg = readPackageJson(moduleDir);
    const name = basename(moduleDir);
    return {
        concepts: resolveConcepts(manifest, name, deps),
        docs: manifest.docs ?? {},
        domains: Array.isArray(manifest.domains) ? manifest.domains : [],
        hasCharts,
        maturity: typeof manifest["maturity"] === "string" ? manifest["maturity"] : "",
        moduleDir,
        name,
        pkg,
        principles: resolvePrinciples(manifest, deps.archRelations),
        repo: repoMetricsFor(manifest, moduleDir, deps),
        scoped: pkg.name ?? manifest.label ?? `${PACKAGE_SCOPE}${name}`,
        summary: manifest.summary ?? "",
        surface: readPublicSurface(moduleDir, pkg).surface,
    };
};

export const createModuleDocs = function createModuleDocs(options: ModuleDocsOptions): ModuleDocs {
    const { archRelations, conceptMap, consumerRoot, hostTokens } = options;
    const registries: DocRegistries = {
        concerns: options.docConcerns,
        forms: options.docForms ?? DOC_FORMS,
        owners: options.docMembers,
    };
    const locationOptions: LocationOptions = { rootPrefix: DEFAULT_ROOT_PREFIX };
    const declaredContext = { consumerRoot, hostTokens, options: locationOptions, registries };
    const contextDeps: ContextDeps = {
        archRelations,
        conceptMap,
        deriveGovernedBy: options.deriveGovernedBy,
        deriveRepoMetrics: options.deriveRepoMetrics,
    };
    return {
        declaredDocLocation: (doc) => declaredDocLocation(doc, registries, locationOptions),
        generateReadme: (moduleDir, hasCharts = false) =>
            generateModuleDoc(buildContext(moduleDir, hasCharts, contextDeps)),
        governConcepts: (manifest) => governConcepts(manifest, conceptMap),
        governDeclaredDocs: (module) => governDeclaredDocs(module, declaredContext),
        governManifest: (module) => governManifest(module, consumerRoot, hostTokens),
        governPrinciples: (manifest) => governPrinciples(manifest, archRelations),
        ontologyDuplicates: () => ontologyDuplicateFindings(archRelations),
        renderDeclaredDoc,
    };
};
```
