diff options
Diffstat (limited to 'packages')
-rw-r--r-- | packages/astro/test/astro-markdown.test.js | 36 | ||||
-rw-r--r-- | packages/markdown/remark/src/index.ts | 8 | ||||
-rw-r--r-- | packages/markdown/remark/src/remark-shiki.ts | 15 | ||||
-rw-r--r-- | packages/markdown/remark/src/types.ts | 2 |
4 files changed, 45 insertions, 16 deletions
diff --git a/packages/astro/test/astro-markdown.test.js b/packages/astro/test/astro-markdown.test.js index 2bccf8d87..44315a510 100644 --- a/packages/astro/test/astro-markdown.test.js +++ b/packages/astro/test/astro-markdown.test.js @@ -57,18 +57,36 @@ describe('Astro Markdown', () => { const html = await fixture.readFile('/scopedStyles-code/index.html'); const $ = cheerio.load(html); - // test 1: <pre> tag has scopedStyle class passed down - expect($('pre').is('[class]')).to.equal(true); - expect($('pre').attr('class').split(' ').length).to.equal(2); + // test 1: <pre> tag has correct shiki class + expect($('pre').hasClass('astro-code')).to.equal(true); + + // test 2: inline styles are still applied + expect($('pre').is('[style]')).to.equal(true); + + // test 3: There are styled child spans in code blocks + expect($('pre code span').length).to.be.greaterThan(0); + expect($('pre code span').is('[style]')).to.equal(true); + }); - // test 2: <pre> tag has correct language - expect($('pre').hasClass('language-js')).to.equal(true); + function isAstroScopedClass(cls) { + return /^astro-.*/.test(cls) + } - // test 3: <code> tag has correct language - expect($('code').hasClass('language-js')).to.equal(true); + it('Scoped styles should be applied to syntax highlighted lines', async () => { + const html = await fixture.readFile('/scopedStyles-code/index.html'); + const $ = cheerio.load(html); - // test 4: There are child spans in code blocks - expect($('code span').length).to.be.greaterThan(0); + // test 1: the "pre" tag receives scoped style + const preClassList = $('pre').attr('class').split(/\s+/); + expect(preClassList.length).to.equal(2); + const preAstroClass = preClassList.find(isAstroScopedClass); + expect(Boolean(preAstroClass)).to.equal(true); + + // test 2: each "span" line receives scoped style + const spanClassList = $('pre code span').attr('class').split(/\s+/); + expect(spanClassList.length).to.equal(2); + const spanAstroClass = spanClassList.find(isAstroScopedClass); + expect(Boolean(spanAstroClass)).to.equal(true); }); it('Renders correctly when deeply nested on a page', async () => { diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index e8a315ef1..3ea436795 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -38,7 +38,7 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp let { remarkPlugins = [], rehypePlugins = [] } = opts ?? {}; const scopedClassName = opts?.$?.scopedClassName; const mode = opts?.mode ?? 'mdx'; - const syntaxHighlight = opts?.syntaxHighlight ?? 'prism'; + const syntaxHighlight = opts?.syntaxHighlight ?? 'shiki'; const shikiConfig = opts?.shikiConfig ?? {}; const isMDX = mode === 'mdx'; const { headers, rehypeCollectHeaders } = createCollectHeaders(); @@ -67,10 +67,10 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp parser.use([scopedStyles(scopedClassName)]); } - if (syntaxHighlight === 'prism') { + if (syntaxHighlight === 'shiki') { + parser.use([await remarkShiki(shikiConfig, scopedClassName)]); + } else if (syntaxHighlight === 'prism') { parser.use([remarkPrism(scopedClassName)]); - } else if (syntaxHighlight === 'shiki') { - parser.use([await remarkShiki(shikiConfig)]); } parser.use([[markdownToHtml as any, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement'] }]]); diff --git a/packages/markdown/remark/src/remark-shiki.ts b/packages/markdown/remark/src/remark-shiki.ts index 5bee7ef6e..ebbe4032c 100644 --- a/packages/markdown/remark/src/remark-shiki.ts +++ b/packages/markdown/remark/src/remark-shiki.ts @@ -36,7 +36,7 @@ export interface ShikiConfig { */ const highlighterCache = new Map<string, shiki.Highlighter>(); -const remarkShiki = async ({ langs = [], theme = 'github-dark', wrap = false }: ShikiConfig) => { +const remarkShiki = async ({ langs = [], theme = 'github-dark', wrap = false }: ShikiConfig, scopedClassName?: string | null) => { const cacheID: string = typeof theme === 'string' ? theme : theme.name; let highlighter = highlighterCache.get(cacheID); if (!highlighter) { @@ -50,8 +50,14 @@ const remarkShiki = async ({ langs = [], theme = 'github-dark', wrap = false }: visit(tree, 'code', (node) => { let html = highlighter!.codeToHtml(node.value, { lang: node.lang ?? 'plaintext' }); + // Q: Couldn't these regexes match on a user's inputted code blocks? + // A: Nope! All rendered HTML is properly escaped. + // Ex. If a user typed `<span class="line"` into a code block, + // It would become this before hitting our regexes: + // <span class="line" + // Replace "shiki" class naming with "astro" and add "is:raw". - html = html.replace('<pre class="shiki"', '<pre is:raw class="astro-code"'); + html = html.replace('<pre class="shiki"', `<pre is:raw class="astro-code${scopedClassName ? ' ' + scopedClassName : ''}"`); // Replace "shiki" css variable naming with "astro". html = html.replace(/style="(background-)?color: var\(--shiki-/g, 'style="$1color: var(--astro-code-'); // Handle code wrapping @@ -62,6 +68,11 @@ const remarkShiki = async ({ langs = [], theme = 'github-dark', wrap = false }: html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"'); } + // Apply scopedClassName to all nested lines + if (scopedClassName) { + html = html.replace(/\<span class="line"\>/g, `<span class="line ${scopedClassName}"`); + } + node.type = 'html'; node.value = html; node.children = []; diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index 040c33b79..eb0eb5d3e 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -5,7 +5,7 @@ export type Plugin = string | [string, any] | unified.Plugin | [unified.Plugin, export interface AstroMarkdownOptions { mode?: 'md' | 'mdx'; - syntaxHighlight?: 'prism' | 'shiki' | false; + syntaxHighlight?: 'shiki' | 'prism' | false; shikiConfig?: ShikiConfig; remarkPlugins?: Plugin[]; rehypePlugins?: Plugin[]; |