diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index 60859031c2..de6f07a928 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -15,7 +15,8 @@ import type {Props} from '@theme/CodeBlock'; import Translate, {translate} from '@docusaurus/Translate'; import styles from './styles.module.css'; -import {useThemeConfig} from '@docusaurus/theme-common'; + +import {useThemeConfig, parseCodeBlockTitle} from '@docusaurus/theme-common'; const highlightLinesRangeRegex = /{([\d,-]+)}/; const getHighlightDirectiveRegex = ( @@ -85,7 +86,6 @@ const highlightDirectiveRegex = (lang) => { return getHighlightDirectiveRegex(); } }; -const codeBlockTitleRegex = /(?:title=")(.*)(?:")/; export default function CodeBlock({ children, @@ -109,7 +109,7 @@ export default function CodeBlock({ const button = useRef(null); let highlightLines: number[] = []; - let codeBlockTitle = ''; + const codeBlockTitle = parseCodeBlockTitle(metastring); const prismTheme = usePrismTheme(); @@ -123,12 +123,6 @@ export default function CodeBlock({ highlightLines = rangeParser(highlightLinesRange).filter((n) => n > 0); } - if (metastring && codeBlockTitleRegex.test(metastring)) { - // Tested above - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, prefer-destructuring - codeBlockTitle = metastring.match(codeBlockTitleRegex)![1]; - } - let language = languageClassName && // Force Prism's language union type to `any` because it does not contain all available languages diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 29e4dd8af3..5792d43862 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -20,6 +20,8 @@ export {createStorageSlot, listStorageKeys} from './utils/storageUtils'; export {useAlternatePageUtils} from './utils/useAlternatePageUtils'; +export {parseCodeBlockTitle} from './utils/codeBlockUtils'; + export {docVersionSearchTag, DEFAULT_SEARCH_TAG} from './utils/searchUtils'; export {isDocsPluginEnabled} from './utils/docsUtils'; diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/codeBlockUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/codeBlockUtils.test.ts new file mode 100644 index 0000000000..80e0f49fe5 --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/__tests__/codeBlockUtils.test.ts @@ -0,0 +1,54 @@ +/** + * 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 {parseCodeBlockTitle} from '../codeBlockUtils'; + +describe('parseCodeBlockTitle', () => { + test('should parse double quote delimited title', () => { + expect(parseCodeBlockTitle(`title="index.js"`)).toEqual(`index.js`); + }); + + test('should parse single quote delimited title', () => { + expect(parseCodeBlockTitle(`title='index.js'`)).toEqual(`index.js`); + }); + + test('should not parse mismatched quote delimiters', () => { + expect(parseCodeBlockTitle(`title="index.js'`)).toEqual(``); + }); + + test('should parse undefined metastring', () => { + expect(parseCodeBlockTitle(undefined)).toEqual(``); + }); + + test('should parse metastring with no title specified', () => { + expect(parseCodeBlockTitle(`{1,2-3}`)).toEqual(``); + }); + + test('should parse with multiple metadatas title first', () => { + expect(parseCodeBlockTitle(`title="index.js" label="JavaScript"`)).toEqual( + `index.js`, + ); + }); + + test('should parse with multiple metadatas title last', () => { + expect(parseCodeBlockTitle(`label="JavaScript" title="index.js"`)).toEqual( + `index.js`, + ); + }); + + test('should parse double quotes when delimited by single quotes', () => { + expect(parseCodeBlockTitle(`title='console.log("Hello, World!")'`)).toEqual( + `console.log("Hello, World!")`, + ); + }); + + test('should parse single quotes when delimited by double quotes', () => { + expect(parseCodeBlockTitle(`title="console.log('Hello, World!')"`)).toEqual( + `console.log('Hello, World!')`, + ); + }); +}); diff --git a/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts b/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts new file mode 100644 index 0000000000..5bb0cc941c --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts @@ -0,0 +1,12 @@ +/** + * 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. + */ + +const codeBlockTitleRegex = /title=(["'])(.*?)\1/; + +export function parseCodeBlockTitle(metastring?: string): string { + return metastring?.match(codeBlockTitleRegex)?.[2] ?? ''; +}