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": {
".": "./index.mjs"
},
"types": "./index.d.ts",
"keywords": [],
"author": "Skypack",
"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.
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 $ = cheerio.load(html);

View file

@ -18,13 +18,15 @@
"dev": "astro-scripts dev \"src/**/*.ts\""
},
"dependencies": {
"@astrojs/prism": "^0.2.2",
"@silvenon/remark-smartypants": "^1.0.0",
"assert": "^2.0.0",
"github-slugger": "^1.3.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-jsx": "^1.0.0",
"mdast-util-mdx-jsx": "^1.1.0",
"prismjs": "^1.25.0",
"rehype-raw": "^6.0.0",
"rehype-stringify": "^9.0.1",
"remark-footnotes": "^4.0.1",
@ -40,6 +42,7 @@
"//": "Important that gray-matter is in devDependencies so it gets bundled by esbuild!",
"devDependencies": {
"@types/github-slugger": "^1.3.0",
"@types/prismjs": "^1.16.6",
"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 rehypeJsx from './rehype-jsx.js';
//import { remarkCodeBlock } from './codeblock.js';
import remarkPrism from './remark-prism.js';
import { loadPlugins } from './load-plugins.js';
import { unified } from 'unified';
@ -46,12 +47,13 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
.use(markdown)
.use([remarkJsx])
.use([remarkExpressions])
.use([remarkPrism])
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
loadedRemarkPlugins.forEach(([plugin, opts]) => {
parser.use(plugin, opts);
parser.use([[plugin, opts]]);
});
// if (scopedClassName) {
@ -59,18 +61,18 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
// }
//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]) => {
parser.use(plugin, opts);
parser.use([[plugin, opts]]);
});
parser.use(rehypeJsx).use(rehypeExpressions)
parser.use([rehypeJsx]).use(rehypeExpressions)
let result: string;
try {
const vfile = await parser
.use(rehypeCollectHeaders)
.use([rehypeCollectHeaders])
.use(rehypeStringify, { allowParseErrors: true, preferUnquoted: true, allowDangerousHtml: true })
.process(content);
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"
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":
version "2.0.14"
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"
integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
prismjs@^1.23.0:
prismjs@^1.23.0, prismjs@^1.25.0:
version "1.25.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756"
integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==