@atomiks/mdx-pretty-code
Advanced tools
Comparing version 0.0.5 to 0.1.0
@@ -367,4 +367,32 @@ import { JSDOM } from 'jsdom'; | ||
let highlighter = null; | ||
// To make sure we only have one highlighter per theme in a process | ||
const highlighterCache = new Map(); | ||
function getThemesFromSettings(settings) { | ||
if ( | ||
typeof settings.theme === 'string' || | ||
settings.theme?.hasOwnProperty('tokenColors') | ||
) { | ||
return {default: settings.theme}; | ||
} | ||
return settings.theme; | ||
} | ||
function highlightersFromSettings(settings) { | ||
const themes = getThemesFromSettings(settings); | ||
return Promise.all( | ||
Object.keys(themes).map(async (key) => { | ||
const theme = themes[key]; | ||
const highlighter = await shiki.getHighlighter({ | ||
...settings, | ||
theme, | ||
}); | ||
highlighter.themeKey = key; | ||
return highlighter; | ||
}) | ||
); | ||
} | ||
function createRemarkPlugin(options = {}) { | ||
@@ -375,4 +403,10 @@ return () => async (tree) => { | ||
allowedAttributes: { | ||
code: ['style', 'data-language'], | ||
span: ['data-color', 'data-mdx-pretty-code', 'style', 'class'], | ||
code: ['style', 'data-language', 'data-theme'], | ||
span: [ | ||
'data-color', | ||
'data-mdx-pretty-code', | ||
'data-theme', | ||
'style', | ||
'class', | ||
], | ||
}, | ||
@@ -388,12 +422,46 @@ }, | ||
if (!highlighter) { | ||
highlighter = await shiki.getHighlighter(shikiOptions); | ||
if (!highlighterCache.has(shikiOptions)) { | ||
highlighterCache.set( | ||
shikiOptions, | ||
highlightersFromSettings(shikiOptions) | ||
); | ||
} | ||
const loadedLanguages = highlighter.getLoadedLanguages(); | ||
const highlighters = await highlighterCache.get(shikiOptions); | ||
const themes = getThemesFromSettings(shikiOptions); | ||
visit(tree, 'inlineCode', inlineCode); | ||
visit(tree, 'code', blockCode); | ||
function remarkVisitor(fn) { | ||
return (node) => { | ||
let results; | ||
const hasMultipleThemes = highlighters.length > 1; | ||
const output = highlighters.map((highlighter, i) => { | ||
const loadedLanguages = highlighter.getLoadedLanguages(); | ||
function inlineCode(node) { | ||
const theme = themes[highlighter.themeKey]; | ||
return fn(node, { | ||
highlighter, | ||
theme, | ||
loadedLanguages, | ||
hasMultipleThemes, | ||
}); | ||
}); | ||
// return in case of non-meta inline code | ||
if (output.some((o) => !o)) return; | ||
results = output.join('\n'); | ||
if (hasMultipleThemes) { | ||
// If we don't do this the code blocks will be wrapped in <undefined> | ||
// You can set your mdx renderer to replace this span with a React.Fragment during runtime | ||
results = `<span data-mdx-pretty-code-fragment>${results}</span>`; | ||
} | ||
node.value = results; | ||
}; | ||
} | ||
visit(tree, 'inlineCode', remarkVisitor(inlineCode)); | ||
visit(tree, 'code', remarkVisitor(blockCode)); | ||
function inlineCode(node, {highlighter, theme}) { | ||
const meta = node.value.match(/{:([a-zA-Z.-]+)}$/)?.[1]; | ||
@@ -406,6 +474,5 @@ | ||
node.type = 'html'; | ||
// It's a token, not a lang | ||
if (meta[0] === '.') { | ||
if (typeof shikiOptions.theme === 'string') { | ||
if (typeof theme === 'string') { | ||
throw new Error( | ||
@@ -417,15 +484,12 @@ 'MDX Pretty Code: Must be using a JSON theme object to use tokens.' | ||
const color = | ||
shikiOptions.theme.tokenColors.find(({scope}) => | ||
theme.tokenColors.find(({scope}) => | ||
scope?.includes(tokensMap[meta.slice(1)] ?? meta.slice(1)) | ||
)?.settings.foreground ?? 'inherit'; | ||
node.value = sanitizeHtml( | ||
`<span data-mdx-pretty-code data-color="${color}"><span>${node.value.replace( | ||
/{:[a-zA-Z.-]+}/, | ||
'' | ||
)}</span></span>`, | ||
return sanitizeHtml( | ||
`<span data-mdx-pretty-code data-color="${color}" data-theme="${ | ||
highlighter.themeKey | ||
}"><span>${node.value.replace(/{:[a-zA-Z.-]+}/, '')}</span></span>`, | ||
sanitizeOptions | ||
); | ||
return; | ||
} | ||
@@ -441,4 +505,4 @@ | ||
node.value = sanitizeHtml( | ||
`<span data-mdx-pretty-code>${pre.innerHTML}</span>`, | ||
return sanitizeHtml( | ||
`<span data-mdx-pretty-code data-theme="${highlighter.themeKey}">${pre.innerHTML}</span>`, | ||
sanitizeOptions | ||
@@ -448,3 +512,3 @@ ); | ||
function blockCode(node) { | ||
function blockCode(node, {highlighter, loadedLanguages}) { | ||
const lang = | ||
@@ -508,10 +572,7 @@ ignoreUnknownLanguage && !loadedLanguages.includes(node.lang) | ||
dom.window.document | ||
.querySelector('code') | ||
.setAttribute('data-language', lang); | ||
const code = dom.window.document.querySelector('code'); | ||
node.value = sanitizeHtml( | ||
dom.window.document.body.innerHTML, | ||
sanitizeOptions | ||
); | ||
code.setAttribute('data-language', lang); | ||
code.setAttribute('data-theme', highlighter.themeKey); | ||
return sanitizeHtml(dom.window.document.body.innerHTML, sanitizeOptions); | ||
} | ||
@@ -518,0 +579,0 @@ }; |
@@ -0,5 +1,7 @@ | ||
type theme = JSON | string; | ||
export type Options = { | ||
sanitizeOptions: any; | ||
shikiOptions: { | ||
theme: JSON | string; | ||
theme: theme | Record<any, theme>; | ||
[key: string]: any; | ||
@@ -6,0 +8,0 @@ }; |
{ | ||
"name": "@atomiks/mdx-pretty-code", | ||
"version": "0.0.5", | ||
"version": "0.1.0", | ||
"description": "A Remark plugin to make the code in your MDX docs simply beautiful. Powered by [Shiki](https://github.com/shikijs/shiki).", | ||
@@ -5,0 +5,0 @@ "main": "./dist/mdx-pretty-code.cjs", |
@@ -88,2 +88,82 @@ # MDX Pretty Code | ||
## Multiple themes (dark/light mode) | ||
Because Shiki generates themes at build time, client-side theme switching | ||
support is not built in. There are two popular options for supporting something | ||
like Dark Mode with Shiki. See the | ||
[Shiki docs](https://github.com/shikijs/shiki/blob/main/docs/themes.md#dark-mode-support) | ||
for more info. | ||
#### 1. Load multiple themes | ||
This will render duplicate code blocks for each theme. You can then hide the | ||
other blocks with CSS. | ||
Pass your themes to `shikiOptions.theme`, where the keys represent the color | ||
mode: | ||
```js | ||
shikiOptions: { | ||
theme: { | ||
dark: JSON.parse( | ||
fs.readFileSync(require.resolve('./themes/dark.json'), "utf-8") | ||
), | ||
light: JSON.parse( | ||
fs.readFileSync(require.resolve('./themes/light.json'), "utf-8") | ||
), | ||
}, | ||
} | ||
``` | ||
The `code` elements and the inline code `<span data-mdx-pretty-code>` wrappers | ||
will have a data attribute `data-theme="[key]"`, e.g `data-theme="light"`. You | ||
can target the data attribute `[data-theme='dark']` to apply styles for that | ||
theme. | ||
Now, you can use CSS to display the desired theme: | ||
```css | ||
@media (prefers-color-scheme: dark) { | ||
code[data-theme='light'] { | ||
display: none; | ||
} | ||
} | ||
@media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) { | ||
code[data-theme='dark'] { | ||
display: none; | ||
} | ||
} | ||
``` | ||
#### 2. Use the "css-variables" theme (Shiki version `0.9.9` and above) | ||
<details> | ||
This gives you access to CSS variable styling, which you can control across Dark | ||
and Light mode. | ||
Note that **this client-side theme is less granular than most other supported VS | ||
Code themes**. Also, be aware that this will generate unstyled code if you do | ||
not define these CSS variables somewhere else on your page: | ||
```html | ||
<style> | ||
:root { | ||
--shiki-color-text: rgb(248, 248, 242); | ||
--shiki-color-background: rgb(13 13 15); | ||
--shiki-token-constant: rgb(102, 217, 239); | ||
--shiki-token-string: rgb(230, 219, 116); | ||
--shiki-token-comment: rgb(93,93, 95); | ||
--shiki-token-keyword: rgb(249, 38, 114); | ||
--shiki-token-parameter: rgb(230, 219, 116); | ||
--shiki-token-function: rgb(166, 226, 46); | ||
--shiki-token-string-expression: rgb(230, 219, 116); | ||
--shiki-token-punctuation: rgb(230, 219, 116); | ||
--shiki-token-link: rgb(174, 129, 255); | ||
} | ||
</style> | ||
``` | ||
</details> | ||
## API | ||
@@ -133,3 +213,6 @@ | ||
return ( | ||
<code style={{color: props['data-color']}}> | ||
<code | ||
data-theme={props['data-theme']} | ||
style={{color: props['data-color']}} | ||
> | ||
{props.children.props.children} | ||
@@ -143,2 +226,3 @@ </code> | ||
}; | ||
``` | ||
@@ -145,0 +229,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
41255
1007
298