# core/loaders/manifest.loader.ts

> 109 lines of code and 31 definitions.

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

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

## Definitions

- `discoverManifests` (lexical_declaration, line 75, exported)
- `declaredEntries` (lexical_declaration, line 108, exported)
- `recordAt` (lexical_declaration, line 26)
- `walk` (lexical_declaration, line 83, exported)
- `discoverModules` (lexical_declaration, line 104, exported)
- `isExcluded` (lexical_declaration, line 13)
- `isShaped` (lexical_declaration, line 80, exported)
- `ecosystemOf` (lexical_declaration, line 99)
- `moduleAt` (lexical_declaration, line 53)
- `manifestBarrels` (lexical_declaration, line 114, exported)
- `unresolvedEntryPatterns` (lexical_declaration, line 118, exported)
- `isDocsManifest` (lexical_declaration, line 18)
- `readManifest` (lexical_declaration, line 31, exported)
- `readPackageJson` (lexical_declaration, line 39, exported)
- `DEFAULT_DOT_ALLOW` (lexical_declaration, line 14)
- `ROOT_REL` (lexical_declaration, line 15)
- `ENTRIES_KEY` (lexical_declaration, line 16)
- `parsed` (lexical_declaration, line 27)
- `isTextEntry` (lexical_declaration, line 35)
- `record` (lexical_declaration, line 40, exported)
- `{ dependencies, exports, main, name, types }` (lexical_declaration, line 41, exported)
- `rel` (lexical_declaration, line 62)
- `parts` (lexical_declaration, line 63)
- `allowDot` (lexical_declaration, line 79, exported)
- `rootAbs` (lexical_declaration, line 81, exported)
- `modules` (lexical_declaration, line 82, exported)
- `module` (lexical_declaration, line 84, exported)
- `child` (lexical_declaration, line 89, exported)
- `marker` (lexical_declaration, line 100)
- `manifest` (lexical_declaration, line 109, exported)
- `entries` (lexical_declaration, line 110, exported)

## Contained in

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

## Uses

- [core/loaders/base.loader.ts](https://banes-lab.com/source/docs/core/loaders/base.loader.ts.md)
- [core/matchers/segment.matcher.ts](https://banes-lab.com/source/docs/core/matchers/segment.matcher.ts.md)
- [core/predicates/record.predicate.ts](https://banes-lab.com/source/docs/core/predicates/record.predicate.ts.md)

## Used by

- [core/factories/environment.factory.ts](https://banes-lab.com/source/docs/core/factories/environment.factory.ts.md)
- [core/registries/manifest.registry.ts](https://banes-lab.com/source/docs/core/registries/manifest.registry.ts.md)
- [core/validators/barrel.validator.ts](https://banes-lab.com/source/docs/core/validators/barrel.validator.ts.md)
- [runtime/entrypoints/package.entrypoint.ts](https://banes-lab.com/source/docs/runtime/entrypoints/package.entrypoint.ts.md)

## Linked from

- [core/factories](https://banes-lab.com/anatomy/docs/folder-docs-core-factories.md)
- [core/loaders](https://banes-lab.com/anatomy/docs/folder-docs-core-loaders.md)
- [core/matchers](https://banes-lab.com/anatomy/docs/folder-docs-core-matchers.md)
- [core/predicates](https://banes-lab.com/anatomy/docs/folder-docs-core-predicates.md)
- [core/registries](https://banes-lab.com/anatomy/docs/folder-docs-core-registries.md)
- [core/validators](https://banes-lab.com/anatomy/docs/folder-docs-core-validators.md)
- [runtime/entrypoints](https://banes-lab.com/anatomy/docs/folder-docs-runtime-entrypoints.md)

## Source

```typescript
import { DEFAULT_ECOSYSTEM, ECOSYSTEM_MARKERS } from "#configuration/constants/manifest.constants";
import type { DiscoverOptions, ManifestModule } from "#types/manifest.types";
import { MANIFEST_FILE, PACKAGE_FILE } from "#configuration/constants/document.constants";
import type { Manifest, PackageJson } from "#types/readme.types";
import { basename, join, relative, resolve, sep } from "node:path";
import { isPlainRecord, isRecord } from "#core/predicates/record.predicate";
import { readDirSafe, readJsonSafe } from "#core/loaders/base.loader";
import { ROOT } from "@ssot/paths";
import { excludeMatcher } from "@govlab/quality/config";
import { existsSync } from "node:fs";
import { expandGlob } from "#core/matchers/segment.matcher";

const isExcluded = await excludeMatcher(ROOT);
const DEFAULT_DOT_ALLOW: ReadonlySet<string> = new Set<string>();
const ROOT_REL = ".";
const ENTRIES_KEY = "entries";

const isDocsManifest = function isDocsManifest(manifest: Manifest): boolean {
    return (
        typeof manifest["label"] === "string" &&
        typeof manifest["maturity"] === "string" &&
        isRecord(manifest["visibility"])
    );
};

const recordAt = function recordAt(path: string): Manifest | null {
    const parsed = readJsonSafe(path);
    return isPlainRecord(parsed) ? parsed : null;
};

export const readManifest = function readManifest(moduleDir: string): Manifest {
    return recordAt(resolve(moduleDir, MANIFEST_FILE)) ?? {};
};

const isTextEntry = function isTextEntry(entry: [string, unknown]): entry is [string, string] {
    return typeof entry[1] === "string";
};

export const readPackageJson = function readPackageJson(moduleDir: string): PackageJson {
    const record = recordAt(resolve(moduleDir, PACKAGE_FILE)) ?? {};
    const { dependencies, exports, main, name, types } = record;
    return {
        ...(typeof name === "string" ? { name } : {}),
        ...(typeof main === "string" ? { main } : {}),
        ...(typeof types === "string" ? { types } : {}),
        ...(exports === undefined ? {} : { exports }),
        ...(isPlainRecord(dependencies)
            ? { dependencies: Object.fromEntries(Object.entries(dependencies).filter(isTextEntry)) }
            : {}),
    };
};

const moduleAt = function moduleAt(
    rootAbs: string,
    dir: string,
    isShaped: (manifest: Manifest) => boolean,
): ManifestModule | null {
    const manifest = recordAt(join(dir, MANIFEST_FILE));
    if (manifest === null || !isShaped(manifest)) {
        return null;
    }
    const rel = relative(rootAbs, dir);
    const parts = rel.length > 0 ? rel.split(sep) : [];
    return {
        dir,
        group: parts[0] ?? basename(dir),
        label: parts.length > 0 ? parts.join("/") : basename(dir),
        manifest,
        pkg: recordAt(join(dir, PACKAGE_FILE)) ?? {},
        relPath: parts.length > 0 ? parts.join("/") : ROOT_REL,
        slug: basename(dir),
    };
};

export const discoverManifests = function discoverManifests(
    root: string,
    options: DiscoverOptions = {},
): ManifestModule[] {
    const allowDot = options.allowDot ?? DEFAULT_DOT_ALLOW;
    const isShaped = options.isShaped ?? isDocsManifest;
    const rootAbs = resolve(root);
    const modules: ManifestModule[] = [];
    const walk = (dir: string): void => {
        const module = moduleAt(rootAbs, dir, isShaped);
        if (module !== null) {
            modules.push(module);
        }
        for (const entry of readDirSafe(dir)) {
            const child = join(dir, entry.name);
            if (entry.isDirectory() && (!isExcluded(child) || allowDot.has(entry.name))) {
                walk(child);
            }
        }
    };
    walk(rootAbs);
    return modules.toSorted((left, right) => left.slug.localeCompare(right.slug));
};

const ecosystemOf = function ecosystemOf(dir: string): string {
    const marker = ECOSYSTEM_MARKERS.find(([file]) => existsSync(join(dir, file)));
    return marker === undefined ? DEFAULT_ECOSYSTEM : marker[1];
};

export const discoverModules = function discoverModules(): ManifestModule[] {
    return discoverManifests(ROOT).map((module) => ({ ...module, ecosystemHint: ecosystemOf(module.dir) }));
};

export const declaredEntries = function declaredEntries(moduleDir: string): string[] | null {
    const manifest = recordAt(join(moduleDir, MANIFEST_FILE));
    const entries = manifest === null ? undefined : manifest[ENTRIES_KEY];
    return Array.isArray(entries) ? entries.filter((entry): entry is string => typeof entry === "string") : null;
};

export const manifestBarrels = function manifestBarrels(moduleDir: string): string[] {
    return (declaredEntries(moduleDir) ?? []).flatMap((pattern) => expandGlob(moduleDir, pattern.split("/")));
};

export const unresolvedEntryPatterns = function unresolvedEntryPatterns(moduleDir: string): string[] {
    return (declaredEntries(moduleDir) ?? []).filter(
        (pattern) => expandGlob(moduleDir, pattern.split("/")).length === 0,
    );
};
```
