aubade
Version:
filesystem-based content processor
79 lines (78 loc) • 2.72 kB
JavaScript
import * as fs from 'fs/promises';
import { engrave } from '../artisan/index.js';
import { assemble } from '../core/index.js';
export async function orchestrate(entry, inspect = ({ path }) => {
if (!path.endsWith('.md'))
return;
return async ({ assemble, buffer }) => {
const { manifest, md, meta } = assemble(buffer.toString('utf-8'));
if (!manifest)
return;
return { ...manifest, words: meta.words, content: md.html() };
};
}) {
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({
assemble,
buffer: await fs.readFile(path),
engrave,
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);
}
function catenate(...paths) {
if (!paths.length)
return '.';
const index = paths[0].replace(/\\/g, '/').trim();
if (paths.length === 1 && index === '')
return '.';
const parts = index.replace(/[/]*$/g, '').split('/');
if (parts[0] === '')
parts.shift();
for (let i = 1; i < paths.length; i += 1) {
const part = paths[i].replace(/\\/g, '/').trim();
for (const slice of part.split('/')) {
if (slice === '.')
continue;
if (slice === '..')
parts.pop();
else if (slice)
parts.push(slice);
}
}
return (index[0] === '/' ? '/' : '') + parts.join('/');
}