chore: syntax highlighting optimizations 1e573bc0
Steve · 2026-04-30 21:09 8 file(s) · +91 −76
astro.config.mjs +18 −27
1 1
import { defineConfig } from "astro/config";
2 2
import rehypeExternalLinks from "rehype-external-links";
3 +
import rehypeShikiFromHighlighter from "@shikijs/rehype/core";
3 4
import cloudflare from "@astrojs/cloudflare";
4 5
import mdx from "@astrojs/mdx";
5 6
import tailwindcss from "@tailwindcss/vite";
6 7
import sitemap from "@astrojs/sitemap";
7 8
import { fileURLToPath } from "url";
8 9
import path from "path";
9 -
import darkmatter from "./darkmatter.json";
10 +
import { createDarkmatterHighlighter, THEME_NAME } from "./shiki-setup.mjs";
11 +
12 +
const highlighter = createDarkmatterHighlighter();
13 +
14 +
const root = path.dirname(fileURLToPath(import.meta.url));
15 +
const emptyShikiTheme = path.resolve(root, "./empty-shiki-theme.mjs");
10 16
11 17
// https://astro.build/config
12 18
export default defineConfig({
14 20
	outDir: "dist",
15 21
	compressHTML: true,
16 22
	markdown: {
17 -
		shikiConfig: {
18 -
			theme: darkmatter,
19 -
			wrap: false,
20 -
		},
23 +
		syntaxHighlight: false,
21 24
		rehypePlugins: [
25 +
			[(opts) => rehypeShikiFromHighlighter(highlighter, opts), { theme: THEME_NAME, defaultLanguage: "text", fallbackLanguage: "text", addLanguageClass: true }],
22 26
			[rehypeExternalLinks, { target: "_blank", rel: ["noopener", "noreferrer"] }],
23 27
		],
24 28
	},
27 31
	vite: {
28 32
		plugins: [tailwindcss()],
29 33
		resolve: {
30 -
			alias: {
31 -
				"@/components": path.resolve(
32 -
					path.dirname(fileURLToPath(import.meta.url)),
33 -
					"./src/components",
34 -
				),
35 -
				"@/layouts": path.resolve(
36 -
					path.dirname(fileURLToPath(import.meta.url)),
37 -
					"./src/layouts",
38 -
				),
39 -
				"@/utils": path.resolve(
40 -
					path.dirname(fileURLToPath(import.meta.url)),
41 -
					"./src/utils/index.ts",
42 -
				),
43 -
				"@/stores": path.resolve(
44 -
					path.dirname(fileURLToPath(import.meta.url)),
45 -
					"./src/stores",
46 -
				),
47 -
				"@/data": path.resolve(
48 -
					path.dirname(fileURLToPath(import.meta.url)),
49 -
					"./src/data",
50 -
				),
51 -
			},
34 +
			alias: [
35 +
				{ find: /^@shikijs\/themes\/[^/]+$/, replacement: emptyShikiTheme },
36 +
				{ find: /^@shikijs\/themes\/dist\/.+\.mjs$/, replacement: emptyShikiTheme },
37 +
				{ find: "@/components", replacement: path.resolve(root, "./src/components") },
38 +
				{ find: "@/layouts", replacement: path.resolve(root, "./src/layouts") },
39 +
				{ find: "@/utils", replacement: path.resolve(root, "./src/utils/index.ts") },
40 +
				{ find: "@/stores", replacement: path.resolve(root, "./src/stores") },
41 +
				{ find: "@/data", replacement: path.resolve(root, "./src/data") },
42 +
			],
52 43
			extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".astro"],
53 44
		},
54 45
		ssr: {
bun.lock +15 −10
9 9
        "@astrojs/rss": "4.0.14",
10 10
        "@astrojs/ts-plugin": "1.10.6",
11 11
        "astro": "5.16.7",
12 -
        "markdown-it": "^14.1.0",
12 +
        "markdown-it": "^14.1.1",
13 13
        "rehype-external-links": "^3.0.0",
14 -
        "sanitize-html": "^2.17.0",
15 -
        "sharp": "^0.33.1",
14 +
        "sanitize-html": "^2.17.3",
15 +
        "sharp": "^0.33.5",
16 16
        "shiki": "^4.0.2",
17 17
      },
18 18
      "devDependencies": {
19 19
        "@astrojs/mdx": "4.3.13",
20 20
        "@astrojs/sitemap": "3.6.1",
21 21
        "@biomejs/biome": "2.1.1",
22 -
        "@tailwindcss/typography": "^0.5.15",
23 -
        "@tailwindcss/vite": "^4.1.7",
24 -
        "@types/bun": "^1.3.6",
22 +
        "@shikijs/rehype": "^4.0.2",
23 +
        "@tailwindcss/typography": "^0.5.19",
24 +
        "@tailwindcss/vite": "^4.2.4",
25 +
        "@types/bun": "^1.3.13",
25 26
        "@types/markdown-it": "^14.1.2",
26 -
        "@types/sanitize-html": "^2.16.0",
27 -
        "tailwindcss": "^4.1.7",
28 -
        "typescript": "^5.6.2",
29 -
        "wrangler": "^4.58.0",
27 +
        "@types/sanitize-html": "^2.16.1",
28 +
        "tailwindcss": "^4.2.4",
29 +
        "typescript": "^5.9.3",
30 +
        "wrangler": "^4.87.0",
30 31
      },
31 32
    },
32 33
  },
288 289
    "@shikijs/langs": ["@shikijs/langs@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg=="],
289 290
290 291
    "@shikijs/primitive": ["@shikijs/primitive@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw=="],
292 +
293 +
    "@shikijs/rehype": ["@shikijs/rehype@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.1", "shiki": "4.0.2", "unified": "^11.0.5", "unist-util-visit": "^5.1.0" } }, "sha512-cmPlKLD8JeojasNFoY64162ScpEdEdQUMuVodPCrv1nx1z3bjmGwoKWDruQWa/ejSznImlaeB0Ty6Q3zPaVQAA=="],
291 294
292 295
    "@shikijs/themes": ["@shikijs/themes@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA=="],
293 296
578 581
    "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
579 582
580 583
    "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="],
584 +
585 +
    "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="],
581 586
582 587
    "hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="],
583 588
empty-shiki-theme.mjs (added) +1 −0
1 +
export default {};
package.json +1 −0
16 16
		"@astrojs/mdx": "4.3.13",
17 17
		"@astrojs/sitemap": "3.6.1",
18 18
		"@biomejs/biome": "2.1.1",
19 +
		"@shikijs/rehype": "^4.0.2",
19 20
		"@tailwindcss/typography": "^0.5.19",
20 21
		"@tailwindcss/vite": "^4.2.4",
21 22
		"@types/bun": "^1.3.13",
shiki-setup.mjs (added) +49 −0
1 +
import { createHighlighterCoreSync } from "shiki/core";
2 +
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
3 +
import javascript from "@shikijs/langs/javascript";
4 +
import typescript from "@shikijs/langs/typescript";
5 +
import jsx from "@shikijs/langs/jsx";
6 +
import tsx from "@shikijs/langs/tsx";
7 +
import python from "@shikijs/langs/python";
8 +
import bash from "@shikijs/langs/bash";
9 +
import shellscript from "@shikijs/langs/shellscript";
10 +
import json from "@shikijs/langs/json";
11 +
import html from "@shikijs/langs/html";
12 +
import css from "@shikijs/langs/css";
13 +
import rust from "@shikijs/langs/rust";
14 +
import go from "@shikijs/langs/go";
15 +
import markdown from "@shikijs/langs/markdown";
16 +
import yaml from "@shikijs/langs/yaml";
17 +
import toml from "@shikijs/langs/toml";
18 +
import lua from "@shikijs/langs/lua";
19 +
import darkmatter from "./darkmatter.json" with { type: "json" };
20 +
21 +
export const LANGS = [
22 +
	javascript,
23 +
	typescript,
24 +
	jsx,
25 +
	tsx,
26 +
	python,
27 +
	bash,
28 +
	shellscript,
29 +
	json,
30 +
	html,
31 +
	css,
32 +
	rust,
33 +
	go,
34 +
	markdown,
35 +
	yaml,
36 +
	toml,
37 +
	lua,
38 +
];
39 +
40 +
export const THEME = darkmatter;
41 +
export const THEME_NAME = "Darkmatter";
42 +
43 +
export function createDarkmatterHighlighter() {
44 +
	return createHighlighterCoreSync({
45 +
		themes: [darkmatter],
46 +
		langs: LANGS,
47 +
		engine: createJavaScriptRegexEngine(),
48 +
	});
49 +
}
src/pages/git.astro +3 −10
1 1
---
2 2
import PageLayout from "@/layouts/Base.astro";
3 -
import { createHighlighterCore, type ThemeRegistrationRaw } from "shiki/core";
4 -
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
5 -
import bash from "@shikijs/langs/bash";
6 -
import darkmatter from "../../darkmatter.json";
3 +
import { createDarkmatterHighlighter, THEME_NAME } from "../../shiki-setup.mjs";
7 4
8 5
const meta = {
9 6
	title: "Git",
10 7
	description: "Access the open source repos I'm working on",
11 8
};
12 9
13 -
const highlighter = await createHighlighterCore({
14 -
	themes: [darkmatter as unknown as ThemeRegistrationRaw],
15 -
	langs: [bash],
16 -
	engine: createJavaScriptRegexEngine(),
17 -
});
10 +
const highlighter = createDarkmatterHighlighter();
18 11
19 12
const codeHtml = highlighter.codeToHtml("ssh git.stevedylan.dev", {
20 13
	lang: "bash",
21 -
	theme: "Darkmatter",
14 +
	theme: THEME_NAME,
22 15
});
23 16
---
24 17
src/styles/global.css +1 −1
132 132
.prose :where(strong):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
133 133
	font-weight: 700;
134 134
}
135 -
.prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
135 +
.prose :where(code):not(:where(pre code, [class~="not-prose"], [class~="not-prose"] *)) {
136 136
	border: 1px dotted #666;
137 137
	border-radius: 2px;
138 138
}
src/utils/markdown.ts +3 −28
1 1
import MarkdownIt from "markdown-it";
2 -
import { createHighlighterCore, type ThemeRegistrationRaw } from "shiki/core";
3 -
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
4 -
import darkmatter from "../../darkmatter.json";
5 -
6 -
const LANGS = [
7 -
	import("@shikijs/langs/javascript"),
8 -
	import("@shikijs/langs/typescript"),
9 -
	import("@shikijs/langs/jsx"),
10 -
	import("@shikijs/langs/tsx"),
11 -
	import("@shikijs/langs/python"),
12 -
	import("@shikijs/langs/bash"),
13 -
	import("@shikijs/langs/shellscript"),
14 -
	import("@shikijs/langs/json"),
15 -
	import("@shikijs/langs/html"),
16 -
	import("@shikijs/langs/css"),
17 -
	import("@shikijs/langs/rust"),
18 -
	import("@shikijs/langs/go"),
19 -
	import("@shikijs/langs/markdown"),
20 -
	import("@shikijs/langs/yaml"),
21 -
	import("@shikijs/langs/toml"),
22 -
	import("@shikijs/langs/lua"),
23 -
];
2 +
import { createDarkmatterHighlighter, THEME_NAME } from "../../shiki-setup.mjs";
24 3
25 4
export async function createMarkdownRenderer(): Promise<MarkdownIt> {
26 -
	const highlighter = await createHighlighterCore({
27 -
		themes: [darkmatter as unknown as ThemeRegistrationRaw],
28 -
		langs: LANGS,
29 -
		engine: createJavaScriptRegexEngine(),
30 -
	});
5 +
	const highlighter = createDarkmatterHighlighter();
31 6
32 7
	let md: MarkdownIt;
33 8
42 17
			try {
43 18
				return highlighter.codeToHtml(code, {
44 19
					lang: resolvedLang,
45 -
					theme: "Darkmatter",
20 +
					theme: THEME_NAME,
46 21
				});
47 22
			} catch {
48 23
				return `<pre><code>${md.utils.escapeHtml(code)}</code></pre>`;