improve djot plugin

This commit is contained in:
2025-11-20 17:26:50 +01:00
parent 910038a5e3
commit 6fdb515289
2 changed files with 96 additions and 28 deletions

View File

@@ -2,13 +2,19 @@ import lume from "lume/mod.ts";
import nav from "lume/plugins/nav.ts";
import codeHighlight from "lume/plugins/code_highlight.ts";
import googleFonts from "lume/plugins/google_fonts.ts";
import { djotLoader, djotRender } from "./_djot.ts";
import djotPlugin from "./_djot.ts";
import djot from "@djot/djot";
const site = lume();
site.use(nav());
site.use(codeHighlight());
site.use(djotPlugin({
renderOptions: {
},
}));
site.use(googleFonts({
cssFile: "/styles/main.css",
placeholder: "/* import fonts */",
@@ -21,8 +27,4 @@ site.use(googleFonts({
site.copy("/styles");
site.copy("favicon.svg");
site.data("djot", djotRender);
site.filter("djot", djotRender);
site.loadPages([".dj"], djotLoader);
export default site;

View File

@@ -1,4 +1,25 @@
import djot from "@djot/djot";
import Site from "lume/core/site.ts";
import { merge } from "lume/core/utils/object.ts";
// Djot does not export these types
type Filter = object;
type HTMLRenderOptions = object;
export interface Options {
/** The list of file extensions this plugin applies to */
extensions?: string[];
filters?: Filter[];
/** Options passed to djot library */
renderOptions?: HTMLRenderOptions;
}
// Default options
export const defaults: Options = {
extensions: [".dj", ".djot"],
};
function extractTitle(ast: djot.Doc): string {
const firstSection = ast.children.find((c) => c.tag == "section");
@@ -10,35 +31,80 @@ function extractTitle(ast: djot.Doc): string {
return title ?? "No title";
}
export function djotRender(content: string): string {
return djot.renderHTML(djot.parse(content));
}
export async function djotLoader(path: string | URL) {
const content = await Deno.readTextFile(path);
const ast = djot.parse(content);
const title = extractTitle(ast);
djot.applyFilter(ast, () => {
return {
link: (el) => {
title,
content,
};
}
const stripMarkupSourceLinks: Filter = () => {
return {
link: (el: djot.Link) => {
if (el.destination) {
el.destination = el.destination.replace(
/(\.dj)(#[^.\s]+)?$/,
/(.(?:dj|md))(#[^.\s]+)?$/,
(_match: string, _ext: string, section: string) => {
return section ? section : "";
return section ?? "";
},
);
}
},
};
};
const defaultFilters = [
stripMarkupSourceLinks,
];
export class DjotEngine implements Lume.Engine {
filters: Filter[];
renderOptions?: HTMLRenderOptions;
constructor(filters: Filter[] = [], renderOptions?: HTMLRenderOptions) {
this.filters = [...defaultFilters, ...filters];
this.renderOptions = renderOptions;
}
render(
content: string | djot.Doc,
_data?: Record<string, unknown>,
_filename?: string,
): string {
if (typeof content === "string") {
content = djot.parse(content);
}
this.filters.forEach((filter, _index, _array) => {
djot.applyFilter(content, filter);
});
const title = extractTitle(ast);
const html = djot.renderHTML(ast);
return djot.renderHTML(
content,
this.renderOptions,
);
}
return {
title,
content: html,
addHelper() {}
deleteCache() {}
}
export default function (userOptions?: Options) {
const options = merge(defaults, userOptions);
return function (site: Site) {
const engine = new DjotEngine(options.filters, options.renderOptions);
site.loadPages(options.extensions, {
loader: djotLoader,
engine,
});
site.filter("djot", (content) => engine.render(content as string));
};
}