You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

97 lines
3.2 KiB

/*
* Implements a simple markdown blog loader that's used primarily in the
* `rendered/pages/blog` example. It supports generating an RSS/Atom/JSON
* feed of pages, loading an index of .md pages, and render markdown with
* the headers. It uses the `lib/docgen.js:render_with_code` to do the
* main markdown processing.
*/
import path from "path";
import glob from "fast-glob";
import assert from "assert";
import { render_with_code } from "./docgen.js";
import fs from "fs";
import { Feed } from "feed";
// have to use the client config to avoid pino crashing rollup with json errors
import { base_host } from "../client/config.js";
// caches the posts so we don't load them repeatedly during runs
const POSTS = {};
// this keeps POSTS alive, for some reason it disappears during processing
process.POSTS = POSTS;
/*
Takes a directory of `.md` files and returns all of them rendered
to HTML+Metadata using `lib/docgen.js:render_with_code`.
+ `source string` -- Source directory to read.
*/
export const load = (source) => {
assert(source !== undefined, "source for markdown processing can't be undefined");
// just a dumb cache to avoid reading the files over and over
// cache only works if load isn't async!
let cache = POSTS[source];
if(cache === undefined) {
cache = []; // this is fresh so make a new one
let source_path = path.join(process.cwd(), "..", source);
let posts_regex = path.join(source_path, "*.md");
assert(!posts_regex.includes("//"), `You have a // in your path which fast-glob hates: ${posts_regex}`);
const files = glob.sync(posts_regex.replace(/\\/g, "/"));
for(let post of files.reverse()) {
try {
const {content, toc, metadata} = render_with_code(source_path, post);
assert(content && toc && metadata, `Rendering ${source} failed.`);
// so many Svelte builders (like 7ty) need slug that we should do it here
cache.push({content, toc, metadata, slug: metadata.slug});
} catch(error) {
console.error(error, `Failure processing post: ${post}`);
}
}
POSTS[source] = cache;
return cache;
} else {
return cache;
}
}
/*
Renders the `/public/feed.rss`, `/public/feed.atom`, and `/public/feed.json` files
based on the `index_config` and posts from `load`. The `index_config` is a `.json`
file that describes the blog. The current configuration can be
found in `renderd/feed_config.json`.
+ `index_config string` -- The path to the `feed_config.json` file.
+ `posts Array` -- List of posts.
*/
export const render_feed = (index_config, posts) => {
const index = JSON.parse(fs.readFileSync(index_config));
const feed = new Feed(index);
index.updated = new Date();
for(let post of posts) {
post.url = `${base_host}/blog/${post.slug}/`;
feed.addItem({
title: post.metadata.title,
id: post.metadata.url,
link: post.metadata.url,
date: new Date(post.metadata.date),
description: post.metadata.summary,
content: post.content,
author: index.author
});
}
fs.writeFileSync('../public/feed.rss', feed.rss2());
fs.writeFileSync('../public/feed.json', feed.json1());
fs.writeFileSync('../public/feed.atom', feed.atom1());
}
export default { load, render_feed };