# core/persistence/tree.persistence.ts

> 44 lines of code and 10 definitions.

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

Listed in [core/persistence](https://banes-lab.com/api/source/build-scripts/core/persistence.md), after [core/persistence/text.persistence.ts](https://banes-lab.com/source/build-scripts/core/persistence/text.persistence.ts.md).

## Definitions

- `persistTreeDeclarations` (lexical_declaration, line 14, exported)
- `publishTree` (lexical_declaration, line 32, exported)
- `FOLDER_SEPARATOR` (lexical_declaration, line 12)
- `foldersOf` (lexical_declaration, line 20)
- `end` (lexical_declaration, line 23)
- `files` (lexical_declaration, line 37, exported)
- `folders` (lexical_declaration, line 38, exported)
- `sync` (lexical_declaration, line 39, exported)
- `path` (lexical_declaration, line 40, exported)
- `missing` (lexical_declaration, line 43, exported)

## Contained in

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

## Uses

- [configuration/strings/anatomy.strings.ts](https://banes-lab.com/source/build-scripts/configuration/strings/anatomy.strings.ts.md)
- [core/formatters/tree.formatter.ts](https://banes-lab.com/source/build-scripts/core/formatters/tree.formatter.ts.md)

## Used by

- [core/coordinators/anatomy.coordinator.ts](https://banes-lab.com/source/build-scripts/core/coordinators/anatomy.coordinator.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-configuration-strings.md)
- [core/coordinators](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-coordinators.md)
- [core/formatters](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-formatters.md)

## Source

```typescript
import { join, relative } from "node:path";
import type { AnatomyTreeDeclaration } from "@banes-lab/web/types/anatomy.types.js";
import type { FolderSync } from "#types/folder.types";
import { absolutePath } from "@ssot/paths";
import { existsSync } from "node:fs";
import { renderTreeDeclarations } from "#core/formatters/tree.formatter";
import { syncFolder } from "#core/persistence/folder.persistence";
import { toPosix } from "#core/resolvers/asset.resolver";
import { unpublishedFiles } from "#configuration/strings/anatomy.strings";
import { writeCanonicalText } from "@govlab/canonical-write";

const FOLDER_SEPARATOR = "/";

export const persistTreeDeclarations = async function persistTreeDeclarations(
    declarations: readonly AnatomyTreeDeclaration[],
): Promise<void> {
    await writeCanonicalText(absolutePath("app.anatomyTrees"), renderTreeDeclarations(declarations));
};

const foldersOf = function foldersOf(paths: readonly string[]): ReadonlySet<string> {
    const folders = new Set<string>();
    for (const path of paths) {
        let end = path.lastIndexOf(FOLDER_SEPARATOR);
        while (end > 0) {
            folders.add(path.slice(0, end));
            end = path.lastIndexOf(FOLDER_SEPARATOR, end - 1);
        }
    }
    return folders;
};

export const publishTree = async function publishTree(
    from: string,
    to: string,
    paths: readonly string[],
): Promise<FolderSync> {
    const files = new Set(paths);
    const folders = foldersOf(paths);
    const sync = await syncFolder(from, to, (source) => {
        const path = toPosix(relative(from, source));
        return files.has(path) || folders.has(path);
    });
    const missing = paths.filter((path) => !existsSync(join(to, path)));
    if (missing.length > 0) {
        throw new Error(unpublishedFiles(to, missing));
    }
    return sync;
};
```
