0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-01-06 22:10:10 -05:00

Add Prism syntax highlighting (#1598)

This commit is contained in:
Matthew Phillips 2021-10-19 16:01:43 -04:00 committed by Drew Powers
parent a1c594fc95
commit b695c8aa15
8 changed files with 85 additions and 8 deletions

1
packages/astro-prism/index.d.ts vendored Normal file
View file

@ -0,0 +1 @@
export function addAstro(Prism: any): void;

View file

@ -15,6 +15,7 @@
"exports": { "exports": {
".": "./index.mjs" ".": "./index.mjs"
}, },
"types": "./index.d.ts",
"keywords": [], "keywords": [],
"author": "Skypack", "author": "Skypack",
"license": "MIT", "license": "MIT",

View file

@ -46,7 +46,7 @@ describe('Astro Markdown', () => {
}); });
// This doesn't work because the markdown plugin doesn't have Prism support yet. // This doesn't work because the markdown plugin doesn't have Prism support yet.
it.skip('Runs code blocks through syntax highlighter', async () => { it('Runs code blocks through syntax highlighter', async () => {
const html = await fixture.readFile('/code/index.html'); const html = await fixture.readFile('/code/index.html');
const $ = cheerio.load(html); const $ = cheerio.load(html);

View file

@ -18,13 +18,15 @@
"dev": "astro-scripts dev \"src/**/*.ts\"" "dev": "astro-scripts dev \"src/**/*.ts\""
}, },
"dependencies": { "dependencies": {
"@astrojs/prism": "^0.2.2",
"@silvenon/remark-smartypants": "^1.0.0", "@silvenon/remark-smartypants": "^1.0.0",
"assert": "^2.0.0", "assert": "^2.0.0",
"github-slugger": "^1.3.0", "github-slugger": "^1.3.0",
"mdast-util-mdx-expression": "^1.1.0", "mdast-util-mdx-expression": "^1.1.0",
"mdast-util-mdx-jsx": "^1.1.0",
"micromark-extension-mdx-expression": "^1.0.0", "micromark-extension-mdx-expression": "^1.0.0",
"micromark-extension-mdx-jsx": "^1.0.0", "micromark-extension-mdx-jsx": "^1.0.0",
"mdast-util-mdx-jsx": "^1.1.0", "prismjs": "^1.25.0",
"rehype-raw": "^6.0.0", "rehype-raw": "^6.0.0",
"rehype-stringify": "^9.0.1", "rehype-stringify": "^9.0.1",
"remark-footnotes": "^4.0.1", "remark-footnotes": "^4.0.1",
@ -40,6 +42,7 @@
"//": "Important that gray-matter is in devDependencies so it gets bundled by esbuild!", "//": "Important that gray-matter is in devDependencies so it gets bundled by esbuild!",
"devDependencies": { "devDependencies": {
"@types/github-slugger": "^1.3.0", "@types/github-slugger": "^1.3.0",
"@types/prismjs": "^1.16.6",
"gray-matter": "^4.0.3" "gray-matter": "^4.0.3"
} }
} }

View file

@ -7,6 +7,7 @@ import rehypeExpressions from './rehype-expressions.js';
import { remarkJsx, loadRemarkJsx } from './remark-jsx.js'; import { remarkJsx, loadRemarkJsx } from './remark-jsx.js';
import rehypeJsx from './rehype-jsx.js'; import rehypeJsx from './rehype-jsx.js';
//import { remarkCodeBlock } from './codeblock.js'; //import { remarkCodeBlock } from './codeblock.js';
import remarkPrism from './remark-prism.js';
import { loadPlugins } from './load-plugins.js'; import { loadPlugins } from './load-plugins.js';
import { unified } from 'unified'; import { unified } from 'unified';
@ -46,12 +47,13 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
.use(markdown) .use(markdown)
.use([remarkJsx]) .use([remarkJsx])
.use([remarkExpressions]) .use([remarkExpressions])
.use([remarkPrism])
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins)); const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins)); const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
loadedRemarkPlugins.forEach(([plugin, opts]) => { loadedRemarkPlugins.forEach(([plugin, opts]) => {
parser.use(plugin, opts); parser.use([[plugin, opts]]);
}); });
// if (scopedClassName) { // if (scopedClassName) {
@ -59,18 +61,18 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
// } // }
//parser.use(remarkCodeBlock); //parser.use(remarkCodeBlock);
parser.use(markdownToHtml, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement']}); parser.use([[markdownToHtml as any, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement']}]]);
loadedRehypePlugins.forEach(([plugin, opts]) => { loadedRehypePlugins.forEach(([plugin, opts]) => {
parser.use(plugin, opts); parser.use([[plugin, opts]]);
}); });
parser.use(rehypeJsx).use(rehypeExpressions) parser.use([rehypeJsx]).use(rehypeExpressions)
let result: string; let result: string;
try { try {
const vfile = await parser const vfile = await parser
.use(rehypeCollectHeaders) .use([rehypeCollectHeaders])
.use(rehypeStringify, { allowParseErrors: true, preferUnquoted: true, allowDangerousHtml: true }) .use(rehypeStringify, { allowParseErrors: true, preferUnquoted: true, allowDangerousHtml: true })
.process(content); .process(content);
result = vfile.toString(); result = vfile.toString();

View file

@ -0,0 +1,65 @@
import { visit } from 'unist-util-visit';
import Prism from 'prismjs';
import { addAstro } from '@astrojs/prism';
import loadLanguages from 'prismjs/components/index.js';
const noVisit = new Set(['root', 'html', 'text']);
const languageMap = new Map([
['ts', 'typescript']
]);
function runHighlighter(lang: string, code: string) {
let classLanguage = `language-${lang}`
if (lang == null) {
console.warn('Prism.astro: No language provided.');
}
const ensureLoaded = (lang: string) => {
if(lang && !Prism.languages[lang]) {
loadLanguages([lang]);
}
};
if(languageMap.has(lang)) {
ensureLoaded(languageMap.get(lang)!);
} else if(lang === 'astro') {
ensureLoaded('typescript');
addAstro(Prism);
} else {
ensureLoaded('markup-templating'); // Prism expects this to exist for a number of other langs
ensureLoaded(lang);
}
if(lang && !Prism.languages[lang]) {
console.warn(`Unable to load the language: ${lang}`);
}
const grammar = Prism.languages[lang];
let html = code;
if (grammar) {
html = Prism.highlight(code, grammar, lang);
}
return { classLanguage, html };
}
/** */
function transformer(tree: any) {
const visitor = (node: any) => {
let {lang, value} = node;
node.type = 'html';
let { html, classLanguage } = runHighlighter(lang, value);
node.value = `<pre class="${classLanguage}"><code class="${classLanguage}">${html}</code></pre>`;
return node;
};
return visit(tree, 'code', visitor)
}
function plugin() {
return transformer;
}
export default plugin;

View file

@ -1963,6 +1963,11 @@
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.1.tgz#f8ae4fbcd2b9ba4ff934698e28778961f9cb22ca" resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.1.tgz#f8ae4fbcd2b9ba4ff934698e28778961f9cb22ca"
integrity sha512-ARATsLdrGPUnaBvxLhUlnltcMgn7pQG312S8ccdYlnyijabrX9RN/KN/iGj9Am96CoW8e/K9628BA7Bv4XHdrA== integrity sha512-ARATsLdrGPUnaBvxLhUlnltcMgn7pQG312S8ccdYlnyijabrX9RN/KN/iGj9Am96CoW8e/K9628BA7Bv4XHdrA==
"@types/prismjs@^1.16.6":
version "1.16.6"
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.16.6.tgz#377054f72f671b36dbe78c517ce2b279d83ecc40"
integrity sha512-dTvnamRITNqNkqhlBd235kZl3KfVJQQoT5jkXeiWSBK7i4/TLKBNLV0S1wOt8gy4E2TY722KLtdmv2xc6+Wevg==
"@types/prompts@^2.0.12": "@types/prompts@^2.0.12":
version "2.0.14" version "2.0.14"
resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.0.14.tgz#10cb8899844bb0771cabe57c1becaaaca9a3b521" resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.0.14.tgz#10cb8899844bb0771cabe57c1becaaaca9a3b521"
@ -8782,7 +8787,7 @@ pretty-hrtime@^1.0.3:
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
prismjs@^1.23.0: prismjs@^1.23.0, prismjs@^1.25.0:
version "1.25.0" version "1.25.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756"
integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg== integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==