# shared/loaders/manifest.loader.ts

> 48 lines of code and 18 definitions.

Tree: Governance tree
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-loaders-manifest-loader-ts
Source text: https://banes-lab.com/source/governance/shared/loaders/manifest.loader.ts.txt

Listed in [shared/loaders](https://banes-lab.com/api/source/governance/shared/loaders.md), after [shared/loaders/index.loader.ts](https://banes-lab.com/source/governance/shared/loaders/index.loader.ts.md).

## Definitions

- `isManifestRecord` (lexical_declaration, line 11, exported)
- `memberDirs` (lexical_declaration, line 55, exported)
- `memberRels` (lexical_declaration, line 49, exported)
- `jsonRecordAt` (lexical_declaration, line 24, exported)
- `parsedAt` (lexical_declaration, line 15)
- `manifestAt` (lexical_declaration, line 32, exported)
- `MANIFEST` (lexical_declaration, line 6)
- `WILDCARD` (lexical_declaration, line 7)
- `WORKSPACE_POSIX` (lexical_declaration, line 9, exported)
- `text` (lexical_declaration, line 16)
- `parsed` (lexical_declaration, line 25, exported)
- `expand` (lexical_declaration, line 36)
- `star` (lexical_declaration, line 37)
- `cut` (lexical_declaration, line 41)
- `dir` (lexical_declaration, line 42)
- `prefix` (lexical_declaration, line 43)
- `{ workspaces }` (lexical_declaration, line 50, exported)
- `patterns` (lexical_declaration, line 51, exported)

## Uses

- [shared/strings/manifest.strings.ts](https://banes-lab.com/source/governance/shared/strings/manifest.strings.ts.md)

## Used by

- [codemods/analyzers/specifier.analyzer.ts](https://banes-lab.com/source/governance/codemods/analyzers/specifier.analyzer.ts.md)
- [rules/eslint/closure-no-cross-member-relative.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-cross-member-relative.eslint.rule.ts.md)
- [shared/analyzers/specifier.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/specifier.analyzer.ts.md)
- [shared/loaders/document.loader.ts](https://banes-lab.com/source/governance/shared/loaders/document.loader.ts.md)

## Source

```typescript
import { existsSync, readFileSync, readdirSync } from "node:fs";
import { notJsonObject, notValidJson } from "../strings/manifest.strings.ts";
import { ROOT } from "@ssot/paths";
import path from "node:path";

const MANIFEST = "package.json";
const WILDCARD = "*";

export const WORKSPACE_POSIX = ROOT.split(path.sep).join(path.posix.sep);

export const isManifestRecord = function isManifestRecord(value: unknown): value is Record<string, unknown> {
    return typeof value === "object" && value !== null && !Array.isArray(value);
};

const parsedAt = function parsedAt(location: string): unknown {
    const text = readFileSync(location, "utf8");
    try {
        return JSON.parse(text);
    } catch (error) {
        throw new Error(notValidJson(location), { cause: error });
    }
};

export const jsonRecordAt = function jsonRecordAt(location: string): Record<string, unknown> {
    const parsed = parsedAt(location);
    if (!isManifestRecord(parsed)) {
        throw new TypeError(notJsonObject(location));
    }
    return parsed;
};

export const manifestAt = function manifestAt(dir: string): Record<string, unknown> {
    return jsonRecordAt(path.join(dir, MANIFEST));
};

const expand = function expand(pattern: string): string[] {
    const star = pattern.indexOf(WILDCARD);
    if (star === -1) {
        return [pattern];
    }
    const cut = pattern.lastIndexOf("/", star);
    const dir = pattern.slice(0, cut);
    const prefix = pattern.slice(cut + 1, star);
    return readdirSync(path.join(ROOT, dir), { withFileTypes: true })
        .filter((entry) => entry.isDirectory() && entry.name.startsWith(prefix))
        .map((entry) => `${dir}/${entry.name}`);
};

export const memberRels = function memberRels(): readonly string[] {
    const { workspaces } = manifestAt(ROOT);
    const patterns = Array.isArray(workspaces) ? workspaces.filter((value) => typeof value === "string") : [];
    return patterns.flatMap(expand).filter((member) => existsSync(path.join(ROOT, member, MANIFEST)));
};

export const memberDirs = function memberDirs(): readonly string[] {
    return memberRels().map((member) => `${WORKSPACE_POSIX}/${member}`);
};
```
