mirror of
https://github.com/withastro/astro.git
synced 2025-01-27 22:19:04 -05:00
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;
|
$scope: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { content, $scope } = Astro.props as InternalProps;
|
let { content, class: className } = Astro.props as InternalProps;
|
||||||
let html = null;
|
let html = null;
|
||||||
|
|
||||||
// If no content prop provided, use the slot.
|
// If no content prop provided, use the slot.
|
||||||
|
@ -23,7 +23,7 @@ if(!content) {
|
||||||
const { code: htmlContent } = await renderMarkdown(content, {
|
const { code: htmlContent } = await renderMarkdown(content, {
|
||||||
mode: 'md',
|
mode: 'md',
|
||||||
$: {
|
$: {
|
||||||
scopedClassName: $scope
|
scopedClassName: className
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,6 @@ describe('Astro Markdown', () => {
|
||||||
expect($('pre')[1].children).to.have.lengthOf(0);
|
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 () => {
|
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);
|
||||||
|
@ -54,13 +53,13 @@ describe('Astro Markdown', () => {
|
||||||
expect($('code span').length).greaterThan(0);
|
expect($('code span').length).greaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Blocked by lack of syntax highlighting
|
it('Scoped styles should not break syntax highlight', async () => {
|
||||||
it.skip('Scoped styles should not break syntax highlight', async () => {
|
|
||||||
const html = await fixture.readFile('/scopedStyles-code/index.html');
|
const html = await fixture.readFile('/scopedStyles-code/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
// test 1: <pre> tag has scopedStyle class passed down
|
// test 1: <pre> tag has scopedStyle class passed down
|
||||||
expect($('pre').is('[class]')).to.equal(true);
|
expect($('pre').is('[class]')).to.equal(true);
|
||||||
|
expect($('pre').attr('class').split(' ').length).to.equal(2)
|
||||||
|
|
||||||
// test 2: <pre> tag has correct language
|
// test 2: <pre> tag has correct language
|
||||||
expect($('pre').hasClass('language-js')).to.equal(true);
|
expect($('pre').hasClass('language-js')).to.equal(true);
|
||||||
|
@ -69,7 +68,7 @@ describe('Astro Markdown', () => {
|
||||||
expect($('code').hasClass('language-js')).to.equal(true);
|
expect($('code').hasClass('language-js')).to.equal(true);
|
||||||
|
|
||||||
// test 4: There are child spans in code blocks
|
// 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 () => {
|
it('Renders correctly when deeply nested on a page', async () => {
|
||||||
|
@ -90,9 +89,8 @@ describe('Astro Markdown', () => {
|
||||||
expect($('.c > h2').text()).to.equal('C');
|
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');
|
const html = await fixture.readFile('/external/index.html');
|
||||||
console.log(html)
|
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
// test 1: Rendered markdown content
|
// test 1: Rendered markdown content
|
||||||
|
|
|
@ -5,7 +5,10 @@ const outer = `# Outer`;
|
||||||
const inner = `## Inner`;
|
const inner = `## Inner`;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div>
|
<style>
|
||||||
|
#root { color: green; }
|
||||||
|
</style>
|
||||||
|
<div id="root">
|
||||||
<Markdown content={outer} />
|
<Markdown content={outer} />
|
||||||
|
|
||||||
<Markdown>
|
<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 */
|
/** Shared utility for rendering markdown */
|
||||||
export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) {
|
export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) {
|
||||||
const { remarkPlugins = DEFAULT_REMARK_PLUGINS, rehypePlugins = DEFAULT_REHYPE_PLUGINS } = opts ?? {};
|
const { remarkPlugins = DEFAULT_REMARK_PLUGINS, rehypePlugins = DEFAULT_REHYPE_PLUGINS } = opts ?? {};
|
||||||
|
const scopedClassName = opts?.$?.scopedClassName;
|
||||||
const { headers, rehypeCollectHeaders } = createCollectHeaders();
|
const { headers, rehypeCollectHeaders } = createCollectHeaders();
|
||||||
|
|
||||||
await Promise.all([loadRemarkExpressions(), loadRemarkJsx()]); // Vite bug: dynamically import() these because of CJS interop (this will cache)
|
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(markdown)
|
||||||
.use([remarkJsx])
|
.use([remarkJsx])
|
||||||
.use([remarkExpressions])
|
.use([remarkExpressions])
|
||||||
.use([remarkPrism])
|
.use([remarkPrism(scopedClassName)])
|
||||||
|
|
||||||
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));
|
||||||
|
@ -56,9 +57,9 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
|
||||||
parser.use([[plugin, opts]]);
|
parser.use([[plugin, opts]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// if (scopedClassName) {
|
if (scopedClassName) {
|
||||||
// parser.use(scopedStyles(scopedClassName));
|
parser.use([scopedStyles(scopedClassName)]);
|
||||||
// }
|
}
|
||||||
|
|
||||||
//parser.use(remarkCodeBlock);
|
//parser.use(remarkCodeBlock);
|
||||||
parser.use([[markdownToHtml as any, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement']}]]);
|
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 };
|
return { classLanguage, html };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
type MaybeString = string | null | undefined;
|
||||||
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>`;
|
function transformer(className: MaybeString) {
|
||||||
return node;
|
return function(tree: any) {
|
||||||
};
|
const visitor = (node: any) => {
|
||||||
return visit(tree, 'code', visitor)
|
let {lang, value} = node;
|
||||||
|
node.type = 'html';
|
||||||
|
|
||||||
|
let { html, classLanguage } = runHighlighter(lang, value);
|
||||||
|
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() {
|
function plugin(className: MaybeString) {
|
||||||
return transformer;
|
return transformer.bind(null, className);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default plugin;
|
export default plugin;
|
Loading…
Add table
Reference in a new issue