112 lines
3.2 KiB
TypeScript
112 lines
3.2 KiB
TypeScript
/*
|
|
* 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 * as v from "@valibot/valibot";
|
|
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<typeof OptionsSchema>;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
};
|
|
}
|