# core/loaders/heritage.loader.ts

> 43 lines of code and 16 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-loaders-heritage-loader-ts
Source text: https://banes-lab.com/source/build-scripts/core/loaders/heritage.loader.ts.txt

Listed in [core/loaders](https://banes-lab.com/api/source/build-scripts/core/loaders.md), after [core/loaders/graph.loader.ts](https://banes-lab.com/source/build-scripts/core/loaders/graph.loader.ts.md) and before [core/loaders/icons.loader.ts](https://banes-lab.com/source/build-scripts/core/loaders/icons.loader.ts.md).

## Definitions

- `chainFrom` (lexical_declaration, line 20)
- `isInside` (lexical_declaration, line 16)
- `inheritedFiles` (lexical_declaration, line 41, exported)
- `EXTENDS_KEY` (lexical_declaration, line 7)
- `RELATIVE_MARK` (lexical_declaration, line 8)
- `extendsOf` (lexical_declaration, line 10)
- `parsed` (lexical_declaration, line 11)
- `target` (lexical_declaration, line 21)
- `absolute` (lexical_declaration, line 25)
- `name` (lexical_declaration, line 30)
- `next` (lexical_declaration, line 31)
- `JSON_SUFFIX` (lexical_declaration, line 35)
- `isConfigFile` (lexical_declaration, line 37)
- `seen` (lexical_declaration, line 42, exported)
- `withInherited` (lexical_declaration, line 48, exported)
- `inherited` (lexical_declaration, line 49, exported)

## Contained in

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

## Linked from

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

## Source

```typescript
import type { DiskFile, DiskFolder } from "#types/structure.types";
import { basename, dirname, isAbsolute, resolve } from "node:path";
import { existsSync } from "node:fs";
import { isRecord } from "#core/selectors/base.selector";
import { readDiskFile } from "#core/loaders/structure.loader";

const EXTENDS_KEY = "extends";
const RELATIVE_MARK = ".";

const extendsOf = function extendsOf(text: string): string | null {
    const parsed: unknown = JSON.parse(text);
    const target = isRecord(parsed) ? parsed[EXTENDS_KEY] : null;
    return typeof target === "string" ? target : null;
};

const isInside = function isInside(dir: string, file: string): boolean {
    return file.startsWith(dir);
};

const chainFrom = function chainFrom(moduleDir: string, from: string, text: string, seen: Set<string>): DiskFile[] {
    const target = extendsOf(text) ?? "";
    if (!target.startsWith(RELATIVE_MARK)) {
        return [];
    }
    const absolute = isAbsolute(target) ? target : resolve(dirname(from), target);
    if (seen.has(absolute) || !existsSync(absolute) || isInside(moduleDir, absolute)) {
        return [];
    }
    seen.add(absolute);
    const name = basename(absolute);
    const next = readDiskFile(absolute, name, name, true);
    return [next, ...chainFrom(moduleDir, absolute, next.text, seen)];
};

const JSON_SUFFIX = ".json";

const isConfigFile = function isConfigFile(file: DiskFile): boolean {
    return file.name.endsWith(JSON_SUFFIX);
};

export const inheritedFiles = function inheritedFiles(moduleDir: string, tree: DiskFolder): DiskFile[] {
    const seen = new Set<string>();
    return tree.files
        .filter(isConfigFile)
        .flatMap((file) => chainFrom(moduleDir, resolve(moduleDir, file.path), file.text, seen));
};

export const withInherited = function withInherited(moduleDir: string, tree: DiskFolder): DiskFolder {
    const inherited = inheritedFiles(moduleDir, tree);
    return inherited.length === 0 ? tree : { ...tree, files: [...tree.files, ...inherited] };
};
```
