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:
parent
a1c594fc95
commit
b695c8aa15
8 changed files with 85 additions and 8 deletions
1
packages/astro-prism/index.d.ts
vendored
Normal file
1
packages/astro-prism/index.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export function addAstro(Prism: any): void;
|
|
@ -15,6 +15,7 @@
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.mjs"
|
".": "./index.mjs"
|
||||||
},
|
},
|
||||||
|
"types": "./index.d.ts",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Skypack",
|
"author": "Skypack",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
65
packages/markdown/remark/src/remark-prism.ts
Normal file
65
packages/markdown/remark/src/remark-prism.ts
Normal 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;
|
|
@ -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==
|
||||||
|
|
Loading…
Reference in a new issue