Skip to content

feat(theme-classic): Add CodeBlockToken component for swizzling #11022

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

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ const plugin: Plugin<unknown[], Root> = function plugin(): Transformer<Root> {

node.data.hProperties = node.data.hProperties || {};
node.data.hProperties.metastring = node.meta;

// Retrocompatible support for live codeblock metastring
// Not really the appropriate place to handle that :s
node.data.hProperties.live = node.meta?.split(' ').includes('live');
});
};
};
Expand Down
14 changes: 14 additions & 0 deletions packages/docusaurus-theme-classic/src/theme-classic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ declare module '@theme/BlogLayout' {

declare module '@theme/CodeBlock' {
import type {ReactNode} from 'react';
import type {CodeBlockMetaOptions} from '@docusaurus/theme-common/internal';

export interface Props {
readonly children: ReactNode;
Expand All @@ -413,6 +414,7 @@ declare module '@theme/CodeBlock' {
readonly title?: ReactNode;
readonly language?: string;
readonly showLineNumbers?: boolean | number;
readonly metaOptions?: CodeBlockMetaOptions;
}

export default function CodeBlock(props: Props): ReactNode;
Expand Down Expand Up @@ -500,6 +502,18 @@ declare module '@theme/CodeBlock/WordWrapButton' {
export default function WordWrapButton(props: Props): ReactNode;
}

declare module '@theme/CodeBlock/Token' {
import type {ReactNode} from 'react';
import type {Props as LineProps} from '@theme/CodeBlock/Line';
import type {TokenOutputProps} from 'prism-react-renderer';

export interface Props extends TokenOutputProps {
line: LineProps;
}

export default function CodeBlockToken(props: Props): ReactNode;
}

declare module '@theme/DocCard' {
import type {ReactNode} from 'react';
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import React, {type ReactNode} from 'react';
import clsx from 'clsx';
import {useThemeConfig, usePrismTheme} from '@docusaurus/theme-common';
import {
parseCodeBlockTitle,
parseCodeBlockMetaOptions,
parseLanguage,
parseLines,
getCodeBlockTitle,
getLineNumbersStart,
useCodeWordWrap,
} from '@docusaurus/theme-common/internal';
Expand Down Expand Up @@ -39,6 +40,7 @@ export default function CodeBlockString({
title: titleProp,
showLineNumbers: showLineNumbersProp,
language: languageProp,
metaOptions: metaOptionsProp,
}: Props): ReactNode {
const {
prism: {defaultLanguage, magicComments},
Expand All @@ -51,10 +53,15 @@ export default function CodeBlockString({
const wordWrap = useCodeWordWrap();
const isBrowser = useIsBrowser();

const metaOptions = parseCodeBlockMetaOptions(metastring, metaOptionsProp);

// 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 title = getCodeBlockTitle({
titleProp,
metaOptions,
});

const {lineClassNames, code} = parseLines(children, {
metastring,
Expand All @@ -63,7 +70,7 @@ export default function CodeBlockString({
});
const lineNumbersStart = getLineNumbersStart({
showLineNumbers: showLineNumbersProp,
metastring,
metaOptions,
});

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import React, {type ReactNode} from 'react';
import clsx from 'clsx';
import type {Props} from '@theme/CodeBlock/Line';
import CodeBlockToken from '@theme/CodeBlock/Token';

import styles from './styles.module.css';

Expand All @@ -26,13 +27,14 @@ function fixLineBreak(line: Token[]) {
return line;
}

export default function CodeBlockLine({
line: lineProp,
classNames,
showLineNumbers,
getLineProps,
getTokenProps,
}: Props): ReactNode {
export default function CodeBlockLine(props: Props): ReactNode {
const {
line: lineProp,
classNames,
showLineNumbers,
getLineProps,
getTokenProps,
} = props;
const line = fixLineBreak(lineProp);

const lineProps = getLineProps({
Expand All @@ -41,7 +43,7 @@ export default function CodeBlockLine({
});

const lineTokens = line.map((token, key) => (
<span key={key} {...getTokenProps({token})} />
<CodeBlockToken key={key} line={props} {...getTokenProps({token})} />
));

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* 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 ReactNode} from 'react';
import type {Props} from '@theme/CodeBlock/Token';

export default function CodeBlockToken(props: Props): ReactNode {
// We omit the "line" information in the default rendering,
// but its meant for devs who devs who Swizzle this component.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

const {line, ...tokenProps} = props;
return <span {...tokenProps} />;
}
4 changes: 3 additions & 1 deletion packages/docusaurus-theme-common/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ export {ColorModeProvider} from './contexts/colorMode';
export {useAlternatePageUtils} from './utils/useAlternatePageUtils';

export {
parseCodeBlockTitle,
parseCodeBlockMetaOptions,
parseLanguage,
parseLines,
getCodeBlockTitle,
getLineNumbersStart,
type CodeBlockMetaOptions,
} from './utils/codeBlockUtils';

export {DEFAULT_SEARCH_TAG} from './utils/searchUtils';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,149 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`getCodeBlockTitle returns option with empty prop 1`] = `"Option"`;

exports[`getCodeBlockTitle returns option with filled prop 1`] = `"Option"`;

exports[`getCodeBlockTitle returns option with undefined prop 1`] = `"Option"`;

exports[`getCodeBlockTitle returns titleProp with empty options 1`] = `"Prop"`;

exports[`getCodeBlockTitle returns titleProp with empty string on option 1`] = `"Prop"`;

exports[`getCodeBlockTitle with nothing set 1`] = `undefined`;

exports[`getLineNumbersStart from metastring parses flags as 1 1`] = `1`;

exports[`getLineNumbersStart from metastring parses value 1`] = `10`;

exports[`getLineNumbersStart handles metadata combined with other options set as flag 1`] = `1`;

exports[`getLineNumbersStart handles metadata combined with other options set with number 1`] = `10`;

exports[`getLineNumbersStart handles metadata standalone set as flag 1`] = `1`;

exports[`getLineNumbersStart handles metadata standalone set with number 1`] = `10`;

exports[`getLineNumbersStart handles prop combined with metaoptions set to false 1`] = `undefined`;

exports[`getLineNumbersStart handles prop combined with metaoptions set to number 1`] = `10`;

exports[`getLineNumbersStart handles prop combined with metaoptions set to true 1`] = `1`;

exports[`getLineNumbersStart handles prop standalone set to false 1`] = `undefined`;

exports[`getLineNumbersStart handles prop standalone set to number 1`] = `10`;

exports[`getLineNumbersStart handles prop standalone set to true 1`] = `1`;

exports[`getLineNumbersStart with nothing set 1`] = `undefined`;

exports[`getLineNumbersStart with nothing set 2`] = `undefined`;

exports[`getLineNumbersStart with parsed metaoption handles metadata combined with other options set as flag 1`] = `1`;

exports[`getLineNumbersStart with parsed metaoption handles metadata combined with other options set with number 1`] = `10`;

exports[`getLineNumbersStart with parsed metaoption handles metadata standalone set as flag 1`] = `1`;

exports[`getLineNumbersStart with parsed metaoption handles metadata standalone set with number 1`] = `10`;

exports[`getLineNumbersStart with parsed metaoption handles prop combined with metaoptions set to false 1`] = `undefined`;

exports[`getLineNumbersStart with parsed metaoption handles prop combined with metaoptions set to number 1`] = `10`;

exports[`getLineNumbersStart with parsed metaoption handles prop combined with metaoptions set to true 1`] = `1`;

exports[`getLineNumbersStart with parsed metaoption handles prop standalone set to false 1`] = `undefined`;

exports[`getLineNumbersStart with parsed metaoption handles prop standalone set to number 1`] = `10`;

exports[`getLineNumbersStart with parsed metaoption handles prop standalone set to true 1`] = `1`;

exports[`getLineNumbersStart with parsed metaoption with nothing set 1`] = `undefined`;

exports[`getLineNumbersStart with parsed metaoption with nothing set 2`] = `undefined`;

exports[`parseCodeBlockMetaOptions any option double quotes as string 1`] = `
{
"PascalCase": "Hello'Docusaurus Options",
"UPPER_CASE": "Hello'Docusaurus Options",
"camelCase": "Hello'Docusaurus Options",
"kebab-case": "Hello'Docusaurus Options",
"lowercase": "Hello'Docusaurus Options",
}
`;

exports[`parseCodeBlockMetaOptions any option false 1`] = `
{
"PascalCase": false,
"UPPER_CASE": false,
"camelCase": false,
"kebab-case": false,
"lowercase": false,
}
`;

exports[`parseCodeBlockMetaOptions any option flag as true 1`] = `
{
"PascalCase": true,
"UPPER_CASE": true,
"camelCase": true,
"kebab-case": true,
"lowercase": true,
}
`;

exports[`parseCodeBlockMetaOptions any option float numbers 1`] = `
{
"PascalCase": 3.3,
"UPPER_CASE": 4.4,
"camelCase": 2.2,
"kebab-case": 5.5,
"lowercase": 1.1,
}
`;

exports[`parseCodeBlockMetaOptions any option integer numbers 1`] = `
{
"PascalCase": 3,
"UPPER_CASE": 4,
"camelCase": 2,
"kebab-case": 5,
"lowercase": 1,
}
`;

exports[`parseCodeBlockMetaOptions any option non quoted value as string 1`] = `
{
"PascalCase": "simple",
"UPPER_CASE": "simple",
"camelCase": "simple",
"kebab-case": "simple",
"lowercase": "simple",
}
`;

exports[`parseCodeBlockMetaOptions any option single quotes as string 1`] = `
{
"PascalCase": "Hello"Docusaurus Options",
"UPPER_CASE": "Hello"Docusaurus Options",
"camelCase": "Hello"Docusaurus Options",
"kebab-case": "Hello"Docusaurus Options",
"lowercase": "Hello"Docusaurus Options",
}
`;

exports[`parseCodeBlockMetaOptions any option true 1`] = `
{
"PascalCase": true,
"UPPER_CASE": true,
"camelCase": true,
"kebab-case": true,
"lowercase": true,
}
`;

exports[`parseLines does not parse content with metastring 1`] = `
{
"code": "aaaaa
Expand Down
Loading