* Fixes #1033 - Markdown with applied scopedStyles doesn't run Prism syntax highlight * Removed the incorrect lang attribute to indicate language syntax * Fixed UT for scopedStyles + markdown * Update packages/astro/test/astro-markdown.test.js Co-authored-by: Caleb Jasik <calebjasik@jasik.xyz> * Added changeset for PR #1037 * Update curly-queens-pay.md Changing from a `minor` release to a `patch` since we're pre-v1.0.0! This Co-authored-by: Vitor Calejuri <vitor.cajuleri@gmail.com> Co-authored-by: Caleb Jasik <calebjasik@jasik.xyz> Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
This commit is contained in:
parent
3f093fa830
commit
2321b577ee
6 changed files with 50 additions and 5 deletions
5
.changeset/curly-queens-pay.md
Normal file
5
.changeset/curly-queens-pay.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
- Allow Markdown with scoped styles to coexist happily with code syntax highlighting via Prism
|
|
@ -3,7 +3,8 @@ import Prism from 'prismjs';
|
||||||
import { addAstro } from '@astrojs/prism';
|
import { addAstro } from '@astrojs/prism';
|
||||||
import loadLanguages from 'prismjs/components/index.js';
|
import loadLanguages from 'prismjs/components/index.js';
|
||||||
|
|
||||||
const { lang, code } = Astro.props;
|
const { class: className, lang, code } = Astro.props;
|
||||||
|
let classLanguage = `language-${lang}`
|
||||||
|
|
||||||
const languageMap = new Map([
|
const languageMap = new Map([
|
||||||
['ts', 'typescript']
|
['ts', 'typescript']
|
||||||
|
@ -39,7 +40,6 @@ if (grammar) {
|
||||||
html = Prism.highlight(code, grammar, lang);
|
html = Prism.highlight(code, grammar, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
let className = lang ? `language-${lang}` : '';
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<pre class={className}><code class={className}>{html}</code></pre>
|
<pre class={className}><code class={classLanguage}>{html}</code></pre>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { getAttrValue } from '../../ast.js';
|
||||||
|
|
||||||
export const PRISM_IMPORT = `import Prism from 'astro/components/Prism.astro';`;
|
export const PRISM_IMPORT = `import Prism from 'astro/components/Prism.astro';`;
|
||||||
const prismImportExp = /import Prism from ['"]astro\/components\/Prism.astro['"]/;
|
const prismImportExp = /import Prism from ['"]astro\/components\/Prism.astro['"]/;
|
||||||
|
|
||||||
/** escaping code samples that contain template string replacement parts, ${foo} or example. */
|
/** escaping code samples that contain template string replacement parts, ${foo} or example. */
|
||||||
function escape(code: string) {
|
function escape(code: string) {
|
||||||
return code
|
return code
|
||||||
|
@ -22,6 +23,7 @@ function unescapeCode(code: TemplateNode) {
|
||||||
return child;
|
return child;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** default export - Transform prism */
|
/** default export - Transform prism */
|
||||||
export default function (module: Script): Transformer {
|
export default function (module: Script): Transformer {
|
||||||
let usesPrism = false;
|
let usesPrism = false;
|
||||||
|
@ -43,15 +45,17 @@ export default function (module: Script): Transformer {
|
||||||
const className = getAttrValue(codeEl.attributes, 'class') || '';
|
const className = getAttrValue(codeEl.attributes, 'class') || '';
|
||||||
const classes = className.split(' ');
|
const classes = className.split(' ');
|
||||||
|
|
||||||
let lang;
|
let lang: string | undefined;
|
||||||
for (let cn of classes) {
|
for (let cn of classes) {
|
||||||
const matches = /language-(.+)/.exec(cn);
|
const matches = /language-(.+)/.exec(cn);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
lang = matches[1];
|
lang = matches[1];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lang) return;
|
if (!lang) return;
|
||||||
|
let classesWithoutLang = classes.filter((cn) => cn !== `language-${lang}`);
|
||||||
|
|
||||||
let codeData = codeEl.children && codeEl.children[0];
|
let codeData = codeEl.children && codeEl.children[0];
|
||||||
if (!codeData) return;
|
if (!codeData) return;
|
||||||
|
@ -74,6 +78,17 @@ export default function (module: Script): Transformer {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'Attribute',
|
||||||
|
name: 'class',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'Text',
|
||||||
|
raw: classesWithoutLang.join(' '),
|
||||||
|
data: classesWithoutLang.join(' '),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'Attribute',
|
type: 'Attribute',
|
||||||
name: 'code',
|
name: 'code',
|
||||||
|
|
|
@ -35,6 +35,16 @@ Markdown('Runs code blocks through syntax highlighter', async ({ runtime }) => {
|
||||||
assert.ok($el.length > 0, 'There are child spans in code blocks');
|
assert.ok($el.length > 0, 'There are child spans in code blocks');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Markdown('Scoped styles should not break syntax highlight', async ({ runtime }) => {
|
||||||
|
const result = await runtime.load('/scopedStyles-code');
|
||||||
|
assert.ok(!result.error, `build error: ${result.error}`);
|
||||||
|
|
||||||
|
const $ = doc(result.contents);
|
||||||
|
assert.ok($('pre').is('[class]'), 'Pre tag has scopedStyle class passed down');
|
||||||
|
assert.ok($('code').hasClass('language-js'), 'Code tag has correct language');
|
||||||
|
assert.ok($('code span').length > 0, 'There are child spans in code blocks');
|
||||||
|
});
|
||||||
|
|
||||||
Markdown('Bundles client-side JS for prod', async (context) => {
|
Markdown('Bundles client-side JS for prod', async (context) => {
|
||||||
await context.build();
|
await context.build();
|
||||||
|
|
||||||
|
|
14
packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
vendored
Normal file
14
packages/astro/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
import { Markdown } from 'astro/components';
|
||||||
|
import Layout from '../layouts/content.astro';
|
||||||
|
|
||||||
|
---
|
||||||
|
<Markdown>
|
||||||
|
<Layout>
|
||||||
|
## Interesting Topic
|
||||||
|
|
||||||
|
```js
|
||||||
|
const thing = () => {};
|
||||||
|
```
|
||||||
|
</Layout>
|
||||||
|
</Markdown>
|
|
@ -4,7 +4,7 @@ import createCollectHeaders from './rehype-collect-headers.js';
|
||||||
import scopedStyles from './remark-scoped-styles.js';
|
import scopedStyles from './remark-scoped-styles.js';
|
||||||
import remarkExpressions from './remark-expressions.js';
|
import remarkExpressions from './remark-expressions.js';
|
||||||
import rehypeExpressions from './rehype-expressions.js';
|
import rehypeExpressions from './rehype-expressions.js';
|
||||||
import { rehypeCodeBlock } from './codeblock.js';
|
import { remarkCodeBlock, rehypeCodeBlock } from './codeblock.js';
|
||||||
import { loadPlugins } from './load-plugins.js';
|
import { loadPlugins } from './load-plugins.js';
|
||||||
import raw from 'rehype-raw';
|
import raw from 'rehype-raw';
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
|
||||||
parser.use(scopedStyles(scopedClassName));
|
parser.use(scopedStyles(scopedClassName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parser.use(remarkCodeBlock());
|
||||||
parser.use(markdownToHtml, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression'] });
|
parser.use(markdownToHtml, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression'] });
|
||||||
parser.use(rehypeExpressions);
|
parser.use(rehypeExpressions);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue