diff options
-rw-r--r-- | .changeset/tender-suits-glow.md | 6 | ||||
-rw-r--r-- | packages/astro/components/Code.astro | 17 | ||||
-rw-r--r-- | packages/astro/src/core/config/schema.ts | 9 | ||||
-rw-r--r-- | packages/astro/src/vite-plugin-markdown/index.ts | 1 | ||||
-rw-r--r-- | packages/markdown/remark/src/index.ts | 1 | ||||
-rw-r--r-- | packages/markdown/remark/src/remark-shiki.ts | 11 | ||||
-rw-r--r-- | packages/markdown/remark/src/types.ts | 1 | ||||
-rw-r--r-- | packages/markdown/remark/test/shiki.js | 24 |
8 files changed, 62 insertions, 8 deletions
diff --git a/.changeset/tender-suits-glow.md b/.changeset/tender-suits-glow.md new file mode 100644 index 000000000..a01662ce6 --- /dev/null +++ b/.changeset/tender-suits-glow.md @@ -0,0 +1,6 @@ +--- +'@astrojs/markdown-remark': minor +'astro': minor +--- + +Adds experimental support for multiple shiki themes with the new `markdown.shikiConfig.experimentalThemes` option. diff --git a/packages/astro/components/Code.astro b/packages/astro/components/Code.astro index 0a4fff6b9..b1d21fd9e 100644 --- a/packages/astro/components/Code.astro +++ b/packages/astro/components/Code.astro @@ -33,6 +33,11 @@ interface Props { */ theme?: BuiltinTheme | ThemeRegistration | ThemeRegistrationRaw; /** + * Multiple themes to style with -- alternative to "theme" option. + * Supports all themes found above; see https://github.com/antfu/shikiji#lightdark-dual-themes for more information. + */ + experimentalThemes?: Record<string, BuiltinTheme | ThemeRegistration | ThemeRegistrationRaw>; + /** * Enable word wrapping. * - true: enabled. * - false: disabled. @@ -53,6 +58,7 @@ const { code, lang = 'plaintext', theme = 'github-dark', + experimentalThemes = {}, wrap = false, inline = false, } = Astro.props; @@ -88,12 +94,15 @@ if (typeof lang === 'object') { const highlighter = await getCachedHighlighter({ langs: [lang], - themes: [theme], + themes: Object.values(experimentalThemes).length ? Object.values(experimentalThemes) : [theme], }); +const themeOptions = Object.values(experimentalThemes).length + ? { themes: experimentalThemes } + : { theme }; const html = highlighter.codeToHtml(code, { lang: typeof lang === 'string' ? lang : lang.name, - theme, + ...themeOptions, transforms: { pre(node) { // Swap to `code` tag if inline @@ -123,6 +132,10 @@ const html = highlighter.codeToHtml(code, { } }, root(node) { + if (Object.values(experimentalThemes).length) { + return; + } + // theme.id for shiki -> shikiji compat const themeName = typeof theme === 'string' ? theme : theme.name; if (themeName === 'css-variables') { diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index 5a5964a12..ea656b9bf 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -292,7 +292,14 @@ export const AstroConfigSchema = z.object({ theme: z .enum(Object.keys(bundledThemes) as [BuiltinTheme, ...BuiltinTheme[]]) .or(z.custom<ShikiTheme>()) - .default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme as BuiltinTheme), + .default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme!), + experimentalThemes: z + .record( + z + .enum(Object.keys(bundledThemes) as [BuiltinTheme, ...BuiltinTheme[]]) + .or(z.custom<ShikiTheme>()) + ) + .default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.experimentalThemes!), wrap: z.boolean().or(z.null()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.wrap!), }) .default({}), diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index 3c8a1af46..fdfe3db37 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -81,6 +81,7 @@ export default function markdown({ settings, logger }: AstroPluginOptions): Plug const renderResult = await processor .render(raw.content, { + // @ts-expect-error passing internal prop fileURL, frontmatter: raw.data, }) diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index 89c9ca8bd..61f97072b 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -39,6 +39,7 @@ export const markdownConfigDefaults: Omit<Required<AstroMarkdownOptions>, 'draft shikiConfig: { langs: [], theme: 'github-dark', + experimentalThemes: {}, wrap: false, }, remarkPlugins: [], diff --git a/packages/markdown/remark/src/remark-shiki.ts b/packages/markdown/remark/src/remark-shiki.ts index bf3dd0b78..4eaae5ff2 100644 --- a/packages/markdown/remark/src/remark-shiki.ts +++ b/packages/markdown/remark/src/remark-shiki.ts @@ -30,9 +30,15 @@ const highlighterCacheAsync = new Map<string, Promise<Highlighter>>(); export function remarkShiki({ langs = [], theme = 'github-dark', + experimentalThemes = {}, wrap = false, }: ShikiConfig = {}): ReturnType<RemarkPlugin> { + const themes = experimentalThemes; + const cacheId = + Object.values(themes) + .map((t) => (typeof t === 'string' ? t : t.name ?? '')) + .join(',') + (typeof theme === 'string' ? theme : theme.name ?? '') + langs.map((l) => l.name ?? (l as any).id).join(','); @@ -40,7 +46,7 @@ export function remarkShiki({ if (!highlighterAsync) { highlighterAsync = getHighlighter({ langs: langs.length ? langs : Object.keys(bundledLanguages), - themes: [theme], + themes: Object.values(themes).length ? Object.values(themes) : [theme], }); highlighterCacheAsync.set(cacheId, highlighterAsync); } @@ -64,7 +70,8 @@ export function remarkShiki({ lang = 'plaintext'; } - let html = highlighter.codeToHtml(node.value, { lang, theme }); + let themeOptions = Object.values(themes).length ? { themes } : { theme }; + let html = highlighter.codeToHtml(node.value, { ...themeOptions, lang }); // Q: Couldn't these regexes match on a user's inputted code blocks? // A: Nope! All rendered HTML is properly escaped. diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index 4abcf578d..7038e2425 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -42,6 +42,7 @@ export type RemarkRehype = Omit<RemarkRehypeOptions, 'handlers' | 'unknownHandle export interface ShikiConfig { langs?: LanguageRegistration[]; theme?: BuiltinTheme | ThemeRegistration | ThemeRegistrationRaw; + experimentalThemes?: Record<string, BuiltinTheme | ThemeRegistration | ThemeRegistrationRaw>; wrap?: boolean | null; } diff --git a/packages/markdown/remark/test/shiki.js b/packages/markdown/remark/test/shiki.js index cc5c6b771..c7ace6187 100644 --- a/packages/markdown/remark/test/shiki.js +++ b/packages/markdown/remark/test/shiki.js @@ -1,12 +1,30 @@ import { createMarkdownProcessor } from '../dist/index.js'; import chai from 'chai'; -describe('shiki syntax highlighting', async () => { - const processor = await createMarkdownProcessor(); - +describe('shiki syntax highlighting', () => { it('does not add is:raw to the output', async () => { + const processor = await createMarkdownProcessor(); const { code } = await processor.render('```\ntest\n```'); chai.expect(code).not.to.contain('is:raw'); }); + + it('supports light/dark themes', async () => { + const processor = await createMarkdownProcessor({ + shikiConfig: { + experimentalThemes: { + light: 'github-light', + dark: 'github-dark', + }, + }, + }); + const { code } = await processor.render('```\ntest\n```'); + + // light theme is there: + chai.expect(code).to.contain('background-color:'); + chai.expect(code).to.contain('github-light'); + // dark theme is there: + chai.expect(code).to.contain('--shiki-dark-bg:'); + chai.expect(code).to.contain('github-dark'); + }); }); |