mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 23:49:51 +00:00
Migrate to shikiji
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'next-docs-zeta': minor
|
||||
'next-docs-mdx': minor
|
||||
'next-docs-ui': minor
|
||||
---
|
||||
|
||||
Migrate to Shikiji
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'next-docs-zeta': minor
|
||||
'next-docs-mdx': minor
|
||||
'next-docs-ui': minor
|
||||
---
|
||||
|
||||
Support code highlighting options
|
||||
@@ -1,14 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { IThemedToken } from 'shiki'
|
||||
import type { ThemedToken } from 'shikiji'
|
||||
|
||||
export function TypingCode({
|
||||
tokens,
|
||||
delay,
|
||||
time
|
||||
}: {
|
||||
tokens: IThemedToken[]
|
||||
tokens: ThemedToken[]
|
||||
/**
|
||||
* Delay to start animation (in ms)
|
||||
*/
|
||||
@@ -36,7 +36,7 @@ export function TypingCode({
|
||||
|
||||
let currentLen = 0
|
||||
return (
|
||||
<code data-line>
|
||||
<span data-line>
|
||||
{tokens.flatMap((s, j) => {
|
||||
if (currentLen > length) return
|
||||
const slicedContent = s.content.slice(
|
||||
@@ -51,6 +51,6 @@ export function TypingCode({
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</code>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { cn } from '@/utils/cn'
|
||||
import { Pre } from 'next-docs-ui/mdx-server'
|
||||
import { useMemo, type ComponentPropsWithoutRef } from 'react'
|
||||
import { getHighlighter, type IThemedToken } from 'shiki'
|
||||
import { getHighlighter, type ThemedToken } from 'shikiji'
|
||||
import { TypingCode } from './typing-code-block.client'
|
||||
|
||||
const highlighter = await getHighlighter({
|
||||
theme: 'css-variables'
|
||||
langs: ['bash', 'ts', 'tsx'],
|
||||
themes: ['github-dark']
|
||||
})
|
||||
|
||||
export type CodeBlockProps = ComponentPropsWithoutRef<typeof Pre> & {
|
||||
@@ -15,21 +16,26 @@ export type CodeBlockProps = ComponentPropsWithoutRef<typeof Pre> & {
|
||||
|
||||
export function CodeBlock({ code, lang, className, ...props }: CodeBlockProps) {
|
||||
const tokens = useMemo(
|
||||
() => highlighter.codeToThemedTokens(code, lang),
|
||||
() =>
|
||||
highlighter.codeToThemedTokens(code, {
|
||||
lang
|
||||
}),
|
||||
[code]
|
||||
)
|
||||
|
||||
return (
|
||||
<Pre className={cn('grid', className)} {...props}>
|
||||
{tokens.map((token, i) => (
|
||||
<code data-line key={i}>
|
||||
{token.map((s, j) => (
|
||||
<span key={j} style={{ color: s.color }}>
|
||||
{s.content}
|
||||
</span>
|
||||
))}
|
||||
</code>
|
||||
))}
|
||||
<Pre className={cn('bg-black', className)} {...props}>
|
||||
<code className="grid">
|
||||
{tokens.map((token, i) => (
|
||||
<span data-line key={i}>
|
||||
{token.map((s, j) => (
|
||||
<span key={j} style={{ color: s.color }}>
|
||||
{s.content}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
))}
|
||||
</code>
|
||||
</Pre>
|
||||
)
|
||||
}
|
||||
@@ -41,27 +47,30 @@ export function TypingCodeBlock({
|
||||
...props
|
||||
}: CodeBlockProps) {
|
||||
const result = useMemo(
|
||||
() => highlighter.codeToThemedTokens(code, lang),
|
||||
() =>
|
||||
highlighter.codeToThemedTokens(code, { lang, includeExplanation: false }),
|
||||
[code]
|
||||
)
|
||||
|
||||
return (
|
||||
<Pre className={cn('grid', className)} {...props}>
|
||||
{result.map((tokens, i) => (
|
||||
<TypingCode
|
||||
delay={result
|
||||
.slice(0, i)
|
||||
.map(t => tokenLength(t) * 50)
|
||||
.reduce((a, b) => a + b + 50, 0)}
|
||||
key={i}
|
||||
tokens={tokens}
|
||||
time={50}
|
||||
/>
|
||||
))}
|
||||
<Pre className={cn('bg-black', className)} {...props}>
|
||||
<code className="grid">
|
||||
{result.map((tokens, i) => (
|
||||
<TypingCode
|
||||
delay={result
|
||||
.slice(0, i)
|
||||
.map(t => tokenLength(t) * 50)
|
||||
.reduce((a, b) => a + b + 50, 0)}
|
||||
key={i}
|
||||
tokens={tokens}
|
||||
time={50}
|
||||
/>
|
||||
))}
|
||||
</code>
|
||||
</Pre>
|
||||
)
|
||||
}
|
||||
|
||||
function tokenLength(tokens: IThemedToken[]): number {
|
||||
function tokenLength(tokens: ThemedToken[]): number {
|
||||
return tokens.map(s => s.content.length).reduce((a, b) => a + b, 0)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ It's recommended to use Next Docs with [Tailwind CSS](https://tailwindcss.com) +
|
||||
</Callout>
|
||||
|
||||
```package-install
|
||||
next-docs-zeta shiki
|
||||
next-docs-zeta
|
||||
```
|
||||
|
||||
### Demo
|
||||
|
||||
@@ -22,7 +22,7 @@ module.exports = withContentlayer(config)
|
||||
|
||||
Display code blocks, required by default.
|
||||
|
||||
- Syntax highlighting powered by [Shiki](https://github.com/shikijs/shiki).
|
||||
- Syntax highlighting powered by Shikiji
|
||||
- Copy button
|
||||
- Custom Titles
|
||||
|
||||
|
||||
@@ -12,6 +12,15 @@ const withAnalyzer = createBundleAnalyzer({
|
||||
const config = {
|
||||
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
|
||||
reactStrictMode: true,
|
||||
webpack: config => {
|
||||
// Next.js can't load shikiji without this config
|
||||
config.module.rules.push({
|
||||
test: /\.mjs$/,
|
||||
include: /node_modules/,
|
||||
type: 'javascript/auto'
|
||||
})
|
||||
return config
|
||||
},
|
||||
eslint: {
|
||||
// Replaced by root workspace command
|
||||
ignoreDuringBuilds: true
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"react-dom": "18.2.0",
|
||||
"rehype-katex": "^6.0.3",
|
||||
"remark-math": "^5.1.1",
|
||||
"shiki": "^0.14.5",
|
||||
"shikiji": "^0.7.6",
|
||||
"tailwind-merge": "^2.0.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
rehypeNextDocs,
|
||||
remarkGfm,
|
||||
remarkStructure,
|
||||
remarkToc
|
||||
remarkToc,
|
||||
type RehypeNextDocsOptions
|
||||
} from 'next-docs-zeta/mdx-plugins'
|
||||
import remarkFrontmatter, {
|
||||
type Options as RemarkFrontmatterOptions
|
||||
@@ -48,6 +49,11 @@ type NextDocsMDXOptions = {
|
||||
* @default './content/docs`
|
||||
*/
|
||||
rootContentPath?: string
|
||||
|
||||
/**
|
||||
* Options passed to the built-in rehype plugin
|
||||
*/
|
||||
pluginOptions?: RehypeNextDocsOptions
|
||||
}
|
||||
|
||||
const createNextDocs =
|
||||
@@ -57,7 +63,8 @@ const createNextDocs =
|
||||
mdxOptions = {},
|
||||
cwd = process.cwd(),
|
||||
rootMapPath = './_map.ts',
|
||||
rootContentPath = './content/docs'
|
||||
rootContentPath = './content/docs',
|
||||
pluginOptions
|
||||
}: NextDocsMDXOptions = {}) =>
|
||||
(nextConfig: NextConfig = {}) => {
|
||||
const exports = ['structuredData', 'toc', ...dataExports]
|
||||
@@ -74,7 +81,7 @@ const createNextDocs =
|
||||
]
|
||||
|
||||
const rehypePlugins: PluggableList = [
|
||||
rehypeNextDocs,
|
||||
[rehypeNextDocs, pluginOptions],
|
||||
...(mdxOptions?.rehypePlugins ?? [])
|
||||
]
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The Next.js framework for building documentation website.
|
||||
|
||||
It's build on the top of Next.js, Radix UI and **Next Docs**, fully styled using
|
||||
It's built on top of Next.js, Radix UI and **Next Docs**, fully styled using
|
||||
Tailwind CSS.
|
||||
|
||||
## Features
|
||||
@@ -13,7 +13,7 @@ Next Docs UI provides many features out of the box.
|
||||
- Full-text Search
|
||||
- Table of Contents
|
||||
- Built-in Components
|
||||
- Code Syntax highlighting powered by Shiki
|
||||
- Code Syntax highlighting powered by Shikiji
|
||||
- Well Designed UI & Accessibility
|
||||
|
||||
**Moreover:**
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
:root {
|
||||
--shiki-color-text: oklch(37.53% 0 0);
|
||||
--shiki-color-background: transparent;
|
||||
--shiki-token-constant: oklch(56.45% 0.163 253.27);
|
||||
--shiki-token-string: oklch(54.64% 0.144 147.32);
|
||||
--shiki-token-comment: oklch(73.8% 0 0);
|
||||
--shiki-token-keyword: oklch(56.8% 0.2 26.41);
|
||||
--shiki-token-parameter: oklch(77.03% 0.174 64.05);
|
||||
--shiki-token-function: oklch(50.15% 0.188 294.99);
|
||||
--shiki-token-string-expression: var(--shiki-token-string);
|
||||
--shiki-token-punctuation: oklch(24.78% 0 0);
|
||||
--shiki-token-link: var(--shiki-token-string);
|
||||
|
||||
/* from github-light */
|
||||
--shiki-color-ansi-black: #24292e;
|
||||
--shiki-color-ansi-black-dim: #24292e80;
|
||||
--shiki-color-ansi-red: #d73a49;
|
||||
--shiki-color-ansi-red-dim: #d73a4980;
|
||||
--shiki-color-ansi-green: #28a745;
|
||||
--shiki-color-ansi-green-dim: #28a74580;
|
||||
--shiki-color-ansi-yellow: #dbab09;
|
||||
--shiki-color-ansi-yellow-dim: #dbab0980;
|
||||
--shiki-color-ansi-blue: #0366d6;
|
||||
--shiki-color-ansi-blue-dim: #0366d680;
|
||||
--shiki-color-ansi-magenta: #5a32a3;
|
||||
--shiki-color-ansi-magenta-dim: #5a32a380;
|
||||
--shiki-color-ansi-cyan: #1b7c83;
|
||||
--shiki-color-ansi-cyan-dim: #1b7c8380;
|
||||
--shiki-color-ansi-white: #6a737d;
|
||||
--shiki-color-ansi-white-dim: #6a737d80;
|
||||
--shiki-color-ansi-bright-black: #959da5;
|
||||
--shiki-color-ansi-bright-black-dim: #959da580;
|
||||
--shiki-color-ansi-bright-red: #cb2431;
|
||||
--shiki-color-ansi-bright-red-dim: #cb243180;
|
||||
--shiki-color-ansi-bright-green: #22863a;
|
||||
--shiki-color-ansi-bright-green-dim: #22863a80;
|
||||
--shiki-color-ansi-bright-yellow: #b08800;
|
||||
--shiki-color-ansi-bright-yellow-dim: #b0880080;
|
||||
--shiki-color-ansi-bright-blue: #005cc5;
|
||||
--shiki-color-ansi-bright-blue-dim: #005cc580;
|
||||
--shiki-color-ansi-bright-magenta: #5a32a3;
|
||||
--shiki-color-ansi-bright-magenta-dim: #5a32a380;
|
||||
--shiki-color-ansi-bright-cyan: #3192aa;
|
||||
--shiki-color-ansi-bright-cyan-dim: #3192aa80;
|
||||
--shiki-color-ansi-bright-white: #d1d5da;
|
||||
--shiki-color-ansi-bright-white-dim: #d1d5da80;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--shiki-color-text: oklch(91.18% 0.019 257.29);
|
||||
--shiki-token-constant: oklch(76.85% 0.121 252.34);
|
||||
--shiki-token-string: oklch(81.11% 0.124 0);
|
||||
--shiki-token-comment: oklch(55.18% 0.017 251.27);
|
||||
--shiki-token-keyword: oklch(72.14% 0.162 15.49);
|
||||
--shiki-token-function: oklch(72.67% 0.137 299.15);
|
||||
--shiki-token-string-expression: oklch(69.28% 0.179 143.2);
|
||||
--shiki-token-punctuation: oklch(79.21% 0 0);
|
||||
--shiki-token-link: var(--shiki-token-string);
|
||||
|
||||
/* from github-dark */
|
||||
--shiki-color-ansi-black: #586069;
|
||||
--shiki-color-ansi-black-dim: #58606980;
|
||||
--shiki-color-ansi-red: #ea4a5a;
|
||||
--shiki-color-ansi-red-dim: #ea4a5a80;
|
||||
--shiki-color-ansi-green: #34d058;
|
||||
--shiki-color-ansi-green-dim: #34d05880;
|
||||
--shiki-color-ansi-yellow: #ffea7f;
|
||||
--shiki-color-ansi-yellow-dim: #ffea7f80;
|
||||
--shiki-color-ansi-blue: #2188ff;
|
||||
--shiki-color-ansi-blue-dim: #2188ff80;
|
||||
--shiki-color-ansi-magenta: #b392f0;
|
||||
--shiki-color-ansi-magenta-dim: #b392f080;
|
||||
--shiki-color-ansi-cyan: #39c5cf;
|
||||
--shiki-color-ansi-cyan-dim: #39c5cf80;
|
||||
--shiki-color-ansi-white: #d1d5da;
|
||||
--shiki-color-ansi-white-dim: #d1d5da80;
|
||||
--shiki-color-ansi-bright-black: #959da5;
|
||||
--shiki-color-ansi-bright-black-dim: #959da580;
|
||||
--shiki-color-ansi-bright-red: #f97583;
|
||||
--shiki-color-ansi-bright-red-dim: #f9758380;
|
||||
--shiki-color-ansi-bright-green: #85e89d;
|
||||
--shiki-color-ansi-bright-green-dim: #85e89d80;
|
||||
--shiki-color-ansi-bright-yellow: #ffea7f;
|
||||
--shiki-color-ansi-bright-yellow-dim: #ffea7f80;
|
||||
--shiki-color-ansi-bright-blue: #79b8ff;
|
||||
--shiki-color-ansi-bright-blue-dim: #79b8ff80;
|
||||
--shiki-color-ansi-bright-magenta: #b392f0;
|
||||
--shiki-color-ansi-bright-magenta-dim: #b392f080;
|
||||
--shiki-color-ansi-bright-cyan: #56d4dd;
|
||||
--shiki-color-ansi-bright-cyan-dim: #56d4dd80;
|
||||
--shiki-color-ansi-bright-white: #fafbfc;
|
||||
--shiki-color-ansi-bright-white-dim: #fafbfc80;
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
@import './codeblock.css';
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -12,11 +10,11 @@ body {
|
||||
@apply nd-bg-background nd-text-foreground;
|
||||
}
|
||||
|
||||
[data-line] {
|
||||
[data-nd-codeblock] [data-line] {
|
||||
@apply nd-px-4 nd-border-l-2 nd-border-transparent;
|
||||
}
|
||||
|
||||
[data-highlighted-line] {
|
||||
[data-highlighted-line][data-line] {
|
||||
@apply nd-border-primary nd-bg-primary/10;
|
||||
}
|
||||
|
||||
@@ -24,6 +22,14 @@ body {
|
||||
@apply nd-bg-primary/10 nd-border-b-2 nd-border-primary;
|
||||
}
|
||||
|
||||
[data-line] span {
|
||||
color: var(--shiki-light);
|
||||
}
|
||||
|
||||
.dark [data-line] span {
|
||||
color: var(--shiki-dark);
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 3.9%;
|
||||
|
||||
@@ -1,44 +1,55 @@
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { cn } from '@/utils/cn'
|
||||
import { CheckIcon, CopyIcon } from 'lucide-react'
|
||||
import type { ButtonHTMLAttributes, ComponentPropsWithoutRef } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import type { ButtonHTMLAttributes, HTMLAttributes } from 'react'
|
||||
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
type PreProps = ComponentPropsWithoutRef<'pre'> & {
|
||||
type PreProps = HTMLAttributes<HTMLElement> & {
|
||||
allowCopy?: boolean
|
||||
}
|
||||
|
||||
export function Pre({ title, allowCopy = true, ...props }: PreProps) {
|
||||
const ref = useRef<HTMLPreElement>(null)
|
||||
const onCopy = useCallback(() => {
|
||||
if (ref.current?.textContent == null) return
|
||||
export const Pre = forwardRef<HTMLElement, PreProps>(
|
||||
({ title, allowCopy = true, className, ...props }, ref) => {
|
||||
const preRef = useRef<HTMLPreElement>(null)
|
||||
const onCopy = useCallback(() => {
|
||||
if (preRef.current?.textContent == null) return
|
||||
|
||||
navigator.clipboard.writeText(ref.current.textContent)
|
||||
}, [])
|
||||
navigator.clipboard.writeText(preRef.current.textContent)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="nd-relative nd-overflow-hidden nd-group nd-border nd-rounded-lg nd-text-sm nd-bg-secondary/50 nd-my-6 nd-not-prose">
|
||||
{title ? (
|
||||
<div className="nd-flex nd-flex-row nd-items-center nd-bg-muted nd-text-muted-foreground nd-pl-4 nd-pr-2 nd-py-1.5 nd-border-b">
|
||||
<span className="nd-flex-1">{title}</span>
|
||||
{allowCopy && <CopyButton onCopy={onCopy} />}
|
||||
</div>
|
||||
) : (
|
||||
allowCopy && (
|
||||
<CopyButton
|
||||
className="nd-absolute nd-top-2 nd-right-2 nd-z-[2]"
|
||||
onCopy={onCopy}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
<ScrollArea>
|
||||
<pre ref={ref} {...props} className={cn('nd-py-4', props.className)}>
|
||||
{props.children}
|
||||
</pre>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<figure
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'nd-relative nd-overflow-hidden nd-group nd-border nd-rounded-lg nd-text-sm nd-bg-secondary/50 nd-my-6 nd-not-prose',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{title ? (
|
||||
<div className="nd-flex nd-flex-row nd-items-center nd-bg-muted nd-text-muted-foreground nd-pl-4 nd-pr-2 nd-py-1.5 nd-border-b">
|
||||
<figcaption className="nd-flex-1">{title}</figcaption>
|
||||
{allowCopy && <CopyButton onCopy={onCopy} />}
|
||||
</div>
|
||||
) : (
|
||||
allowCopy && (
|
||||
<CopyButton
|
||||
className="nd-absolute nd-top-2 nd-right-2 nd-z-[2]"
|
||||
onCopy={onCopy}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
<ScrollArea>
|
||||
<pre data-nd-codeblock ref={preRef} className="nd-py-4">
|
||||
{props.children}
|
||||
</pre>
|
||||
</ScrollArea>
|
||||
</figure>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Pre.displayName = 'Pre'
|
||||
|
||||
function CopyButton({
|
||||
className,
|
||||
|
||||
@@ -30,7 +30,12 @@ export function RootProvider({
|
||||
)
|
||||
|
||||
return enableThemeProvider ? (
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
{body}
|
||||
</ThemeProvider>
|
||||
) : (
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-mdx": "^2.3.0",
|
||||
"scroll-into-view-if-needed": "^3.1.0",
|
||||
"shiki": "^0.14.5",
|
||||
"shikiji": "^0.7.6",
|
||||
"swr": "^2.2.2",
|
||||
"unist-util-visit": "^4.1.2"
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import { rehypeImgSize, rehypeNextDocs, remarkGfm } from '@/mdx-plugins'
|
||||
import type { RehypeNextDocsOptions } from '@/mdx-plugins/rehype-next-docs'
|
||||
import type { Args, ComputedFields, FieldDef } from 'contentlayer/source-files'
|
||||
import { defineDocumentType } from 'contentlayer/source-files'
|
||||
import type { Options as ImgSizeOptions } from 'rehype-img-size'
|
||||
@@ -50,10 +51,12 @@ type Options = {
|
||||
*/
|
||||
imgDirPath: string
|
||||
|
||||
docFields?: Record<string, FieldDef>
|
||||
docsComputedFields?: ComputedFields<'Docs'>
|
||||
metaFields?: Record<string, FieldDef>
|
||||
metaComputedFields?: ComputedFields<'Meta'>
|
||||
pluginOptions: RehypeNextDocsOptions
|
||||
|
||||
docFields: Record<string, FieldDef>
|
||||
docsComputedFields: ComputedFields<'Docs'>
|
||||
metaFields: Record<string, FieldDef>
|
||||
metaComputedFields: ComputedFields<'Meta'>
|
||||
}
|
||||
|
||||
export function createConfig(options: Partial<Options> = {}): Args {
|
||||
@@ -64,6 +67,7 @@ export function createConfig(options: Partial<Options> = {}): Args {
|
||||
docFields,
|
||||
metaFields,
|
||||
docsComputedFields,
|
||||
pluginOptions,
|
||||
metaComputedFields
|
||||
} = options
|
||||
|
||||
@@ -146,7 +150,7 @@ export function createConfig(options: Partial<Options> = {}): Args {
|
||||
documentTypes: [Docs, Meta],
|
||||
mdx: {
|
||||
rehypePlugins: [
|
||||
rehypeNextDocs,
|
||||
[rehypeNextDocs, pluginOptions],
|
||||
[
|
||||
// @ts-ignore
|
||||
rehypeImgSize,
|
||||
|
||||
@@ -6,7 +6,10 @@ export {
|
||||
default as remarkGfm,
|
||||
type Options as RemarkGfmOptions
|
||||
} from 'remark-gfm'
|
||||
export { rehypeNextDocs } from './mdx-plugins/rehype-next-docs'
|
||||
export {
|
||||
type RehypeNextDocsOptions,
|
||||
rehypeNextDocs
|
||||
} from './mdx-plugins/rehype-next-docs'
|
||||
export {
|
||||
remarkDynamicContent,
|
||||
type Options as RemarkDynamicContentOptions
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Slugger from 'github-slugger'
|
||||
import type { Root } from 'hast'
|
||||
import type { Element, Root } from 'hast'
|
||||
import rehypePrettycode, {
|
||||
type Options as RehypePrettyCodeOptions
|
||||
} from 'rehype-pretty-code'
|
||||
@@ -11,7 +11,10 @@ const headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
|
||||
const customMetaRegex = /custom="([^"]+)"/
|
||||
|
||||
const rehypePrettyCodeOptions: RehypePrettyCodeOptions = {
|
||||
theme: 'css-variables',
|
||||
theme: {
|
||||
light: 'github-light',
|
||||
dark: 'github-dark'
|
||||
},
|
||||
defaultLang: {
|
||||
block: 'text'
|
||||
},
|
||||
@@ -22,10 +25,16 @@ const rehypePrettyCodeOptions: RehypePrettyCodeOptions = {
|
||||
}
|
||||
}
|
||||
|
||||
export type RehypeNextDocsOptions = {
|
||||
codeOptions?: RehypePrettyCodeOptions
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle codeblocks and heading slugs
|
||||
*/
|
||||
export function rehypeNextDocs(): Transformer<Root, Root> {
|
||||
export function rehypeNextDocs({
|
||||
codeOptions
|
||||
}: RehypeNextDocsOptions = {}): Transformer<Root, Root> {
|
||||
return async tree => {
|
||||
slugger.reset()
|
||||
|
||||
@@ -52,31 +61,29 @@ export function rehypeNextDocs(): Transformer<Root, Root> {
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
await rehypePrettycode(rehypePrettyCodeOptions)(tree)
|
||||
await rehypePrettycode({ ...rehypePrettyCodeOptions, ...codeOptions })(tree)
|
||||
|
||||
visit(tree, ['div', 'pre'], node => {
|
||||
visit(tree, ['figure', 'pre'], node => {
|
||||
node.properties ||= {}
|
||||
|
||||
// Remove default fragment div
|
||||
// Remove default fragment element
|
||||
// Add title to pre element
|
||||
if ('data-rehype-pretty-code-fragment' in node.properties) {
|
||||
if (node.children.length === 0) return
|
||||
let title: string | undefined = undefined
|
||||
if ('data-rehype-pretty-code-figure' in node.properties) {
|
||||
const titleNode = node.children.find(
|
||||
child => child.type === 'element' && child.tagName === 'figcaption'
|
||||
) as Element | undefined
|
||||
const preNode = node.children.find(
|
||||
child => child.type === 'element' && child.tagName === 'pre'
|
||||
) as Element | undefined
|
||||
|
||||
for (const child of node.children) {
|
||||
if (child.type !== 'element') continue
|
||||
child.properties ||= {}
|
||||
if (!preNode) return
|
||||
|
||||
if ('data-rehype-pretty-code-title' in child.properties) {
|
||||
title = flattenNode(child)
|
||||
}
|
||||
|
||||
if (child.tagName === 'pre') {
|
||||
child.properties.title = title
|
||||
Object.assign(node, child)
|
||||
break
|
||||
}
|
||||
if (titleNode) {
|
||||
preNode.properties ||= {}
|
||||
preNode.properties.title = flattenNode(titleNode)
|
||||
}
|
||||
|
||||
Object.assign(node, preNode)
|
||||
}
|
||||
|
||||
// Add custom meta to properties
|
||||
|
||||
Generated
+6
-31
@@ -113,9 +113,9 @@ importers:
|
||||
remark-math:
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.1
|
||||
shiki:
|
||||
specifier: ^0.14.5
|
||||
version: 0.14.5
|
||||
shikiji:
|
||||
specifier: ^0.7.6
|
||||
version: 0.7.6
|
||||
tailwind-merge:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@@ -343,9 +343,9 @@ importers:
|
||||
scroll-into-view-if-needed:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0
|
||||
shiki:
|
||||
specifier: ^0.14.5
|
||||
version: 0.14.5
|
||||
shikiji:
|
||||
specifier: ^0.7.6
|
||||
version: 0.7.6
|
||||
swr:
|
||||
specifier: ^2.2.2
|
||||
version: 2.2.2([email protected])
|
||||
@@ -3595,10 +3595,6 @@ packages:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==}
|
||||
dev: false
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -5873,10 +5869,6 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
||||
dev: false
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
|
||||
optionalDependencies:
|
||||
@@ -8124,15 +8116,6 @@ packages:
|
||||
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
|
||||
dev: true
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==}
|
||||
dependencies:
|
||||
ansi-sequence-parser: 1.1.1
|
||||
jsonc-parser: 3.2.0
|
||||
vscode-oniguruma: 1.7.0
|
||||
vscode-textmate: 8.0.0
|
||||
dev: false
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-KzEtvSGQtBvfwVIB70kOmIfl/5rz1LC8j+tvlHXsJKAIdONNQvG1at7ivUUq3xUctqgO6fsO3AGomUSh0F+wsQ==}
|
||||
dependencies:
|
||||
@@ -9079,14 +9062,6 @@ packages:
|
||||
vfile-message: 4.0.2
|
||||
dev: false
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
|
||||
dev: false
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
|
||||
dev: false
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
Reference in New Issue
Block a user