Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor mdx remark plugins #8430

Merged
merged 3 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cuddly-baboons-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/markdown-remark': minor
---

Export remarkShiki and remarkPrism plugins
5 changes: 5 additions & 0 deletions .changeset/stupid-olives-push.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this since it seems to only been used for Astro-flavoured markdown, which we don't use anymore.


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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Loading
Loading