summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/astro/test/astro-markdown.test.js36
-rw-r--r--packages/markdown/remark/src/index.ts8
-rw-r--r--packages/markdown/remark/src/remark-shiki.ts15
-rw-r--r--packages/markdown/remark/src/types.ts2
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:
+ // &lt;span class=&quot;line&quot;
+
// 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[];