diff --git a/packages/docusaurus-mdx-loader/src/__tests__/processor.test.ts b/packages/docusaurus-mdx-loader/src/__tests__/processor.test.ts
index 46286ad41502..abc6eef7ea96 100644
--- a/packages/docusaurus-mdx-loader/src/__tests__/processor.test.ts
+++ b/packages/docusaurus-mdx-loader/src/__tests__/processor.test.ts
@@ -73,6 +73,16 @@ const TestMDXComponents = {
React.createElement('header', null, title ?? 'Default title'),
children,
),
+ // Like react-helmet-async, Head only handles its element children
+ // Rendered separately, otherwise React hoists
and out of it
+ Head: ({children}: {children?: ReactNode}) =>
+ React.createElement('test-head', {
+ dangerouslySetInnerHTML: {
+ __html: renderToStaticMarkup(
+ React.Children.toArray(children).filter(React.isValidElement),
+ ),
+ },
+ }),
};
async function renderContent(content: string, options: Options) {
@@ -265,6 +275,41 @@ Body
});
});
+ describe('head', () => {
+ describe.each(['md', 'mdx'] as const)('%s format', (format) => {
+ it('renders head metadata', async () => {
+ const html = await renderContent(
+ `
+ Custom title
+
+
+
+Body`,
+ createOptions({markdownConfig: {format}}),
+ );
+ expect(html).toBe(
+ 'Custom title\nBody
',
+ );
+ });
+ });
+
+ it('renders structured data with the md format', async () => {
+ const html = await renderContent(
+ `
+
+`,
+ createOptions({markdownConfig: {format: 'md'}}),
+ );
+ expect(html).toBe(
+ ``,
+ );
+ });
+ });
+
it.each([true, false])('supports emoji=%s', async (emoji) => {
const options = createOptions({markdownConfig: {emoji}});
const result = await processContent(
diff --git a/packages/docusaurus-mdx-loader/src/remark/head/index.ts b/packages/docusaurus-mdx-loader/src/remark/head/index.ts
index 4ec2b49756e8..2ca737e1cf9c 100644
--- a/packages/docusaurus-mdx-loader/src/remark/head/index.ts
+++ b/packages/docusaurus-mdx-loader/src/remark/head/index.ts
@@ -6,18 +6,53 @@
*/
import {visit} from 'unist-util-visit';
+import {transformNode} from '../utils';
import type {Transformer} from 'unified';
import type {Root} from 'mdast';
+import type {MdxJsxFlowElement} from 'mdast-util-mdx';
// Transform to
// MDX 2 doesn't allow to substitute html elements with the provider anymore
export default function plugin(): Transformer {
return (root) => {
- visit(root, 'mdxJsxFlowElement', (node) => {
- if (node.name === 'head') {
- node.name = 'Head';
- }
- });
+ transformJsxHead(root);
+ transformHtmlHead(root);
};
}
+
+// With the MDX format, is a JSX element
+function transformJsxHead(root: Root) {
+ visit(root, 'mdxJsxFlowElement', (node) => {
+ if (node.name === 'head') {
+ node.name = 'Head';
+ }
+ });
+}
+
+// With the CommonMark format, is a raw HTML block
+// rehype-raw parses raw HTML in body context, where the tag is dropped
+// We wrap its content in a JSX element, that rehype-raw passes through
+// The content remains raw HTML that rehype-raw parses inside
+function transformHtmlHead(root: Root) {
+ visit(root, 'html', (node) => {
+ const headContent = getHeadContent(node.value);
+ if (headContent === undefined) {
+ return;
+ }
+ const headElement: MdxJsxFlowElement = {
+ type: 'mdxJsxFlowElement',
+ name: 'Head',
+ attributes: [],
+ children: [{type: 'html', value: headContent}],
+ position: node.position,
+ };
+ transformNode(node, headElement);
+ });
+}
+
+const HeadHtmlRegExp = /^(?.*)<\/head>$/s;
+
+function getHeadContent(html: string): string | undefined {
+ return HeadHtmlRegExp.exec(html.trim())?.groups?.content;
+}
diff --git a/website/_dogfooding/_pages tests/markdown-tests-md.md b/website/_dogfooding/_pages tests/markdown-tests-md.md
index 1f09421176ce..ba7df4fa158b 100644
--- a/website/_dogfooding/_pages tests/markdown-tests-md.md
+++ b/website/_dogfooding/_pages tests/markdown-tests-md.md
@@ -14,19 +14,21 @@ This file should be interpreted in a more CommonMark compliant way
HEAD Markdown Page tests title
+
```
HEAD Markdown Page tests title
+
-:::danger
-
-TODO unsupported (yet), see [issue](https://github.com/meta/docusaurus/issues/9092)
-
-:::
+The title, keywords and structured data should be in the `` of the static HTML page
## Comment
@@ -154,12 +156,6 @@ function Clock(props) {
}
```
-:::danger
-
-TODO unsupported (yet), see [issue](https://github.com/meta/docusaurus/issues/9092)
-
-:::
-
## Mermaid
```mermaid