mirror of
https://github.com/versia-pub/blog.git
synced 2026-03-13 01:29:15 +01:00
feat: ✨ Initialize new repo
This commit is contained in:
commit
e7941231a5
23 changed files with 858 additions and 0 deletions
153
utils/markdown.ts
Normal file
153
utils/markdown.ts
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
import markdownItTaskLists from "@hackmd/markdown-it-task-lists";
|
||||
import { fromHighlighter } from "@shikijs/markdown-it/core";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import markdownItAnchor from "markdown-it-anchor";
|
||||
import markdownItContainer from "markdown-it-container";
|
||||
import markdownItTocDoneRight from "markdown-it-toc-done-right";
|
||||
import { createHighlighterCore } from "shiki/core";
|
||||
|
||||
const highlighter = createHighlighterCore({
|
||||
themes: [import("shiki/themes/rose-pine.mjs")],
|
||||
langs: [
|
||||
import("shiki/langs/javascript.mjs"),
|
||||
import("shiki/langs/typescript.mjs"),
|
||||
import("shiki/langs/python.mjs"),
|
||||
import("shiki/langs/toml.mjs"),
|
||||
import("shiki/langs/rust.mjs"),
|
||||
import("shiki/langs/sql.mjs"),
|
||||
import("shiki/langs/json.mjs"),
|
||||
import("shiki/langs/html.mjs"),
|
||||
import("shiki/langs/css.mjs"),
|
||||
import("shiki/langs/scss.mjs"),
|
||||
import("shiki/langs/bash.mjs"),
|
||||
import("shiki/langs/shell.mjs"),
|
||||
import("shiki/langs/yaml.mjs"),
|
||||
],
|
||||
loadWasm: import("shiki/wasm"),
|
||||
});
|
||||
|
||||
export const getMarkdownRenderer = async () => {
|
||||
const renderer = MarkdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
});
|
||||
|
||||
const otherRenderer = MarkdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
});
|
||||
|
||||
for (const ren of [renderer, otherRenderer]) {
|
||||
ren.use(
|
||||
// @ts-ignore
|
||||
fromHighlighter(await highlighter, {
|
||||
theme: "rose-pine",
|
||||
}),
|
||||
);
|
||||
|
||||
ren.use(markdownItAnchor, {
|
||||
permalink: markdownItAnchor.permalink.ariaHidden({
|
||||
symbol: "",
|
||||
placement: "before",
|
||||
}),
|
||||
});
|
||||
|
||||
ren.use(markdownItTocDoneRight, {
|
||||
containerClass: "toc",
|
||||
level: [1, 2, 3, 4],
|
||||
listType: "ul",
|
||||
listClass: "toc-list",
|
||||
itemClass: "toc-item",
|
||||
linkClass: "toc-link",
|
||||
});
|
||||
|
||||
ren.use(markdownItTaskLists);
|
||||
|
||||
ren.use(markdownItContainer, "spoiler");
|
||||
}
|
||||
|
||||
renderer.use((md) => {
|
||||
md.renderer.rules.html_block = (tokens, idx) => {
|
||||
// Modify figure tags
|
||||
if (tokens[idx]?.content.startsWith("<figure")) {
|
||||
const imageUrl = (tokens[idx].content.match(
|
||||
/src="([^"]+)"/,
|
||||
) ?? [null, null])[1];
|
||||
|
||||
if (!imageUrl) {
|
||||
return otherRenderer.render(tokens[idx].content);
|
||||
}
|
||||
|
||||
// Delete the first / if it exists
|
||||
const newUrl = `/_ipx/w_800&f_webp/${imageUrl.replace(
|
||||
/^\//,
|
||||
"",
|
||||
)}`;
|
||||
|
||||
return tokens[idx].content.replace(imageUrl, newUrl);
|
||||
}
|
||||
|
||||
return tokens[idx]
|
||||
? otherRenderer.render(tokens[idx]?.content)
|
||||
: "";
|
||||
};
|
||||
|
||||
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
||||
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
||||
const output = otherRenderer.renderer.rules.image!(
|
||||
tokens,
|
||||
idx,
|
||||
options,
|
||||
env,
|
||||
self,
|
||||
);
|
||||
|
||||
const imageUrl = (output.match(/src="([^"]+)"/) ?? [null, null])[1];
|
||||
|
||||
if (!imageUrl) {
|
||||
return output;
|
||||
}
|
||||
|
||||
const newUrl = `/_ipx/w_800&f_webp/${imageUrl.replace(/^\//, "")}`;
|
||||
|
||||
return output.replace(imageUrl, newUrl);
|
||||
};
|
||||
});
|
||||
|
||||
return renderer;
|
||||
};
|
||||
|
||||
// Extract the FrontMatter header from raw markdown
|
||||
export const parseFrontMatter = <T>(frontMatter: string): T | null => {
|
||||
const regex = /---\n([\s\S]+?)\n---/;
|
||||
const match = frontMatter.match(regex);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const [, frontMatterString] = match;
|
||||
|
||||
if (!frontMatterString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const frontMatterObject = frontMatterString
|
||||
.split("\n")
|
||||
.map((line) => line.split(": "))
|
||||
.reduce(
|
||||
(acc, [key, value]) => {
|
||||
if (!(key && value)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
return frontMatterObject as T;
|
||||
};
|
||||
|
||||
export const stripFrontMatter = (markdown: string) => {
|
||||
// Also strip the --- --- around the front matter
|
||||
return markdown.replace(/---\n([\s\S]+?)\n---/, "");
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue