/* * Copyright 2024 Foster Hangdaan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { valibot as v } from "./deps.ts"; import Site from "lume/core/site.ts"; import diagrams from "./resources/diagrams.json" with { type: "json" }; import { deflateSync } from "node:zlib"; const textEncoder = new TextEncoder(); const diagramNames: string[] = diagrams.map((d) => d.name); const OptionsSchema = v.optional( v.object({ // The names of diagrams that will be converted. enabledDiagrams: v.optional( v.pipe( v.array(v.string()), v.everyItem( (item) => diagramNames.includes(item), `Invalid names provided to \`enabledDiagrams\`. Valid names are: ${ diagramNames.join(", ") }`, ), ), diagramNames, ), // Preferred output format. // Falls back to `svg` if the diagram does not support it. // For a complete list, refer to https://kroki.io/#support format: v.optional( v.union([ v.literal("jpeg"), v.literal("png"), v.literal("svg"), ]), "svg", ), server: v.optional( v.pipe( v.string(), v.nonEmpty(), v.url(), ), "https://kroki.io", ), }), { enabledDiagrams: diagramNames, format: "svg", server: "https://kroki.io", }, ); export type Options = v.InferInput; export default function (options?: Options): (site: Site) => void { const opts = v.parse(OptionsSchema, options); return (site: Site) => { site.process([".html"], (pages) => { pages.forEach((page) => { diagrams.forEach((diagram) => { if (page.document && opts.enabledDiagrams.includes(diagram.name)) { const format = diagram.formats.includes(opts.format) ? opts.format : "svg"; const codeblocks = page.document.querySelectorAll( `pre > code.${diagram.matcher}`, ); for (const codeblock of codeblocks) { if ( codeblock.textContent && codeblock.parentElement ) { const encoded = textEncoder.encode(codeblock.textContent); const compressed = deflateSync(encoded); const result = compressed.toString("base64url"); const img = page.document.createElement("img"); const url = new URL( `${opts.server}/${diagram.slug}/${format}/${result}`, ); img.setAttribute("src", url.toString()); codeblock.parentElement.replaceWith(img); } } } }); }); }); }; }