Skip to content

Commit

Permalink
Refactor mdx remark plugins (#8430)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy committed Sep 7, 2023
1 parent 0fa4832 commit f3f62a5
Show file tree
Hide file tree
Showing 12 changed files with 52 additions and 241 deletions.
5 changes: 5 additions & 0 deletions .changeset/cuddly-baboons-begin.md
@@ -0,0 +1,5 @@
---
'@astrojs/markdown-remark': minor
---

Export remarkShiki and remarkPrism plugins
5 changes: 5 additions & 0 deletions .changeset/stupid-olives-push.md
@@ -0,0 +1,5 @@
---
'@astrojs/mdx': patch
---

Use exported remarkShiki and remarkPrism plugins from `@astrojs/markdown-remark`
3 changes: 0 additions & 3 deletions packages/integrations/mdx/package.json
Expand Up @@ -35,7 +35,6 @@
},
"dependencies": {
"@astrojs/markdown-remark": "workspace:*",
"@astrojs/prism": "workspace:*",
"@mdx-js/mdx": "^2.3.0",
"acorn": "^8.10.0",
"es-module-lexer": "^1.3.0",
Expand All @@ -45,10 +44,8 @@
"hast-util-to-html": "^8.0.4",
"kleur": "^4.1.4",
"rehype-raw": "^6.1.1",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1",
"remark-smartypants": "^2.0.0",
"shiki": "^0.14.3",
"source-map": "^0.7.4",
"unist-util-visit": "^4.1.2",
"vfile": "^5.3.7"
Expand Down
11 changes: 7 additions & 4 deletions packages/integrations/mdx/src/plugins.ts
@@ -1,4 +1,9 @@
import { rehypeHeadingIds, remarkCollectImages } from '@astrojs/markdown-remark';
import {
rehypeHeadingIds,
remarkCollectImages,
remarkPrism,
remarkShiki,
} from '@astrojs/markdown-remark';
import {
InvalidAstroDataError,
safelyGetAstroData,
Expand All @@ -16,8 +21,6 @@ import { rehypeInjectHeadingsExport } from './rehype-collect-headings.js';
import rehypeMetaString from './rehype-meta-string.js';
import { rehypeOptimizeStatic } from './rehype-optimize-static.js';
import { remarkImageToComponent } from './remark-images-to-component.js';
import remarkPrism from './remark-prism.js';
import remarkShiki from './remark-shiki.js';
import { jsToTreeNode } from './utils.js';

// Skip nonessential plugins during performance benchmark runs
Expand Down Expand Up @@ -112,7 +115,7 @@ export async function getRemarkPlugins(mdxOptions: MdxOptions): Promise<Pluggabl
if (!isPerformanceBenchmark) {
// Apply syntax highlighters after user plugins to match `markdown/remark` behavior
if (mdxOptions.syntaxHighlight === 'shiki') {
remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
remarkPlugins.push([remarkShiki, mdxOptions.shikiConfig]);
}
if (mdxOptions.syntaxHighlight === 'prism') {
remarkPlugins.push(remarkPrism);
Expand Down
18 changes: 0 additions & 18 deletions packages/integrations/mdx/src/remark-prism.ts

This file was deleted.

94 changes: 0 additions & 94 deletions packages/integrations/mdx/src/remark-shiki.ts

This file was deleted.

18 changes: 7 additions & 11 deletions packages/markdown/remark/src/index.ts
Expand Up @@ -9,9 +9,8 @@ import { toRemarkInitializeAstroData } from './frontmatter-injection.js';
import { loadPlugins } from './load-plugins.js';
import { rehypeHeadingIds } from './rehype-collect-headings.js';
import { remarkCollectImages } from './remark-collect-images.js';
import remarkPrism from './remark-prism.js';
import scopedStyles from './remark-scoped-styles.js';
import remarkShiki from './remark-shiki.js';
import { remarkPrism } from './remark-prism.js';
import { remarkShiki } from './remark-shiki.js';

import rehypeRaw from 'rehype-raw';
import rehypeStringify from 'rehype-stringify';
Expand All @@ -25,6 +24,8 @@ import { rehypeImages } from './rehype-images.js';

export { rehypeHeadingIds } from './rehype-collect-headings.js';
export { remarkCollectImages } from './remark-collect-images.js';
export { remarkPrism } from './remark-prism.js';
export { remarkShiki } from './remark-shiki.js';
export * from './types.js';

export const markdownConfigDefaults: Omit<Required<AstroMarkdownOptions>, 'drafts'> = {
Expand Down Expand Up @@ -61,7 +62,6 @@ export async function renderMarkdown(
frontmatter: userFrontmatter = {},
} = opts;
const input = new VFile({ value: content, path: fileURL });
const scopedClassName = opts.$?.scopedClassName;

let parser = unified()
.use(markdown)
Expand All @@ -85,18 +85,14 @@ export async function renderMarkdown(
});

if (!isPerformanceBenchmark) {
if (scopedClassName) {
parser.use([scopedStyles(scopedClassName)]);
}

if (syntaxHighlight === 'shiki') {
parser.use([await remarkShiki(shikiConfig, scopedClassName)]);
parser.use(remarkShiki, shikiConfig);
} else if (syntaxHighlight === 'prism') {
parser.use([remarkPrism(scopedClassName)]);
parser.use(remarkPrism);
}

// Apply later in case user plugins resolve relative image paths
parser.use([remarkCollectImages]);
parser.use(remarkCollectImages);
}

parser.use([
Expand Down
20 changes: 4 additions & 16 deletions packages/markdown/remark/src/remark-prism.ts
@@ -1,31 +1,19 @@
import { runHighlighterWithAstro } from '@astrojs/prism/dist/highlighter';
import { visit } from 'unist-util-visit';
import type { RemarkPlugin } from './types.js';

type MaybeString = string | null | undefined;

/** */
function transformer(className: MaybeString) {
export function remarkPrism(): ReturnType<RemarkPlugin> {
return function (tree: any) {
const visitor = (node: any) => {
visit(tree, 'code', (node) => {
let { lang, value } = node;
node.type = 'html';

let { html, classLanguage } = runHighlighterWithAstro(lang, value);
let classes = [classLanguage];
if (className) {
classes.push(className);
}
node.value = `<pre class="${classes.join(
' '
)}"><code is:raw class="${classLanguage}">${html}</code></pre>`;
return node;
};
return visit(tree, 'code', visitor);
});
};
}

function plugin(className: MaybeString) {
return transformer.bind(null, className);
}

export default plugin;
18 changes: 0 additions & 18 deletions packages/markdown/remark/src/remark-scoped-styles.ts

This file was deleted.

46 changes: 22 additions & 24 deletions packages/markdown/remark/src/remark-shiki.ts
@@ -1,7 +1,7 @@
import type * as shiki from 'shiki';
import { getHighlighter } from 'shiki';
import { visit } from 'unist-util-visit';
import type { ShikiConfig } from './types.js';
import type { RemarkPlugin, ShikiConfig } from './types.js';

/**
* getHighlighter() is the most expensive step of Shiki. Instead of calling it on every page,
Expand All @@ -10,10 +10,11 @@ import type { ShikiConfig } from './types.js';
*/
const highlighterCacheAsync = new Map<string, Promise<shiki.Highlighter>>();

const remarkShiki = async (
{ langs = [], theme = 'github-dark', wrap = false }: ShikiConfig,
scopedClassName?: string | null
) => {
export function remarkShiki({
langs = [],
theme = 'github-dark',
wrap = false,
}: ShikiConfig = {}): ReturnType<RemarkPlugin> {
const cacheID: string = typeof theme === 'string' ? theme : theme.name;
let highlighterAsync = highlighterCacheAsync.get(cacheID);
if (!highlighterAsync) {
Expand All @@ -35,15 +36,22 @@ const remarkShiki = async (
});
highlighterCacheAsync.set(cacheID, highlighterAsync);
}
const highlighter = await highlighterAsync;

// NOTE: There may be a performance issue here for large sites that use `lang`.
// Since this will be called on every page load. Unclear how to fix this.
for (const lang of langs) {
await highlighter.loadLanguage(lang);
}
let highlighter: shiki.Highlighter;

return async (tree: any) => {
// Lazily assign the highlighter as async can only happen within this function,
// and not on `remarkShiki` directly.
if (!highlighter) {
highlighter = await highlighterAsync!;

// NOTE: There may be a performance issue here for large sites that use `lang`.
// Since this will be called on every page load. Unclear how to fix this.
for (const lang of langs) {
await highlighter.loadLanguage(lang);
}
}

return () => (tree: any) => {
visit(tree, 'code', (node) => {
let lang: string;

Expand All @@ -69,10 +77,7 @@ const remarkShiki = async (
// &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="$1astro-code$2${scopedClassName ? ' ' + scopedClassName : ''}"`
);
html = html.replace(/<pre class="(.*?)shiki(.*?)"/, `<pre is:raw class="$1astro-code$2"`);
// Add "user-select: none;" for "+"/"-" diff symbols
if (node.lang === 'diff') {
html = html.replace(
Expand All @@ -91,16 +96,9 @@ const remarkShiki = async (
);
}

// 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 = [];
});
};
};

export default remarkShiki;
}
4 changes: 0 additions & 4 deletions packages/markdown/remark/src/types.ts
Expand Up @@ -61,10 +61,6 @@ export interface ImageMetadata {
export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
/** @internal */
fileURL?: URL;
/** @internal */
$?: {
scopedClassName: string | null;
};
/** Used for frontmatter injection plugins */
frontmatter?: Record<string, any>;
}
Expand Down

0 comments on commit f3f62a5

Please sign in to comment.