You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
getcryst.al/lib/tree.ts

175 lines
3.7 KiB
TypeScript

import fm from "front-matter";
import { readdir, readFile, stat } from "fs/promises";
import { load } from "js-yaml";
import { join, resolve } from "path";
import { removeExt } from "./files";
export interface ITreeItem {
value: string;
current: boolean;
children: ITreeItem[];
weight: number;
pretty: string | null;
}
export class TreeItem {
value: string;
current: boolean;
children: TreeItem[] = [];
weight: number;
pretty: string | null;
contents: string | null = null;
constructor(
value = "root",
pretty: string | null = null,
weight = 0,
contents: string | null = null,
children: TreeItem[] = []
) {
this.value = value;
this.current = false;
this.pretty = pretty;
this.weight = weight;
this.contents = contents;
this.children = children;
}
async addEntry(dir: string, name: string) {
const contents = (await readFile(resolve(dir, name))).toString();
const {
attributes: { title, weight },
} = fm<FrontMatter>(contents);
this.addChild(
new TreeItem(
removeExt(name),
title ? title : null,
weight ? weight : 0
).setContents(contents)
);
}
async walk(dir: string) {
const dirents = await readdir(resolve(process.cwd(), dir), {
withFileTypes: true,
});
for (const dirent of dirents) {
if (dirent.name.startsWith(".")) continue;
if (dirent.isDirectory()) {
const resolvedDir = resolve(dir, dirent.name);
this.addChild(
await (
await new TreeItem(removeExt(dirent.name), null, 0).walk(
resolvedDir
)
).readConfigYaml(resolvedDir)
);
} else {
await this.addEntry(dir, dirent.name);
}
}
return this;
}
async readConfigYaml(dir: string) {
try {
const configFile = join(dir, ".config.yaml");
const config = await stat(configFile);
if (config.isFile()) {
const { title, weight } = load(
(await readFile(configFile)).toString(),
{}
) as FrontMatter;
if (weight) this.weight = weight;
if (title) this.pretty = title;
}
} catch (e) {
console.info("Could not load .config.yaml for ", dir);
console.error(e);
}
return this;
}
setContents(contents: string) {
this.contents = contents;
return this;
}
find(slug: string[], current: boolean = false, i = 0): TreeItem | undefined {
if (slug[i] !== this.value) return;
for (const child of this.children) {
const match = child.find(slug, current, i + 1);
if (match !== undefined) return match;
}
if (this.children.length === 0) return this;
}
walkCurrents(aim: string[], i = 0) {
this.current = aim[i] === this.value;
if (this.current) {
for (const child of this.children) {
child.walkCurrents(aim, i + 1);
}
}
}
copy(): TreeItem {
return new TreeItem(
this.value,
this.pretty,
this.weight,
this.contents,
this.children.map((child) => child.copy())
);
}
addChild(child: TreeItem) {
this.children.push(child);
}
plain(): ITreeItem {
return {
value: this.value,
current: this.current,
children: this.children.map((child) => child.plain()),
weight: this.weight,
pretty: this.pretty,
};
}
sort() {
this.children.forEach((child) => child.sort());
this.children.sort((a, b) => b.weight - a.weight);
return this;
}
}
export const findCurrentDir = (node: ITreeItem): ITreeItem | null => {
if (!node.current) {
return null;
}
for (const child of node.children) {
if (child.current && child.children.length !== 0) {
return child;
}
}
return node;
};