core/converters/folder.converter.ts

core/converters/folder.converter.ts is a file in GovLab Patterns. 32 lines of code and 10 definitions.

import type { TreeGroup, TreeLeaf, TreeNode } from "#types/page.types";
import type { FileEntry } from "#types/package.types";

const PATH_SEP = "/";

const relParts = function relParts(rel: string, root: string): string[] {
    return (root === "" ? rel : rel.slice(root.length + 1)).split(PATH_SEP);
};

const leafOf = function leafOf(entry: FileEntry, rel: string): TreeLeaf {
    return {
        count: entry.walk.length,
        entry,
        kind: "file",
        name: relParts(entry.rel, rel)[0] ?? entry.rel,
        rel: entry.rel,
    };
};

export const buildGroup = function buildGroup(entries: readonly FileEntry[], rel: string, name: string): TreeGroup {
    const leaves = entries.filter((entry) => relParts(entry.rel, rel).length === 1).map((entry) => leafOf(entry, rel));
    const deeper = entries.filter((entry) => relParts(entry.rel, rel).length > 1);
    const dirNames = [...new Set(deeper.map((entry) => relParts(entry.rel, rel)[0] ?? ""))];
    const dirs = dirNames.map((dirName) =>
        buildGroup(
            deeper.filter((entry) => (relParts(entry.rel, rel)[0] ?? "") === dirName),
            rel === "" ? dirName : `${rel}${PATH_SEP}${dirName}`,
            dirName,
        ),
    );
    const children: TreeNode[] = [...leaves, ...dirs];
    return { children, count: children.reduce((sum, child) => sum + child.count, 0), kind: "dir", name, rel };
};

export const subtreeFiles = function subtreeFiles(node: TreeNode): FileEntry[] {
    return node.kind === "file" ? [node.entry] : node.children.flatMap(subtreeFiles);
};