mirror of
https://github.com/withastro/astro.git
synced 2025-01-06 22:10:10 -05:00
Support <Code inline />
to output inline code (#6959)
* Support `<Code inline />` to output inline code * Fix typo * Fix typo again * Remove expected error --------- Co-authored-by: Matthew Phillips <matthew@skypack.dev>
This commit is contained in:
parent
6916e5c79f
commit
cac4a321e8
4 changed files with 71 additions and 21 deletions
5
.changeset/nine-geckos-act.md
Normal file
5
.changeset/nine-geckos-act.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Support `<Code inline />` to output inline code HTML (no `pre` tag)
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
import type * as shiki from 'shiki';
|
import type * as shiki from 'shiki';
|
||||||
|
import { renderToHtml } from 'shiki';
|
||||||
import { getHighlighter } from './Shiki.js';
|
import { getHighlighter } from './Shiki.js';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -30,36 +31,60 @@ export interface Props {
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
wrap?: boolean | null;
|
wrap?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Generate inline code element only, without the pre element wrapper.
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
inline?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { code, lang = 'plaintext', theme = 'github-dark', wrap = false } = Astro.props;
|
const {
|
||||||
|
code,
|
||||||
/** Replace the shiki class name with a custom astro class name. */
|
lang = 'plaintext',
|
||||||
function repairShikiTheme(html: string): string {
|
theme = 'github-dark',
|
||||||
// Replace "shiki" class naming with "astro"
|
wrap = false,
|
||||||
html = html.replace(/<pre class="(.*?)shiki(.*?)"/, '<pre class="$1astro-code$2"');
|
inline = false,
|
||||||
// Handle code wrapping
|
} = Astro.props;
|
||||||
// if wrap=null, do nothing.
|
|
||||||
if (wrap === false) {
|
|
||||||
html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto;"');
|
|
||||||
} else if (wrap === true) {
|
|
||||||
html = html.replace(
|
|
||||||
/style="(.*?)"/,
|
|
||||||
'style="$1; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 1. Get the shiki syntax highlighter
|
||||||
const highlighter = await getHighlighter({
|
const highlighter = await getHighlighter({
|
||||||
theme,
|
theme,
|
||||||
// Load custom lang if passed an object, otherwise load the default
|
// Load custom lang if passed an object, otherwise load the default
|
||||||
langs: typeof lang !== 'string' ? [lang] : undefined,
|
langs: typeof lang !== 'string' ? [lang] : undefined,
|
||||||
});
|
});
|
||||||
const _html = highlighter.codeToHtml(code, {
|
|
||||||
lang: typeof lang === 'string' ? lang : lang.id,
|
// 2. Turn code into shiki theme tokens
|
||||||
|
const tokens = highlighter.codeToThemedTokens(code, typeof lang === 'string' ? lang : lang.id);
|
||||||
|
|
||||||
|
// 3. Get shiki theme object
|
||||||
|
const _theme = highlighter.getTheme();
|
||||||
|
|
||||||
|
// 4. Render the theme tokens as html
|
||||||
|
const html = renderToHtml(tokens, {
|
||||||
|
themeName: _theme.name,
|
||||||
|
fg: _theme.fg,
|
||||||
|
bg: _theme.bg,
|
||||||
|
elements: {
|
||||||
|
pre({ className, style, children }) {
|
||||||
|
// Swap to `code` tag if inline
|
||||||
|
const tag = inline ? 'code' : 'pre';
|
||||||
|
// Replace "shiki" class naming with "astro-code"
|
||||||
|
className = className.replace(/shiki/g, 'astro-code');
|
||||||
|
// Handle code wrapping
|
||||||
|
// if wrap=null, do nothing.
|
||||||
|
if (wrap === false) {
|
||||||
|
style += '; overflow-x: auto;"';
|
||||||
|
} else if (wrap === true) {
|
||||||
|
style += '; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"';
|
||||||
|
}
|
||||||
|
return `<${tag} class="${className}" style="${style}" tabindex="0">${children}</${tag}>`;
|
||||||
|
},
|
||||||
|
code({ children }) {
|
||||||
|
return inline ? children : `<code>${children}</code>`;
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const html = repairShikiTheme(_html);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<Fragment set:html={html} />
|
<Fragment set:html={html} />
|
||||||
|
|
|
@ -100,4 +100,14 @@ describe('<Code>', () => {
|
||||||
expect($('#lang > pre')).to.have.lengthOf(1);
|
expect($('#lang > pre')).to.have.lengthOf(1);
|
||||||
expect($('#lang > pre > code span').length).to.equal(3);
|
expect($('#lang > pre > code span').length).to.equal(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('<Code inline> has no pre tag', async () => {
|
||||||
|
let html = await fixture.readFile('/inline/index.html');
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
const codeEl = $('.astro-code');
|
||||||
|
|
||||||
|
expect(codeEl.prop('tagName')).to.eq('CODE');
|
||||||
|
expect(codeEl.attr('style')).to.include('background-color:');
|
||||||
|
expect($('pre')).to.have.lengthOf(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
10
packages/astro/test/fixtures/astro-component-code/src/pages/inline.astro
vendored
Normal file
10
packages/astro/test/fixtures/astro-component-code/src/pages/inline.astro
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
import {Code} from 'astro/components';
|
||||||
|
---
|
||||||
|
<html>
|
||||||
|
<head><title>Code component</title></head>
|
||||||
|
<body>
|
||||||
|
Simple:
|
||||||
|
<Code code="console.log('inline code');" lang="js" inline />
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue