diff --git a/e2e/fixtures/plugin-shiki/doc/index.mdx b/e2e/fixtures/plugin-shiki/doc/index.mdx index e889b6ca8..05fa5beb1 100644 --- a/e2e/fixtures/plugin-shiki/doc/index.mdx +++ b/e2e/fixtures/plugin-shiki/doc/index.mdx @@ -2,7 +2,7 @@ ## transformers -{/* transformerDiff */} +{/* transformerNotationDiff */} ```ts export function foo() { @@ -19,7 +19,7 @@ export function foo() { } ``` -{/* transformerErrorLevel */} +{/* transformerNotationErrorLevel */} ```ts export function foo() { @@ -34,3 +34,12 @@ export function foo() { console.log('Focus'); // [!code focus] } ``` + +{/* transformerNotationHighlight */} + +```js +console.log('1'); // [!code highlight] +console.log('2'); +console.log('3'); // [!code highlight] +console.log('4'); // [!code highlight] +``` diff --git a/e2e/tests/plugin-shiki.test.ts b/e2e/tests/plugin-shiki.test.ts index 757612b6f..fe5627b8b 100644 --- a/e2e/tests/plugin-shiki.test.ts +++ b/e2e/tests/plugin-shiki.test.ts @@ -29,7 +29,7 @@ test.describe('plugin shiki test', async () => { waitUntil: 'networkidle', }); const shikiDoms = await page.$$('.rspress-code-content'); - expect(shikiDoms.length).toBe(4); + expect(shikiDoms.length).toBe(5); const firstShikiDom = shikiDoms[0]; diff --git a/packages/document/package.json b/packages/document/package.json index eb06b9682..957d83747 100644 --- a/packages/document/package.json +++ b/packages/document/package.json @@ -20,6 +20,7 @@ "@rspress/plugin-algolia": "workspace:*", "@rspress/plugin-shiki": "workspace:*", "@rstack-dev/doc-ui": "1.7.3", + "@shikijs/transformers": "^3.1.0", "@types/react": "^18.3.20", "framer-motion": "12.0.6", "rsbuild-plugin-google-analytics": "^1.0.3", diff --git a/packages/document/rspress.config.ts b/packages/document/rspress.config.ts index 04f2e11be..38c3aecbe 100644 --- a/packages/document/rspress.config.ts +++ b/packages/document/rspress.config.ts @@ -1,6 +1,12 @@ import { pluginSass } from '@rsbuild/plugin-sass'; import { pluginAlgolia } from '@rspress/plugin-algolia'; import { pluginShiki } from '@rspress/plugin-shiki'; +import { + transformerNotationDiff, + transformerNotationErrorLevel, + transformerNotationFocus, + transformerNotationHighlight, +} from '@shikijs/transformers'; import { pluginGoogleAnalytics } from 'rsbuild-plugin-google-analytics'; import { pluginOpenGraph } from 'rsbuild-plugin-open-graph'; import { pluginFontOpenSans } from 'rspress-plugin-font-open-sans'; @@ -29,7 +35,13 @@ export default defineConfig({ verificationContent: '0F854AB11EB1D255', }), pluginShiki({ - langs: ['mdx', 'html'], + langs: ['mdx', 'html', 'toml'], + transformers: [ + transformerNotationDiff(), + transformerNotationErrorLevel(), + transformerNotationHighlight(), + transformerNotationFocus(), + ], }), ], builderConfig: { diff --git a/packages/plugin-shiki/src/shiki/pluginShiki.ts b/packages/plugin-shiki/src/shiki/pluginShiki.ts index 866efcc89..e8fd54eb2 100644 --- a/packages/plugin-shiki/src/shiki/pluginShiki.ts +++ b/packages/plugin-shiki/src/shiki/pluginShiki.ts @@ -8,6 +8,7 @@ import { SHIKI_TRANSFORMER_LINE_NUMBER, transformerLineNumber, } from './transformers'; +import { transformerAddTitle } from './transformers/add-title'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -53,7 +54,7 @@ export function pluginShiki( name: '@rspress/plugin-shiki', async config(config) { - const newTransformers = [...transformers]; + const newTransformers = [transformerAddTitle(), ...transformers]; config.markdown = config.markdown || {}; // Shiki will be integrated by rehype plugin, so we should use the javascript version markdown compiler. config.markdown.mdxRs = false; @@ -72,12 +73,12 @@ export function pluginShiki( config.markdown.rehypePlugins.push([ rehypePluginShiki, { - ...restOptions, - transformers: newTransformers, theme: cssVariablesTheme, + defaultLanguage: 'txt', + ...restOptions, addLanguageClass: true, + transformers: newTransformers, langs: [...SHIKI_DEFAULT_HIGHLIGHT_LANGUAGES, ...langs], - defaultLanguage: 'txt', } satisfies RehypeShikiOptions, ]); return config; diff --git a/packages/plugin-shiki/src/shiki/transformers/add-title.ts b/packages/plugin-shiki/src/shiki/transformers/add-title.ts new file mode 100644 index 000000000..0015e9849 --- /dev/null +++ b/packages/plugin-shiki/src/shiki/transformers/add-title.ts @@ -0,0 +1,33 @@ +import type { ShikiTransformer } from 'shiki'; + +export const SHIKI_TRANSFORMER_ADD_TITLE = 'shiki-transformer:add-title'; + +function parseTitleFromMeta(meta: string | undefined): string { + if (!meta) { + return ''; + } + let result = meta; + const highlightReg = /{[\d,-]*}/i; + const highlightMeta = highlightReg.exec(meta)?.[0]; + if (highlightMeta) { + result = meta.replace(highlightReg, '').trim(); + } + result = result.split('=')[1] ?? ''; + return result?.replace(/["'`]/g, ''); +} + +export function transformerAddTitle(): ShikiTransformer { + return { + name: SHIKI_TRANSFORMER_ADD_TITLE, + pre(pre) { + const title = parseTitleFromMeta(this.options.meta?.__raw); + if (title.length > 0) { + pre.properties = { + ...pre.properties, + title, + }; + } + return pre; + }, + }; +} diff --git a/packages/theme-default/src/layout/DocLayout/docComponents/pre.tsx b/packages/theme-default/src/layout/DocLayout/docComponents/pre.tsx index 46213cde1..2c68affbd 100644 --- a/packages/theme-default/src/layout/DocLayout/docComponents/pre.tsx +++ b/packages/theme-default/src/layout/DocLayout/docComponents/pre.tsx @@ -28,7 +28,7 @@ function ShikiPre({ ...otherProps }: { codeElementClassName: string | undefined; - codeTitle: string; + codeTitle: string | undefined; child: React.ReactElement; preElementRef: React.RefObject; className: string | undefined; @@ -42,7 +42,9 @@ function ShikiPre({
             {child}
@@ -61,10 +63,12 @@ function ShikiPre({
 export function Pre({
   children,
   className,
+  title,
   ...otherProps
 }: {
   children: React.ReactElement[] | React.ReactElement;
   className?: string;
+  title?: string;
   [key: string]: any;
 }) {
   const { siteData } = usePageData();
@@ -73,7 +77,6 @@ export function Pre({
 
   const renderChild = (child: React.ReactElement) => {
     const { className: codeElementClassName, meta } = child.props as CodeProps;
-    const codeTitle = parseTitleFromMeta(meta);
 
     if (codeHighlighter === 'shiki') {
       return (
@@ -81,15 +84,16 @@ export function Pre({
           child={child}
           className={className}
           codeElementClassName={codeElementClassName}
-          codeTitle={codeTitle}
+          codeTitle={title}
           preElementRef={preElementRef}
           {...otherProps}
         />
       );
     }
 
+    const codeTitle = parseTitleFromMeta(meta);
     return (
-      
+
{codeTitle &&
{codeTitle}
}
{child}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 561c4bc5a..b80f2c616 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -930,6 +930,9 @@ importers: '@rstack-dev/doc-ui': specifier: 1.7.3 version: 1.7.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@shikijs/transformers': + specifier: ^3.1.0 + version: 3.1.0 '@types/react': specifier: ^18.3.20 version: 18.3.20