From 2e489f0ef6cf8f03d3558a09b1bce91544093668 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Mon, 7 Oct 2024 13:48:08 -0400 Subject: [PATCH] Guard against unknown languages; fix nested pre styles --- packages/astro-md/middleware.ts | 14 +++-- packages/astro-md/package.json | 3 +- packages/astro-md/syntax-highlighting.ts | 40 ++++++++++++ pnpm-lock.yaml | 80 ++++++++++++++++++------ src/style.css | 5 ++ 5 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 packages/astro-md/syntax-highlighting.ts diff --git a/packages/astro-md/middleware.ts b/packages/astro-md/middleware.ts index 5cee79d..469d4d5 100644 --- a/packages/astro-md/middleware.ts +++ b/packages/astro-md/middleware.ts @@ -5,6 +5,7 @@ import { defineMiddleware } from "astro:middleware"; import { myRemark } from "../my-remark"; import { remarkObsidian } from "../remark-obsidian"; import remarkFrontmatter from "remark-frontmatter"; +import { isLangSupported } from "./syntax-highlighting"; // Cache for 30 days const syntaxHighlightCacheTtl = 60 * 60 * 24 * 30; @@ -19,6 +20,7 @@ class SyntaxHighlightRewriter implements HTMLRewriterElementContentHandlers { private code: string = ""; cache: KVNamespace; salt: string; + skip: boolean = false; constructor(private runtime: Runtime["runtime"]) { this.cache = runtime.env.KV_HIGHLIGHT; @@ -28,9 +30,16 @@ class SyntaxHighlightRewriter implements HTMLRewriterElementContentHandlers { element(element: Element) { this.lang = element.getAttribute("data-lang") ?? ""; this.code = ""; + if (!isLangSupported(this.lang)) { + element.removeAttribute("data-lang"); + element.removeAttribute("class"); + this.skip = true; + return; + } element.removeAndKeepContent(); } async text(text: Text) { + if (this.skip) return; this.code += decode(text.text); if (text.lastInTextNode) { const hashBuffer = await crypto.subtle.digest( @@ -102,11 +111,6 @@ export const onRequest = defineMiddleware(async (context, next) => { "code[data-lang]", new SyntaxHighlightRewriter(context.locals.runtime) ) - .on("pre:not(.shiki)", { - element: (element) => { - element.removeAndKeepContent(); - }, - }) .transform(res); } catch (e) { console.error(e); diff --git a/packages/astro-md/package.json b/packages/astro-md/package.json index 9defc54..96bc4a0 100644 --- a/packages/astro-md/package.json +++ b/packages/astro-md/package.json @@ -11,7 +11,8 @@ "license": "MIT", "dependencies": { "@astrojs/markdown-remark": "catalog:", - "remark-frontmatter": "^5.0.0" + "remark-frontmatter": "^5.0.0", + "shiki": "^1.22.0" }, "devDependencies": { "@worker-tools/html-rewriter": "0.1.0-pre.19" diff --git a/packages/astro-md/syntax-highlighting.ts b/packages/astro-md/syntax-highlighting.ts new file mode 100644 index 0000000..6bb5ef2 --- /dev/null +++ b/packages/astro-md/syntax-highlighting.ts @@ -0,0 +1,40 @@ +import { bundledLanguagesInfo } from "shiki/langs"; +import { bundledThemesInfo } from "shiki/themes"; + +interface Theme { + id: string; +} +type ThemeMap = Record; + +const themes: ThemeMap = bundledThemesInfo.reduce( + (themes: ThemeMap, theme: Theme) => { + themes[theme.id] = theme; + return themes; + }, + {} +); + +interface Lang { + id: string; + aliases?: string[]; +} +type LangMap = Record; + +const langs: LangMap = bundledLanguagesInfo.reduce( + (langs: LangMap, lang: Lang) => { + langs[lang.id] = lang; + for (const alias of lang.aliases ?? []) { + langs[alias] = lang; + } + return langs; + }, + {} +); + +export const isThemeSupported = (theme: string) => { + return !!themes[theme]; +}; + +export const isLangSupported = (lang: string) => { + return !!langs[lang]; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8682ed4..25e32e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,9 @@ importers: remark-frontmatter: specifier: ^5.0.0 version: 5.0.0 + shiki: + specifier: ^1.22.0 + version: 1.22.0 devDependencies: '@worker-tools/html-rewriter': specifier: 0.1.0-pre.19 @@ -1060,24 +1063,36 @@ packages: resolution: {integrity: sha512-dFARM360varU+hdU1MCpl0VTL03FkVIC+A9egCE+ureuOryjVNe3cm2mUjv/gnDHHNTOxWC2H2c8BlOkqTGP/w==} engines: {node: '>= 14'} - '@shikijs/core@1.14.1': - resolution: {integrity: sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==} - '@shikijs/core@1.21.0': resolution: {integrity: sha512-zAPMJdiGuqXpZQ+pWNezQAk5xhzRXBNiECFPcJLtUdsFM3f//G95Z15EHTnHchYycU8kIIysqGgxp8OVSj1SPQ==} + '@shikijs/core@1.22.0': + resolution: {integrity: sha512-S8sMe4q71TJAW+qG93s5VaiihujRK6rqDFqBnxqvga/3LvqHEnxqBIOPkt//IdXVtHkQWKu4nOQNk0uBGicU7Q==} + '@shikijs/engine-javascript@1.21.0': resolution: {integrity: sha512-jxQHNtVP17edFW4/0vICqAVLDAxmyV31MQJL4U/Kg+heQALeKYVOWo0sMmEZ18FqBt+9UCdyqGKYE7bLRtk9mg==} + '@shikijs/engine-javascript@1.22.0': + resolution: {integrity: sha512-AeEtF4Gcck2dwBqCFUKYfsCq0s+eEbCEbkUuFou53NZ0sTGnJnJ/05KHQFZxpii5HMXbocV9URYVowOP2wH5kw==} + '@shikijs/engine-oniguruma@1.21.0': resolution: {integrity: sha512-AIZ76XocENCrtYzVU7S4GY/HL+tgHGbVU+qhiDyNw1qgCA5OSi4B4+HY4BtAoJSMGuD/L5hfTzoRVbzEm2WTvg==} + '@shikijs/engine-oniguruma@1.22.0': + resolution: {integrity: sha512-5iBVjhu/DYs1HB0BKsRRFipRrD7rqjxlWTj4F2Pf+nQSPqc3kcyqFFeZXnBMzDf0HdqaFVvhDRAGiYNvyLP+Mw==} + '@shikijs/types@1.21.0': resolution: {integrity: sha512-tzndANDhi5DUndBtpojEq/42+dpUF2wS7wdCDQaFtIXm3Rd1QkrcVgSSRLOvEwexekihOXfbYJINW37g96tJRw==} + '@shikijs/types@1.22.0': + resolution: {integrity: sha512-Fw/Nr7FGFhlQqHfxzZY8Cwtwk5E9nKDUgeLjZgt3UuhcM3yJR9xj3ZGNravZZok8XmEZMiYkSMTPlPkULB8nww==} + '@shikijs/vscode-textmate@9.2.2': resolution: {integrity: sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==} + '@shikijs/vscode-textmate@9.3.0': + resolution: {integrity: sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==} + '@stardazed/streams-compression@1.0.0': resolution: {integrity: sha512-SQCiwxdIVJ5xxUBYptN+fc+tJpSDbxQuJ0+3u2SmHjIzr6JIRZ28AVFtFnGy6x6j3UBlaLx73w9rC6UAFxnd1g==} @@ -2612,12 +2627,12 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@1.14.1: - resolution: {integrity: sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==} - shiki@1.21.0: resolution: {integrity: sha512-apCH5BoWTrmHDPGgg3RF8+HAAbEL/CdbYr8rMw7eIrdhCkZHdVGat5mMNlRtd1erNG01VPMIKHNQ0Pj2HMAiog==} + shiki@1.22.0: + resolution: {integrity: sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -3321,7 +3336,7 @@ snapshots: remark-parse: 11.0.0 remark-rehype: 11.1.0 remark-smartypants: 3.0.2 - shiki: 1.14.1 + shiki: 1.22.0 unified: 11.0.5 unist-util-remove-position: 5.0.0 unist-util-visit: 5.0.0 @@ -4033,10 +4048,6 @@ snapshots: - encoding - supports-color - '@shikijs/core@1.14.1': - dependencies: - '@types/hast': 3.0.4 - '@shikijs/core@1.21.0': dependencies: '@shikijs/engine-javascript': 1.21.0 @@ -4046,24 +4057,51 @@ snapshots: '@types/hast': 3.0.4 hast-util-to-html: 9.0.3 + '@shikijs/core@1.22.0': + dependencies: + '@shikijs/engine-javascript': 1.22.0 + '@shikijs/engine-oniguruma': 1.22.0 + '@shikijs/types': 1.22.0 + '@shikijs/vscode-textmate': 9.3.0 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.3 + '@shikijs/engine-javascript@1.21.0': dependencies: '@shikijs/types': 1.21.0 '@shikijs/vscode-textmate': 9.2.2 oniguruma-to-js: 0.4.3 + '@shikijs/engine-javascript@1.22.0': + dependencies: + '@shikijs/types': 1.22.0 + '@shikijs/vscode-textmate': 9.3.0 + oniguruma-to-js: 0.4.3 + '@shikijs/engine-oniguruma@1.21.0': dependencies: '@shikijs/types': 1.21.0 '@shikijs/vscode-textmate': 9.2.2 + '@shikijs/engine-oniguruma@1.22.0': + dependencies: + '@shikijs/types': 1.22.0 + '@shikijs/vscode-textmate': 9.3.0 + '@shikijs/types@1.21.0': dependencies: '@shikijs/vscode-textmate': 9.2.2 '@types/hast': 3.0.4 + '@shikijs/types@1.22.0': + dependencies: + '@shikijs/vscode-textmate': 9.3.0 + '@types/hast': 3.0.4 + '@shikijs/vscode-textmate@9.2.2': {} + '@shikijs/vscode-textmate@9.3.0': {} + '@stardazed/streams-compression@1.0.0': dependencies: '@stardazed/zlib': 1.0.1 @@ -4111,7 +4149,7 @@ snapshots: '@types/hast@3.0.4': dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 '@types/mdast@4.0.4': dependencies: @@ -6017,11 +6055,6 @@ snapshots: shebang-regex@3.0.0: {} - shiki@1.14.1: - dependencies: - '@shikijs/core': 1.14.1 - '@types/hast': 3.0.4 - shiki@1.21.0: dependencies: '@shikijs/core': 1.21.0 @@ -6031,6 +6064,15 @@ snapshots: '@shikijs/vscode-textmate': 9.2.2 '@types/hast': 3.0.4 + shiki@1.22.0: + dependencies: + '@shikijs/core': 1.22.0 + '@shikijs/engine-javascript': 1.22.0 + '@shikijs/engine-oniguruma': 1.22.0 + '@shikijs/types': 1.22.0 + '@shikijs/vscode-textmate': 9.3.0 + '@types/hast': 3.0.4 + siginfo@2.0.0: {} signal-exit@4.1.0: {} @@ -6247,7 +6289,7 @@ snapshots: unist-util-position@5.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-remove-position@5.0.0: dependencies: @@ -6290,7 +6332,7 @@ snapshots: vfile-location@5.0.3: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 vfile: 6.0.2 vfile-matter@5.0.0: diff --git a/src/style.css b/src/style.css index 6672ab7..72ab57d 100644 --- a/src/style.css +++ b/src/style.css @@ -52,3 +52,8 @@ blockquote ul { .prose pre:has(code[data-lang]) { @apply bg-inherit my-0; } + +/* Work around for nested pre tags */ +.prose pre:has(pre[data-lang]) { + @apply bg-inherit mx-0 -my-5 p-0; +}