Scoped styles with markdown (#1599)
This commit is contained in:
parent
b695c8aa15
commit
d1f42353e8
7 changed files with 56 additions and 39 deletions
|
@ -11,7 +11,7 @@ interface InternalProps extends Props {
|
|||
$scope: string;
|
||||
}
|
||||
|
||||
let { content, $scope } = Astro.props as InternalProps;
|
||||
let { content, class: className } = Astro.props as InternalProps;
|
||||
let html = null;
|
||||
|
||||
// If no content prop provided, use the slot.
|
||||
|
@ -23,7 +23,7 @@ if(!content) {
|
|||
const { code: htmlContent } = await renderMarkdown(content, {
|
||||
mode: 'md',
|
||||
$: {
|
||||
scopedClassName: $scope
|
||||
scopedClassName: className
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ describe('Astro Markdown', () => {
|
|||
expect($('pre')[1].children).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
// This doesn't work because the markdown plugin doesn't have Prism support yet.
|
||||
it('Runs code blocks through syntax highlighter', async () => {
|
||||
const html = await fixture.readFile('/code/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
@ -54,13 +53,13 @@ describe('Astro Markdown', () => {
|
|||
expect($('code span').length).greaterThan(0);
|
||||
});
|
||||
|
||||
// Blocked by lack of syntax highlighting
|
||||
it.skip('Scoped styles should not break syntax highlight', async () => {
|
||||
it('Scoped styles should not break syntax highlight', async () => {
|
||||
const html = await fixture.readFile('/scopedStyles-code/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// test 1: <pre> tag has scopedStyle class passed down
|
||||
expect($('pre').is('[class]')).to.equal(true);
|
||||
expect($('pre').attr('class').split(' ').length).to.equal(2)
|
||||
|
||||
// test 2: <pre> tag has correct language
|
||||
expect($('pre').hasClass('language-js')).to.equal(true);
|
||||
|
@ -69,7 +68,7 @@ describe('Astro Markdown', () => {
|
|||
expect($('code').hasClass('language-js')).to.equal(true);
|
||||
|
||||
// test 4: There are child spans in code blocks
|
||||
expect($('code span').length).toBeGreaterThan(0);
|
||||
expect($('code span').length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('Renders correctly when deeply nested on a page', async () => {
|
||||
|
@ -90,9 +89,8 @@ describe('Astro Markdown', () => {
|
|||
expect($('.c > h2').text()).to.equal('C');
|
||||
});
|
||||
|
||||
it.skip('Renders dynamic content though the content attribute', async () => {
|
||||
it('Renders dynamic content though the content attribute', async () => {
|
||||
const html = await fixture.readFile('/external/index.html');
|
||||
console.log(html)
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// test 1: Rendered markdown content
|
||||
|
|
|
@ -5,7 +5,10 @@ const outer = `# Outer`;
|
|||
const inner = `## Inner`;
|
||||
---
|
||||
|
||||
<div>
|
||||
<style>
|
||||
#root { color: green; }
|
||||
</style>
|
||||
<div id="root">
|
||||
<Markdown content={outer} />
|
||||
|
||||
<Markdown>
|
||||
|
|
21
packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
vendored
Normal file
21
packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import { Markdown } from 'astro/components';
|
||||
import Layout from '../layouts/content.astro';
|
||||
|
||||
---
|
||||
<style>
|
||||
#root {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
<Layout>
|
||||
<div id="root">
|
||||
<Markdown>
|
||||
## Interesting Topic
|
||||
|
||||
```js
|
||||
const thing = () => {};
|
||||
```
|
||||
</Markdown>
|
||||
</div>
|
||||
</Layout>
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
import { Markdown } from 'astro/components';
|
||||
import Layout from '../layouts/content.astro';
|
||||
|
||||
---
|
||||
<Layout>
|
||||
<Markdown>
|
||||
## Interesting Topic
|
||||
|
||||
```js
|
||||
const thing = () => {};
|
||||
```
|
||||
</Markdown>
|
||||
</Layout>
|
|
@ -39,6 +39,7 @@ export const DEFAULT_REHYPE_PLUGINS = [
|
|||
/** Shared utility for rendering markdown */
|
||||
export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) {
|
||||
const { remarkPlugins = DEFAULT_REMARK_PLUGINS, rehypePlugins = DEFAULT_REHYPE_PLUGINS } = opts ?? {};
|
||||
const scopedClassName = opts?.$?.scopedClassName;
|
||||
const { headers, rehypeCollectHeaders } = createCollectHeaders();
|
||||
|
||||
await Promise.all([loadRemarkExpressions(), loadRemarkJsx()]); // Vite bug: dynamically import() these because of CJS interop (this will cache)
|
||||
|
@ -47,7 +48,7 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
|
|||
.use(markdown)
|
||||
.use([remarkJsx])
|
||||
.use([remarkExpressions])
|
||||
.use([remarkPrism])
|
||||
.use([remarkPrism(scopedClassName)])
|
||||
|
||||
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
|
||||
const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
|
||||
|
@ -56,9 +57,9 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
|
|||
parser.use([[plugin, opts]]);
|
||||
});
|
||||
|
||||
// if (scopedClassName) {
|
||||
// parser.use(scopedStyles(scopedClassName));
|
||||
// }
|
||||
if (scopedClassName) {
|
||||
parser.use([scopedStyles(scopedClassName)]);
|
||||
}
|
||||
|
||||
//parser.use(remarkCodeBlock);
|
||||
parser.use([[markdownToHtml as any, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement']}]]);
|
||||
|
|
|
@ -44,22 +44,30 @@ function runHighlighter(lang: string, code: string) {
|
|||
return { classLanguage, html };
|
||||
}
|
||||
|
||||
type MaybeString = string | null | undefined;
|
||||
|
||||
/** */
|
||||
function transformer(tree: any) {
|
||||
function transformer(className: MaybeString) {
|
||||
return function(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>`;
|
||||
let classes = [classLanguage];
|
||||
if(className) {
|
||||
classes.push(className);
|
||||
}
|
||||
node.value = `<pre class="${classes.join(' ')}"><code class="${classLanguage}">${html}</code></pre>`;
|
||||
return node;
|
||||
};
|
||||
return visit(tree, 'code', visitor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function plugin() {
|
||||
return transformer;
|
||||
function plugin(className: MaybeString) {
|
||||
return transformer.bind(null, className);
|
||||
}
|
||||
|
||||
export default plugin;
|
Loading…
Reference in a new issue