Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support multiple overridden syntax themes #1992

Merged
merged 16 commits into from
Aug 19, 2023
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = {
'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }],
'object-shorthand': ['error', 'always'],
'unicorn/prefer-regexp-test': 'error',
'unicorn/no-array-for-each': 'error',
'@typescript-eslint/prefer-for-of': 'error',
// todo: enable
'@typescript-eslint/no-explicit-any': 'off',
Expand Down
75 changes: 72 additions & 3 deletions docs/pages/docs/guide/syntax-highlighting.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { OptionTable } from 'components/table'
import { Callout } from 'nextra/components'

# Syntax Highlighting

Nextra uses [Shiki](https://shiki.matsu.io) to do syntax highlighting at build
time. Its very reliable and performant. For example, adding this in your
time. It's very reliable and performant. For example, adding this in your
Markdown file:

````md filename="Markdown"
Expand Down Expand Up @@ -182,7 +183,7 @@ Renders:
Check [this list](https://github.com/shikijs/shiki/blob/main/docs/languages.md)
for all supported languages.

## Customize The Theme
## Customize the Theme

Nextra uses CSS variables to define the colors for tokens. You can inject a
[global CSS](https://nextjs.org/docs/basic-features/built-in-css-support#adding-a-global-stylesheet)
Expand Down Expand Up @@ -213,7 +214,7 @@ tokens and you can override any of these:
--shiki-token-parameter: #ff9800;
--shiki-token-function: #b392f0;
--shiki-token-string-expression: #4bb74a;
--shiki-token-punctuation: #bbbbbb;
--shiki-token-punctuation: #bbb;
--shiki-token-link: #ffab70;
}
```
Expand Down Expand Up @@ -303,3 +304,71 @@ Nextra configuration (`next.config.js` file).
]
]}
/>

## Custom Grammar

Shiki accepts a
[VSCode TextMate Grammar](https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide)
for syntax highlighting with custom language grammars.

You can provide these grammars by overriding the `getHighlighter` function in
`mdxOptions.rehypePrettyCodeOptions` option in your Nextra config inside
`next.config.js`:

```js filename="next.config.js" {13-18}
import { BUNDLED_LANGUAGES } from 'shiki'

nextra({
// ... other options
mdxOptions: {
rehypePrettyCodeOptions: {
getHighlighter: options =>
getHighlighter({
...options,
langs: [
...BUNDLED_LANGUAGES,
// custom grammar options, see the Shiki documentation for how to provide these options
{
id: 'my-lang',
scopeName: 'source.my-lang',
aliases: ['mylang'], // Along with id, aliases will be included in the allowed names you can use when writing markdown.
path: '../../public/syntax/grammar.tmLanguage.json'
}
]
})
}
}
})
```

## Custom Themes

Within `mdxOptions.rehypePrettyCodeOptions` you may also provide custom themes
instead of [relying on CSS Variables](/docs/guide/syntax-highlighting):

```js filename="next.config.js" {4}
nextra({
// ... other options
mdxOptions: {
rehypePrettyCodeOptions: {
// VSCode theme or built-in Shiki theme, see Shiki documentation for more information
theme: JSON.parse(
readFileSync('./public/syntax/arctis_light.json', 'utf8')
)
}
}
})
```

### Multiple Themes

Nextra currently doesn't support specifying multiple themes. Because Shiki
renders multiple code blocks for every theme and tags them with an attribute
`data-theme`, e.g. `data-theme="dark"`.

<Callout type="info">
However, in the future, multiple themes will be supported. You can track the
progress in shikiji (newly fork of Shiki)
https://github.com/antfu/shikiji#multiple-themes that already supports
multiple themes without rendering multiple code blocks.
</Callout>
8 changes: 6 additions & 2 deletions packages/nextra-theme-blog/src/utils/get-tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ interface Page extends MdxFile {

const flattenPageMap = (page: Page, result: PageMapItem[] = []) => {
if (Array.isArray(page.children!)) {
page.children.forEach(p => flattenPageMap(p, result))
for (const p of page.children) {
flattenPageMap(p, result)
}
}
result.push(page)
}

const flattenPageMaps = (pages: Page[], result: PageMapItem[] = []) => {
pages.forEach(v => flattenPageMap(v, result))
for (const v of pages) {
flattenPageMap(v, result)
}
}
export const getStaticTags = (pageMap: PageMapItem[]) => {
const result: MdxFile[] = []
Expand Down
6 changes: 5 additions & 1 deletion packages/nextra/src/mdx-plugins/rehype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ function visit(node, tagNames, handler) {
handler(node)
return
}
node.children?.forEach(n => visit(n, tagNames, handler))
if ('children' in node) {
for (const n of node.children) {
visit(n, tagNames, handler)
}
}
}

export const parseMeta =
Expand Down