From b1a76b08d2ac58f58b0c9e234f7a2aed19e03859 Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Thu, 14 Apr 2022 19:06:39 +0200 Subject: [PATCH 1/8] extract CodeBlockLine --- .../src/theme/CodeBlock/index.tsx | 111 +++++++++++------- 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index f734ca0b15da..3389cf35c18d 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -5,7 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import React, {isValidElement, useEffect, useState} from 'react'; +import React, { + isValidElement, + useEffect, + useState, + type ComponentProps, +} from 'react'; import clsx from 'clsx'; import Highlight, {defaultProps, type Language} from 'prism-react-renderer'; import { @@ -23,12 +28,65 @@ import type {Props} from '@theme/CodeBlock'; import styles from './styles.module.css'; +// Lib does not make this easy +type RenderProps = Parameters['children']>[0]; +type GetLineProps = RenderProps['getLineProps']; +type GetTokenProps = RenderProps['getTokenProps']; +type Token = RenderProps['tokens'][number][number]; + +function CodeBlockLine({ + line, + highlight, + showLineNumbers, + getLineProps, + getTokenProps, +}: { + line: Token[]; + highlight: boolean; + showLineNumbers: boolean; + getLineProps: GetLineProps; + getTokenProps: GetTokenProps; +}) { + if (line.length === 1 && line[0]!.content === '\n') { + line[0]!.content = ''; + } + + const lineProps = getLineProps({ + line, + ...(showLineNumbers && {className: styles.codeLine}), + }); + + if (highlight) { + lineProps.className += ' docusaurus-highlight-code-line'; + } + + const lineTokens = line.map((token, key) => ( + + )); + + return ( + + {showLineNumbers ? ( + <> + + {lineTokens} + + ) : ( + <> + {lineTokens} +
+ + )} +
+ ); +} + export default function CodeBlock({ children, className: blockClassName = '', metastring, title, - showLineNumbers, + showLineNumbers: showLineNumbersProp, language: languageProp, }: Props): JSX.Element { const {prism} = useThemeConfig(); @@ -93,7 +151,7 @@ export default function CodeBlock({ languageProp ?? parseLanguage(blockClassName) ?? prism.defaultLanguage; const {highlightLines, code} = parseLines(content, metastring, language); const shouldShowLineNumbers = - showLineNumbers || containsLineNumbers(metastring); + showLineNumbersProp || containsLineNumbers(metastring); return ( - {tokens.map((line, i) => { - if (line.length === 1 && line[0]!.content === '\n') { - line[0]!.content = ''; - } - - const lineProps = getLineProps({ - line, - key: i, - ...(shouldShowLineNumbers && {className: styles.codeLine}), - }); - - if (highlightLines.includes(i)) { - lineProps.className += ' docusaurus-highlight-code-line'; - } - - const lineTokens = line.map((token, key) => ( - - )); - - return ( - - {shouldShowLineNumbers ? ( - <> - - - {lineTokens} - - - ) : ( - <> - {lineTokens} -
- - )} -
- ); - })} + {tokens.map((line, i) => ( + + ))} From ccbd950f7dcc98a9f45bce2940354fcaf33320b3 Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Thu, 14 Apr 2022 19:18:34 +0200 Subject: [PATCH 2/8] stable refactor --- .../src/theme/CodeBlock/index.tsx | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index 3389cf35c18d..9f0634db177f 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -10,6 +10,7 @@ import React, { useEffect, useState, type ComponentProps, + type ReactElement, } from 'react'; import clsx from 'clsx'; import Highlight, {defaultProps, type Language} from 'prism-react-renderer'; @@ -81,6 +82,14 @@ function CodeBlockLine({ ); } +function childrenToString(children: string | ReactElement): string | null { + if (React.Children.toArray(children).some((el) => isValidElement(el))) { + return null; + } + // The children is now guaranteed to be one/more plain strings + return Array.isArray(children) ? children.join('') : (children as string); +} + export default function CodeBlock({ children, className: blockClassName = '', @@ -89,7 +98,13 @@ export default function CodeBlock({ showLineNumbers: showLineNumbersProp, language: languageProp, }: Props): JSX.Element { - const {prism} = useThemeConfig(); + const { + prism: {defaultLanguage}, + } = useThemeConfig(); + const language = + languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage; + const prismTheme = usePrismTheme(); + const prismCssVariables = getPrismCssVariables(prismTheme); const [mounted, setMounted] = useState(false); // The Prism theme on SSR is always the default theme but the site theme @@ -103,18 +118,12 @@ export default function CodeBlock({ setMounted(true); }, []); - // We still parse the metastring in case we want to support more syntax in the - // future. Note that MDX doesn't strip quotes when parsing metastring: - // "title=\"xyz\"" => title: "\"xyz\"" - const codeBlockTitle = parseCodeBlockTitle(metastring) || title; - const prismTheme = usePrismTheme(); - - const prismCssVariables = getPrismCssVariables(prismTheme); + const content = childrenToString(children); //
 tags in markdown map to CodeBlocks and they may contain JSX children.
   // When the children is not a simple string, we just return a styled block
   // without actually highlighting.
-  if (React.Children.toArray(children).some((el) => isValidElement(el))) {
+  if (!content) {
     return (
        title: "\"xyz\""
+  const codeBlockTitle = parseCodeBlockTitle(metastring) || title;
 
-  const language =
-    languageProp ?? parseLanguage(blockClassName) ?? prism.defaultLanguage;
   const {highlightLines, code} = parseLines(content, metastring, language);
-  const shouldShowLineNumbers =
+  const showLineNumbers =
     showLineNumbersProp || containsLineNumbers(metastring);
 
   return (
@@ -183,7 +190,7 @@ export default function CodeBlock({
               
                 {tokens.map((line, i) => (
                   
                 ))}
               

From a789f29a2645b11554c233dd2a4b79bef74e5fe9 Mon Sep 17 00:00:00 2001
From: sebastienlorber 
Date: Thu, 14 Apr 2022 19:35:58 +0200
Subject: [PATCH 3/8] stable refactor

---
 .../src/theme/CodeBlock/index.tsx             | 112 ++++++++++--------
 1 file changed, 63 insertions(+), 49 deletions(-)

diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx
index 9f0634db177f..6df491a79f05 100644
--- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx
+++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx
@@ -90,14 +90,74 @@ function childrenToString(children: string | ReactElement): string | null {
   return Array.isArray(children) ? children.join('') : (children as string);
 }
 
-export default function CodeBlock({
+// The Prism theme on SSR is always the default theme but the site theme
+// can be in a different mode. React hydration doesn't update DOM styles
+// that come from SSR. Hence force a re-render after mounting to apply the
+// current relevant styles. There will be a flash seen of the original
+// styles seen using this current approach but that's probably ok. Fixing
+// the flash will require changing the theming approach and is not worth it
+// at this point.
+function useCodeBlockKey(): string {
+  const [mounted, setMounted] = useState(false);
+  useEffect(() => {
+    setMounted(true);
+  }, []);
+  return String(mounted);
+}
+
+export default function CodeBlock({children, ...props}: Props): JSX.Element {
+  const key = useCodeBlockKey();
+
+  const childrenString = childrenToString(children);
+  if (childrenString) {
+    return (
+      
+        {childrenString}
+      
+    );
+  } 
+    return (
+      
+        {children}
+      
+    );
+  
+}
+
+// 
 tags in markdown map to CodeBlocks. They may contain JSX children.
+// When the children is not a simple string, we just return a styled block
+// without actually highlighting.
+function CodeBlockJSX({
+  children,
+  className: blockClassName,
+}: Props): JSX.Element {
+  const prismTheme = usePrismTheme();
+  const prismCssVariables = getPrismCssVariables(prismTheme);
+  return (
+    
+      {children}
+    
+ ); +} + +function CodeBlockString({ children, className: blockClassName = '', metastring, title, showLineNumbers: showLineNumbersProp, language: languageProp, -}: Props): JSX.Element { +}: Omit & {children: string}): JSX.Element { const { prism: {defaultLanguage}, } = useThemeConfig(); @@ -106,64 +166,18 @@ export default function CodeBlock({ const prismTheme = usePrismTheme(); const prismCssVariables = getPrismCssVariables(prismTheme); - const [mounted, setMounted] = useState(false); - // The Prism theme on SSR is always the default theme but the site theme - // can be in a different mode. React hydration doesn't update DOM styles - // that come from SSR. Hence force a re-render after mounting to apply the - // current relevant styles. There will be a flash seen of the original - // styles seen using this current approach but that's probably ok. Fixing - // the flash will require changing the theming approach and is not worth it - // at this point. - useEffect(() => { - setMounted(true); - }, []); - - const content = childrenToString(children); - - //
 tags in markdown map to CodeBlocks and they may contain JSX children.
-  // When the children is not a simple string, we just return a styled block
-  // without actually highlighting.
-  if (!content) {
-    return (
-      
-        {({className}) => (
-          
-            {children}
-          
- )} -
- ); - } - // We still parse the metastring in case we want to support more syntax in the // future. Note that MDX doesn't strip quotes when parsing metastring: // "title=\"xyz\"" => title: "\"xyz\"" const codeBlockTitle = parseCodeBlockTitle(metastring) || title; - const {highlightLines, code} = parseLines(content, metastring, language); + const {highlightLines, code} = parseLines(children, metastring, language); const showLineNumbers = showLineNumbersProp || containsLineNumbers(metastring); return ( From 989fca40c88e5138a0411f739d3e3502ddf846b0 Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Thu, 14 Apr 2022 19:36:59 +0200 Subject: [PATCH 4/8] stable refactor --- .../src/theme/CodeBlock/index.tsx | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index 6df491a79f05..7952c9cc524a 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -107,21 +107,16 @@ function useCodeBlockKey(): string { export default function CodeBlock({children, ...props}: Props): JSX.Element { const key = useCodeBlockKey(); - const childrenString = childrenToString(children); - if (childrenString) { - return ( - - {childrenString} - - ); - } - return ( - - {children} - - ); - + return childrenString ? ( + + {childrenString} + + ) : ( + + {children} + + ); } //
 tags in markdown map to CodeBlocks. They may contain JSX children.

From 763c76062a570671d55163f1f3b39702c17fd4c4 Mon Sep 17 00:00:00 2001
From: sebastienlorber 
Date: Thu, 14 Apr 2022 19:50:03 +0200
Subject: [PATCH 5/8] add CodeBlockContainer

---
 .../src/theme/CodeBlock/index.tsx             | 55 ++++++++++---------
 1 file changed, 30 insertions(+), 25 deletions(-)

diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx
index 7952c9cc524a..2d699299492e 100644
--- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx
+++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx
@@ -119,29 +119,38 @@ export default function CodeBlock({children, ...props}: Props): JSX.Element {
   );
 }
 
+function CodeBlockContainer({
+  as: Wrapper,
+  ...props
+}: {as: As} & ComponentProps) {
+  return (
+    // @ts-expect-error: TODO bad typing here
+    
+  );
+}
+
 // 
 tags in markdown map to CodeBlocks. They may contain JSX children.
 // When the children is not a simple string, we just return a styled block
 // without actually highlighting.
-function CodeBlockJSX({
-  children,
-  className: blockClassName,
-}: Props): JSX.Element {
+function CodeBlockJSX({children, className}: Props): JSX.Element {
   const prismTheme = usePrismTheme();
   const prismCssVariables = getPrismCssVariables(prismTheme);
   return (
-    
       {children}
-    
+ ); } @@ -177,16 +186,12 @@ function CodeBlockString({ code={code} language={(language ?? 'text') as Language}> {({className, tokens, getLineProps, getTokenProps}) => ( -
{codeBlockTitle && (
{codeBlockTitle}
@@ -216,7 +221,7 @@ function CodeBlockString({
- + )} ); From 4e16d0049103a3a232d9444690bdb54feb5490ce Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 15 Apr 2022 10:04:48 +0800 Subject: [PATCH 6/8] refactor --- .../src/theme-classic.d.ts | 4 +- .../src/theme/CodeBlock/index.tsx | 70 +++++++++---------- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 7d3bda55d6a7..f4cead69c404 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -148,10 +148,10 @@ declare module '@theme/BlogLayout' { } declare module '@theme/CodeBlock' { - import type {ReactElement} from 'react'; + import type {ReactNode} from 'react'; export interface Props { - readonly children: string | ReactElement; + readonly children: ReactNode; readonly className?: string; readonly metastring?: string; readonly title?: string; diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index 2d699299492e..f77860afdea0 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -7,13 +7,12 @@ import React, { isValidElement, - useEffect, - useState, type ComponentProps, - type ReactElement, + type ReactNode, } from 'react'; import clsx from 'clsx'; import Highlight, {defaultProps, type Language} from 'prism-react-renderer'; +import useIsBrowser from '@docusaurus/useIsBrowser'; import { useThemeConfig, parseCodeBlockTitle, @@ -82,51 +81,47 @@ function CodeBlockLine({ ); } -function childrenToString(children: string | ReactElement): string | null { +/** + * Best attempt to make the children a plain string so it is copyable. If there + * are react elements, we will not be able to copy the content, and it will + * return `children` as-is; otherwise, it concatenates the string children + * together. + */ +function maybeStringifyChildren(children: ReactNode): ReactNode { if (React.Children.toArray(children).some((el) => isValidElement(el))) { - return null; + return children; } // The children is now guaranteed to be one/more plain strings return Array.isArray(children) ? children.join('') : (children as string); } -// The Prism theme on SSR is always the default theme but the site theme -// can be in a different mode. React hydration doesn't update DOM styles -// that come from SSR. Hence force a re-render after mounting to apply the -// current relevant styles. There will be a flash seen of the original -// styles seen using this current approach but that's probably ok. Fixing -// the flash will require changing the theming approach and is not worth it -// at this point. -function useCodeBlockKey(): string { - const [mounted, setMounted] = useState(false); - useEffect(() => { - setMounted(true); - }, []); - return String(mounted); -} - -export default function CodeBlock({children, ...props}: Props): JSX.Element { - const key = useCodeBlockKey(); - const childrenString = childrenToString(children); - return childrenString ? ( - - {childrenString} - - ) : ( - - {children} - +export default function CodeBlock({ + children: rawChildren, + ...props +}: Props): JSX.Element { + // The Prism theme on SSR is always the default theme but the site theme can + // be in a different mode. React hydration doesn't update DOM styles that come + // from SSR. Hence force a re-render after mounting to apply the current + // relevant styles. + const isBrowser = useIsBrowser(); + const children = maybeStringifyChildren(rawChildren); + const CodeBlockComp = + typeof children === 'string' ? CodeBlockString : CodeBlockJSX; + return ( + + {children as string} + ); } -function CodeBlockContainer({ - as: Wrapper, +function CodeBlockContainer({ + as: As, ...props -}: {as: As} & ComponentProps) { +}: {as: T} & ComponentProps) { return ( - // @ts-expect-error: TODO bad typing here - From 35a77aa74de9f0034db3289c4a0e570335875393 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 15 Apr 2022 10:37:51 +0800 Subject: [PATCH 7/8] refactor --- .../src/theme/CodeBlock/index.tsx | 57 +++++++++---------- .../src/utils/codeBlockUtils.ts | 3 +- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index f77860afdea0..dbf000cb0aa3 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -118,10 +118,13 @@ function CodeBlockContainer({ as: As, ...props }: {as: T} & ComponentProps) { + const prismTheme = usePrismTheme(); + const prismCssVariables = getPrismCssVariables(prismTheme); return ( ({ // When the children is not a simple string, we just return a styled block // without actually highlighting. function CodeBlockJSX({children, className}: Props): JSX.Element { - const prismTheme = usePrismTheme(); - const prismCssVariables = getPrismCssVariables(prismTheme); return ( + className={clsx(styles.codeBlockStandalone, 'thin-scrollbar', className)}> {children} ); @@ -152,7 +152,7 @@ function CodeBlockString({ children, className: blockClassName = '', metastring, - title, + title: titleProp, showLineNumbers: showLineNumbersProp, language: languageProp, }: Omit & {children: string}): JSX.Element { @@ -162,35 +162,33 @@ function CodeBlockString({ const language = languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage; const prismTheme = usePrismTheme(); - const prismCssVariables = getPrismCssVariables(prismTheme); // We still parse the metastring in case we want to support more syntax in the // future. Note that MDX doesn't strip quotes when parsing metastring: // "title=\"xyz\"" => title: "\"xyz\"" - const codeBlockTitle = parseCodeBlockTitle(metastring) || title; + const title = parseCodeBlockTitle(metastring) || titleProp; const {highlightLines, code} = parseLines(children, metastring, language); const showLineNumbers = showLineNumbersProp || containsLineNumbers(metastring); return ( - - {({className, tokens, getLineProps, getTokenProps}) => ( - - {codeBlockTitle && ( -
{codeBlockTitle}
- )} -
+ + {title &&
{title}
} +
+ + {({className, tokens, getLineProps, getTokenProps}) => (
             
- - -
-
- )} - + )} + + +
+
); } diff --git a/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts b/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts index 0dd49cfb2bde..8dd5108d93f1 100644 --- a/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts @@ -182,11 +182,10 @@ export function getPrismCssVariables(prismTheme: PrismTheme): CSSProperties { backgroundColor: '--prism-background-color', }; - const properties: CSSProperties = {}; + const properties: {[key: string]: string} = {}; Object.entries(prismTheme.plain).forEach(([key, value]) => { const varName = mapping[key]; if (varName && typeof value === 'string') { - // @ts-expect-error: why css variables not in inline style type? properties[varName] = value; } }); From f82ea3164b958e1b9a1f29372c57dbd0550227ff Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 15 Apr 2022 16:31:10 +0800 Subject: [PATCH 8/8] do the actual split --- .../src/theme-classic.d.ts | 50 +++++ .../src/theme/CodeBlock/Container/index.tsx | 35 ++++ .../CodeBlock/Container/styles.module.css | 14 ++ .../src/theme/CodeBlock/Content/Element.tsx | 30 +++ .../src/theme/CodeBlock/Content/String.tsx | 94 +++++++++ .../CodeBlock/{ => Content}/styles.module.css | 36 ---- .../src/theme/CodeBlock/Line/index.tsx | 51 +++++ .../theme/CodeBlock/Line/styles.module.css | 34 ++++ .../src/theme/CodeBlock/index.tsx | 182 +----------------- 9 files changed, 312 insertions(+), 214 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/styles.module.css create mode 100644 packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/Element.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/String.tsx rename packages/docusaurus-theme-classic/src/theme/CodeBlock/{ => Content}/styles.module.css (60%) create mode 100644 packages/docusaurus-theme-classic/src/theme/CodeBlock/Line/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/CodeBlock/Line/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index f4cead69c404..233f229c9f84 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -170,6 +170,56 @@ declare module '@theme/CodeBlock/CopyButton' { export default function CopyButton(props: Props): JSX.Element; } +declare module '@theme/CodeBlock/Container' { + import type {ComponentProps} from 'react'; + + export default function CodeBlockContainer({ + as: As, + ...props + }: {as: T} & ComponentProps): JSX.Element; +} + +declare module '@theme/CodeBlock/Content/Element' { + import type {Props} from '@theme/CodeBlock'; + + export type {Props}; + + export default function CodeBlockElementContent(props: Props): JSX.Element; +} + +declare module '@theme/CodeBlock/Content/String' { + import type {Props as CodeBlockProps} from '@theme/CodeBlock'; + + export interface Props extends Omit { + readonly children: string; + } + + export default function CodeBlockStringContent(props: Props): JSX.Element; +} + +declare module '@theme/CodeBlock/Line' { + import type {ComponentProps} from 'react'; + import type Highlight from 'prism-react-renderer'; + + // Lib does not make this easy + type RenderProps = Parameters< + ComponentProps['children'] + >[0]; + type GetLineProps = RenderProps['getLineProps']; + type GetTokenProps = RenderProps['getTokenProps']; + type Token = RenderProps['tokens'][number][number]; + + export interface Props { + readonly line: Token[]; + readonly highlight: boolean; + readonly showLineNumbers: boolean; + readonly getLineProps: GetLineProps; + readonly getTokenProps: GetTokenProps; + } + + export default function CodeBlockLine(props: Props): JSX.Element; +} + declare module '@theme/DocCard' { import type {PropSidebarItem} from '@docusaurus/plugin-content-docs'; diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/index.tsx new file mode 100644 index 000000000000..293d700dad35 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/index.tsx @@ -0,0 +1,35 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, {type ComponentProps} from 'react'; +import clsx from 'clsx'; +import { + usePrismTheme, + getPrismCssVariables, + ThemeClassNames, +} from '@docusaurus/theme-common'; +import styles from './styles.module.css'; + +export default function CodeBlockContainer({ + as: As, + ...props +}: {as: T} & ComponentProps): JSX.Element { + const prismTheme = usePrismTheme(); + const prismCssVariables = getPrismCssVariables(prismTheme); + return ( + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/styles.module.css b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/styles.module.css new file mode 100644 index 000000000000..27adece6bd9b --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/styles.module.css @@ -0,0 +1,14 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.codeBlockContainer { + background: var(--prism-background-color); + color: var(--prism-color); + margin-bottom: var(--ifm-leading); + box-shadow: var(--ifm-global-shadow-lw); + border-radius: var(--ifm-code-border-radius); +} diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/Element.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/Element.tsx new file mode 100644 index 000000000000..3f130e80e6d6 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/Element.tsx @@ -0,0 +1,30 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import Container from '@theme/CodeBlock/Container'; +import clsx from 'clsx'; +import type {Props} from '@theme/CodeBlock/Content/Element'; + +import styles from './styles.module.css'; + +//
 tags in markdown map to CodeBlocks. They may contain JSX children. When
+// the children is not a simple string, we just return a styled block without
+// actually highlighting.
+export default function CodeBlockJSX({
+  children,
+  className,
+}: Props): JSX.Element {
+  return (
+    
+      {children}
+    
+  );
+}
diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/String.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/String.tsx
new file mode 100644
index 000000000000..d4e59b1718c5
--- /dev/null
+++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/String.tsx
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React from 'react';
+import {
+  useThemeConfig,
+  parseCodeBlockTitle,
+  parseLanguage,
+  parseLines,
+  containsLineNumbers,
+  usePrismTheme,
+} from '@docusaurus/theme-common';
+import clsx from 'clsx';
+import Highlight, {defaultProps, type Language} from 'prism-react-renderer';
+import Line from '@theme/CodeBlock/Line';
+import CopyButton from '@theme/CodeBlock/CopyButton';
+import Container from '@theme/CodeBlock/Container';
+import type {Props} from '@theme/CodeBlock/Content/String';
+
+import styles from './styles.module.css';
+
+export default function CodeBlockString({
+  children,
+  className: blockClassName = '',
+  metastring,
+  title: titleProp,
+  showLineNumbers: showLineNumbersProp,
+  language: languageProp,
+}: Props): JSX.Element {
+  const {
+    prism: {defaultLanguage},
+  } = useThemeConfig();
+  const language =
+    languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage;
+  const prismTheme = usePrismTheme();
+
+  // We still parse the metastring in case we want to support more syntax in the
+  // future. Note that MDX doesn't strip quotes when parsing metastring:
+  // "title=\"xyz\"" => title: "\"xyz\""
+  const title = parseCodeBlockTitle(metastring) || titleProp;
+
+  const {highlightLines, code} = parseLines(children, metastring, language);
+  const showLineNumbers =
+    showLineNumbersProp || containsLineNumbers(metastring);
+
+  return (
+    
+      {title && 
{title}
} +
+ + {({className, tokens, getLineProps, getTokenProps}) => ( +
+              
+                {tokens.map((line, i) => (
+                  
+                ))}
+              
+            
+ )} +
+ +
+
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/styles.module.css similarity index 60% rename from packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/styles.module.css index 970367a7e601..6f043e05c4da 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/styles.module.css @@ -5,14 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -.codeBlockContainer { - background: var(--prism-background-color); - color: var(--prism-color); - margin-bottom: var(--ifm-leading); - box-shadow: var(--ifm-global-shadow-lw); - border-radius: var(--ifm-code-border-radius); -} - .codeBlockContent { position: relative; /* rtl:ignore */ @@ -62,31 +54,3 @@ white-space: pre-wrap; } } - -.codeLine { - display: table-row; - counter-increment: line-count; -} - -.codeLineNumber { - display: table-cell; - text-align: right; - width: 1%; - position: sticky; - left: 0; - padding: 0 var(--ifm-pre-padding); - background: var(--ifm-pre-background); -} - -.codeLineNumber::before { - content: counter(line-count); - opacity: 0.4; -} - -:global(.docusaurus-highlight-code-line) .codeLineNumber::before { - opacity: 0.8; -} - -.codeLineContent { - padding-right: var(--ifm-pre-padding); -} diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Line/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Line/index.tsx new file mode 100644 index 000000000000..79505f42d756 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Line/index.tsx @@ -0,0 +1,51 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import type {Props} from '@theme/CodeBlock/Line'; +import styles from './styles.module.css'; + +export default function CodeBlockLine({ + line, + highlight, + showLineNumbers, + getLineProps, + getTokenProps, +}: Props): JSX.Element { + if (line.length === 1 && line[0]!.content === '\n') { + line[0]!.content = ''; + } + + const lineProps = getLineProps({ + line, + ...(showLineNumbers && {className: styles.codeLine}), + }); + + if (highlight) { + lineProps.className += ' docusaurus-highlight-code-line'; + } + + const lineTokens = line.map((token, key) => ( + + )); + + return ( + + {showLineNumbers ? ( + <> + + {lineTokens} + + ) : ( + <> + {lineTokens} +
+ + )} +
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Line/styles.module.css b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Line/styles.module.css new file mode 100644 index 000000000000..7597f007c5b2 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Line/styles.module.css @@ -0,0 +1,34 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.codeLine { + display: table-row; + counter-increment: line-count; +} + +.codeLineNumber { + display: table-cell; + text-align: right; + width: 1%; + position: sticky; + left: 0; + padding: 0 var(--ifm-pre-padding); + background: var(--ifm-pre-background); +} + +.codeLineNumber::before { + content: counter(line-count); + opacity: 0.4; +} + +:global(.docusaurus-highlight-code-line) .codeLineNumber::before { + opacity: 0.8; +} + +.codeLineContent { + padding-right: var(--ifm-pre-padding); +} diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index dbf000cb0aa3..9565932fafcb 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -5,81 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import React, { - isValidElement, - type ComponentProps, - type ReactNode, -} from 'react'; -import clsx from 'clsx'; -import Highlight, {defaultProps, type Language} from 'prism-react-renderer'; +import React, {isValidElement, type ReactNode} from 'react'; import useIsBrowser from '@docusaurus/useIsBrowser'; -import { - useThemeConfig, - parseCodeBlockTitle, - parseLanguage, - parseLines, - containsLineNumbers, - ThemeClassNames, - usePrismTheme, - getPrismCssVariables, -} from '@docusaurus/theme-common'; -import CopyButton from '@theme/CodeBlock/CopyButton'; import type {Props} from '@theme/CodeBlock'; - -import styles from './styles.module.css'; - -// Lib does not make this easy -type RenderProps = Parameters['children']>[0]; -type GetLineProps = RenderProps['getLineProps']; -type GetTokenProps = RenderProps['getTokenProps']; -type Token = RenderProps['tokens'][number][number]; - -function CodeBlockLine({ - line, - highlight, - showLineNumbers, - getLineProps, - getTokenProps, -}: { - line: Token[]; - highlight: boolean; - showLineNumbers: boolean; - getLineProps: GetLineProps; - getTokenProps: GetTokenProps; -}) { - if (line.length === 1 && line[0]!.content === '\n') { - line[0]!.content = ''; - } - - const lineProps = getLineProps({ - line, - ...(showLineNumbers && {className: styles.codeLine}), - }); - - if (highlight) { - lineProps.className += ' docusaurus-highlight-code-line'; - } - - const lineTokens = line.map((token, key) => ( - - )); - - return ( - - {showLineNumbers ? ( - <> - - {lineTokens} - - ) : ( - <> - {lineTokens} -
- - )} -
- ); -} +import ElementContent from '@theme/CodeBlock/Content/Element'; +import StringContent from '@theme/CodeBlock/Content/String'; /** * Best attempt to make the children a plain string so it is copyable. If there @@ -106,114 +36,10 @@ export default function CodeBlock({ const isBrowser = useIsBrowser(); const children = maybeStringifyChildren(rawChildren); const CodeBlockComp = - typeof children === 'string' ? CodeBlockString : CodeBlockJSX; + typeof children === 'string' ? StringContent : ElementContent; return ( {children as string} ); } - -function CodeBlockContainer({ - as: As, - ...props -}: {as: T} & ComponentProps) { - const prismTheme = usePrismTheme(); - const prismCssVariables = getPrismCssVariables(prismTheme); - return ( - - ); -} - -//
 tags in markdown map to CodeBlocks. They may contain JSX children.
-// When the children is not a simple string, we just return a styled block
-// without actually highlighting.
-function CodeBlockJSX({children, className}: Props): JSX.Element {
-  return (
-    
-      {children}
-    
-  );
-}
-
-function CodeBlockString({
-  children,
-  className: blockClassName = '',
-  metastring,
-  title: titleProp,
-  showLineNumbers: showLineNumbersProp,
-  language: languageProp,
-}: Omit & {children: string}): JSX.Element {
-  const {
-    prism: {defaultLanguage},
-  } = useThemeConfig();
-  const language =
-    languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage;
-  const prismTheme = usePrismTheme();
-
-  // We still parse the metastring in case we want to support more syntax in the
-  // future. Note that MDX doesn't strip quotes when parsing metastring:
-  // "title=\"xyz\"" => title: "\"xyz\""
-  const title = parseCodeBlockTitle(metastring) || titleProp;
-
-  const {highlightLines, code} = parseLines(children, metastring, language);
-  const showLineNumbers =
-    showLineNumbersProp || containsLineNumbers(metastring);
-
-  return (
-    
-      {title && 
{title}
} -
- - {({className, tokens, getLineProps, getTokenProps}) => ( -
-              
-                {tokens.map((line, i) => (
-                  
-                ))}
-              
-            
- )} -
- -
-
- ); -}