aubade
Version:
filesystem-based content processor
59 lines (58 loc) • 2.08 kB
JavaScript
import * as fs from 'fs/promises';
import { marker } from '../artisan/index.js';
import { parse } from '../core/index.js';
import { catenate } from './utils.js';
export async function traverse(entry, inspect = ({ path }) => {
if (!path.endsWith('.md'))
return;
return async ({ buffer, parse }) => {
const { body, frontmatter } = parse(buffer.toString('utf-8'));
if (!frontmatter)
return;
const result = { ...frontmatter, content: marker.render(body).trim() };
return result;
};
}) {
const results = [];
const pending = [];
async function scan(current, { depth = 0 } = {}) {
const tree = await fs.readdir(current, { withFileTypes: true });
const files = tree.flatMap((i) => {
if (i.isDirectory())
return [];
return {
filename: i.name,
get buffer() {
return fs.readFile(catenate(current, i.name));
},
};
});
for (const item of tree) {
const path = catenate(current, item.name);
if (item.isDirectory()) {
pending.push(scan(path, { depth: depth + 1 }));
continue;
}
const hydrate = inspect({
breadcrumb: path.split(/[/\\]/).reverse(),
depth,
path,
parent: current,
});
if (hydrate) {
const transformed = hydrate({
buffer: await fs.readFile(path),
marker,
parse: parse,
siblings: files.filter(({ filename }) => filename !== item.name),
task: (fn) => pending.push(fn({ fs })),
});
results.push(transformed);
}
}
}
await scan(entry); // await for the initial scan to complete
while (pending.length)
await Promise.all(pending.splice(0));
return (await Promise.all(results)).filter((i) => !!i);
}