diff --git a/packages/docusaurus-mdx-loader/package.json b/packages/docusaurus-mdx-loader/package.json index 6890e0f33bfc..c15f5d073238 100644 --- a/packages/docusaurus-mdx-loader/package.json +++ b/packages/docusaurus-mdx-loader/package.json @@ -18,8 +18,6 @@ }, "license": "MIT", "dependencies": { - "@babel/parser": "^7.22.7", - "@babel/traverse": "^7.22.8", "@docusaurus/logger": "3.0.0", "@docusaurus/utils": "3.0.0", "@docusaurus/utils-validation": "3.0.0", diff --git a/packages/docusaurus-mdx-loader/src/index.ts b/packages/docusaurus-mdx-loader/src/index.ts index 4a669fce6732..41fbf60da46b 100644 --- a/packages/docusaurus-mdx-loader/src/index.ts +++ b/packages/docusaurus-mdx-loader/src/index.ts @@ -7,7 +7,7 @@ import {mdxLoader} from './loader'; -import type {TOCItem as TOCItemImported} from './remark/toc'; +import type {TOCItem as TOCItemImported} from './remark/toc/types'; export default mdxLoader; diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/SomeComponent.js b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/SomeComponent.js new file mode 100644 index 000000000000..eaaa9a4fcade --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/SomeComponent.js @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function SomeComponent() { + return
Some component
; +} diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial1.md b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial1.md new file mode 100644 index 000000000000..08a294ad26e4 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial1.md @@ -0,0 +1,7 @@ +## Partial 1 + +Partial 1 + +### Partial 1 Sub Heading + +Content diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial2-nested.md b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial2-nested.md new file mode 100644 index 000000000000..9443eaac0087 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial2-nested.md @@ -0,0 +1,3 @@ +## Partial 2 Nested + +Partial 2 Nested diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial2.mdx b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial2.mdx new file mode 100644 index 000000000000..f5b6aee9a0ed --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial2.mdx @@ -0,0 +1,11 @@ +## Partial 2 + +Partial 2 + +### Partial 2 Sub Heading + +Content + +import Partial2Nested from './partial2-nested.md'; + + diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial3-unused.mdx b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial3-unused.mdx new file mode 100644 index 000000000000..da4bf2d00a39 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/_partial3-unused.mdx @@ -0,0 +1,7 @@ +## Partial 3 + +Partial 3 + +### Partial 3 Sub Heading + +Content diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/index.mdx b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/index.mdx new file mode 100644 index 000000000000..0742cb932124 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/index.mdx @@ -0,0 +1,49 @@ +import Partial1 from './_partial1.md'; + +import SomeComponent from './SomeComponent'; + +# Index + +Some text + +import Partial2 from './_partial2.md'; + +## Index section 1 + +Foo + + + +Some text + + + +## Index section 2 + + + +## Unused partials + +Unused partials (that are only imported but not rendered) shouldn't alter the TOC + +import UnusedPartialImport from './_partial3.md'; + +## NonExisting Partials + +Partials that do not exist should alter the TOC + +It's not the responsibility of the Remark plugin to check for their existence + +import DoesNotExist from './_doesNotExist.md'; + + + +## Duplicate partials + +It's fine if we use partials at the end + + + +And we can use the partial multiple times! + + diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/partial-used-before-import.mdx b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/partial-used-before-import.mdx new file mode 100644 index 000000000000..0c8c47327928 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__fixtures__/partials/partial-used-before-import.mdx @@ -0,0 +1,7 @@ +# Partial used before import + +While it looks weird to import after usage, this remains valid MDX usage. + + + +import Partial from './_partial.md'; diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__snapshots__/index.test.ts.snap index 5cad7ef21337..0a712732dc29 100644 --- a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/__snapshots__/index.test.ts.snap @@ -1,238 +1,601 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`toc remark plugin does not overwrite TOC var if no TOC 1`] = ` -"foo - -\`bar\` - -\`\`\`js -baz -\`\`\` - +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; export const toc = 1; +function _createMdxContent(props) { + const _components = { + code: "code", + p: "p", + pre: "pre", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.p, { + children: "foo" + }), "/n", _jsx(_components.p, { + children: _jsx(_components.code, { + children: "bar" + }) + }), "/n", _jsx(_components.pre, { + children: _jsx(_components.code, { + className: "language-js", + children: "baz/n" + }) + })] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} " `; exports[`toc remark plugin escapes inline code 1`] = ` -"export const toc = [ - { - value: '<Head />', - id: 'head-', - level: 2 - }, - { - value: '<Head>Test</Head>', - id: 'headtesthead', - level: 3 - }, - { - value: '<div />', - id: 'div-', - level: 2 - }, - { - value: '<div> Test </div>', - id: 'div-test-div', - level: 2 - }, - { - value: '<div><i>Test</i></div>', - id: 'divitestidiv', - level: 2 - }, - { - value: '<div><i>Test</i></div>', - id: 'divitestidiv-1', - level: 2 - } -] - -## \`\` - -### \`Test\` - -## \`
\` - -## \`
Test
\` - -## \`
Test
\` - -## [\`
Test
\`](/some/link) +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +export const toc = [{ + "value": "<Head />", + "id": "head-", + "level": 2 +}, { + "value": "<Head>Test</Head>", + "id": "headtesthead", + "level": 3 +}, { + "value": "<div />", + "id": "div-", + "level": 2 +}, { + "value": "<div> Test </div>", + "id": "div-test-div", + "level": 2 +}, { + "value": "<div><i>Test</i></div>", + "id": "divitestidiv", + "level": 2 +}, { + "value": "<div><i>Test</i></div>", + "id": "divitestidiv-1", + "level": 2 +}]; +function _createMdxContent(props) { + const _components = { + a: "a", + code: "code", + h2: "h2", + h3: "h3", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h2, { + id: "head-", + children: _jsx(_components.code, { + children: "" + }) + }), "/n", _jsx(_components.h3, { + id: "headtesthead", + children: _jsx(_components.code, { + children: "Test" + }) + }), "/n", _jsx(_components.h2, { + id: "div-", + children: _jsx(_components.code, { + children: "
" + }) + }), "/n", _jsx(_components.h2, { + id: "div-test-div", + children: _jsx(_components.code, { + children: "
Test
" + }) + }), "/n", _jsx(_components.h2, { + id: "divitestidiv", + children: _jsx(_components.code, { + children: "
Test
" + }) + }), "/n", _jsx(_components.h2, { + id: "divitestidiv-1", + children: _jsx(_components.a, { + href: "/some/link", + children: _jsx(_components.code, { + children: "
Test
" + }) + }) + })] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} " `; exports[`toc remark plugin exports even with existing name 1`] = ` -"export const toc = [ - { - value: 'Thanos', - id: 'thanos', - level: 2 - }, - { - value: 'Tony Stark', - id: 'tony-stark', - level: 2 - }, - { - value: 'Avengers', - id: 'avengers', - level: 3 - } -] - -## Thanos - -## Tony Stark - -### Avengers +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +export const toc = ['replaceMe']; +function _createMdxContent(props) { + const _components = { + h2: "h2", + h3: "h3", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h2, { + id: "thanos", + children: "Thanos" + }), "/n", _jsx(_components.h2, { + id: "tony-stark", + children: "Tony Stark" + }), "/n", _jsx(_components.h3, { + id: "avengers", + children: "Avengers" + })] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} " `; exports[`toc remark plugin handles empty headings 1`] = ` -"export const toc = [] - -# Ignore this - -## - -## ![](an-image.svg) +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +export const toc = []; +function _createMdxContent(props) { + const _components = { + h1: "h1", + h2: "h2", + img: "img", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h1, { + id: "ignore-this", + children: "Ignore this" + }), "/n", _jsx(_components.h2, { + id: "" + }), "/n", _jsx(_components.h2, { + id: "-1", + children: _jsx(_components.img, { + src: "an-image.svg", + alt: "" + }) + })] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} " `; exports[`toc remark plugin inserts below imports 1`] = ` -"import something from 'something'; - +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +import something from 'something'; import somethingElse from 'something-else'; - -export const toc = [ - { - value: 'Title', - id: 'title', - level: 2 - }, - { - value: 'Test', - id: 'test', - level: 2 - }, - { - value: 'Again', - id: 'again', - level: 3 - } -] - -## Title - -## Test - -### Again - -Content. +export const toc = [{ + "value": "Title", + "id": "title", + "level": 2 +}, { + "value": "Test", + "id": "test", + "level": 2 +}, { + "value": "Again", + "id": "again", + "level": 3 +}]; +function _createMdxContent(props) { + const _components = { + h2: "h2", + h3: "h3", + p: "p", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h2, { + id: "title", + children: "Title" + }), "/n", _jsx(_components.h2, { + id: "test", + children: "Test" + }), "/n", _jsx(_components.h3, { + id: "again", + children: "Again" + }), "/n", _jsx(_components.p, { + children: "Content." + })] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} " `; exports[`toc remark plugin outputs empty array for no TOC 1`] = ` -"export const toc = [] - -foo - -\`bar\` - -\`\`\`js -baz -\`\`\` +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +export const toc = []; +function _createMdxContent(props) { + const _components = { + code: "code", + p: "p", + pre: "pre", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.p, { + children: "foo" + }), "/n", _jsx(_components.p, { + children: _jsx(_components.code, { + children: "bar" + }) + }), "/n", _jsx(_components.pre, { + children: _jsx(_components.code, { + className: "language-js", + children: "baz/n" + }) + })] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} " `; exports[`toc remark plugin works on non text phrasing content 1`] = ` -"export const toc = [ - { - value: 'Emphasis', - id: 'emphasis', - level: 2 - }, - { - value: 'Importance', - id: 'importance', - level: 3 - }, - { - value: 'Strikethrough', - id: 'strikethrough', - level: 2 - }, - { - value: 'HTML', - id: 'html', - level: 2 - }, - { - value: 'inline.code()', - id: 'inlinecode', - level: 2 - }, - { - value: 'some styled heading test', - id: 'some-styled-heading--test', - level: 2 - } -] - -## *Emphasis* - -### **Importance** - -## ~~Strikethrough~~ - -## HTML - -## \`inline.code()\` - -## some styled heading test +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +export const toc = [{ + "value": "Emphasis", + "id": "emphasis", + "level": 2 +}, { + "value": "Importance", + "id": "importance", + "level": 3 +}, { + "value": "Strikethrough", + "id": "strikethrough", + "level": 2 +}, { + "value": "HTML", + "id": "html", + "level": 2 +}, { + "value": "inline.code()", + "id": "inlinecode", + "level": 2 +}, { + "value": "some styled heading test", + "id": "some-styled-heading--test", + "level": 2 +}]; +function _createMdxContent(props) { + const _components = { + code: "code", + del: "del", + em: "em", + h2: "h2", + h3: "h3", + strong: "strong", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h2, { + id: "emphasis", + children: _jsx(_components.em, { + children: "Emphasis" + }) + }), "/n", _jsx(_components.h3, { + id: "importance", + children: _jsx(_components.strong, { + children: "Importance" + }) + }), "/n", _jsx(_components.h2, { + id: "strikethrough", + children: _jsx(_components.del, { + children: "Strikethrough" + }) + }), "/n", _jsx(_components.h2, { + id: "html", + children: _jsx("i", { + children: "HTML" + }) + }), "/n", _jsx(_components.h2, { + id: "inlinecode", + children: _jsx(_components.code, { + children: "inline.code()" + }) + }), "/n", _jsxs(_components.h2, { + id: "some-styled-heading--test", + children: ["some ", _jsx("span", { + className: "some-class", + style: { + border: "solid" + }, + children: "styled" + }), " ", _jsx("strong", { + children: "heading" + }), " ", _jsx("span", { + class: "myClass", + className: "myClassName <> weird char", + "data-random-attr": "456" + }), " test"] + })] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} " `; exports[`toc remark plugin works on text content 1`] = ` -"export const toc = [ - { - value: 'Endi', - id: 'endi', - level: 3 - }, - { - value: 'Endi', - id: 'endi-1', - level: 2 - }, - { - value: 'Yangshun', - id: 'yangshun', - level: 3 - }, - { - value: 'I ♥ unicode.', - id: 'i--unicode', - level: 2 - } -] - -### Endi - -\`\`\`md -## This is ignored -\`\`\` - -## Endi - -Lorem ipsum - -### Yangshun +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +export const c = 1; +export const toc = [{ + "value": "Endi", + "id": "endi", + "level": 3 +}, { + "value": "Endi", + "id": "endi-1", + "level": 2 +}, { + "value": "Yangshun", + "id": "yangshun", + "level": 3 +}, { + "value": "I ♥ unicode.", + "id": "i--unicode", + "level": 2 +}]; +function _createMdxContent(props) { + const _components = { + code: "code", + h2: "h2", + h3: "h3", + p: "p", + pre: "pre", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h3, { + id: "endi", + children: "Endi" + }), "/n", _jsx(_components.pre, { + children: _jsx(_components.code, { + className: "language-md", + children: "## This is ignored/n" + }) + }), "/n", _jsx(_components.h2, { + id: "endi-1", + children: "Endi" + }), "/n", _jsx(_components.p, { + children: "Lorem ipsum" + }), "/n", _jsx(_components.h3, { + id: "yangshun", + children: "Yangshun" + }), "/n", _jsx(_components.p, { + children: "Some content here" + }), "/n", _jsx(_components.h2, { + id: "i--unicode", + children: "I ♥ unicode." + })] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} +" +`; -Some content here +exports[`toc remark plugin works with imported markdown 1`] = ` +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +import Partial1, {toc as __tocPartial1} from './_partial1.md'; +import SomeComponent from './SomeComponent'; +import Partial2, {toc as __tocPartial2} from './_partial2.md'; +import UnusedPartialImport from './_partial3.md'; +import DoesNotExist, {toc as __tocDoesNotExist} from './_doesNotExist.md'; +export const toc = [{ + "value": "Index section 1", + "id": "index-section-1", + "level": 2 +}, ...__tocPartial1, { + "value": "Index section 2", + "id": "index-section-2", + "level": 2 +}, ...__tocPartial2, { + "value": "Unused partials", + "id": "unused-partials", + "level": 2 +}, { + "value": "NonExisting Partials", + "id": "nonexisting-partials", + "level": 2 +}, ...__tocDoesNotExist, { + "value": "Duplicate partials", + "id": "duplicate-partials", + "level": 2 +}, ...__tocPartial1, ...__tocPartial1]; +function _createMdxContent(props) { + const _components = { + h1: "h1", + h2: "h2", + p: "p", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h1, { + id: "index", + children: "Index" + }), "/n", _jsx(_components.p, { + children: "Some text" + }), "/n", "/n", _jsx(_components.h2, { + id: "index-section-1", + children: "Index section 1" + }), "/n", _jsx(_components.p, { + children: "Foo" + }), "/n", _jsx(Partial1, {}), "/n", _jsx(_components.p, { + children: "Some text" + }), "/n", _jsx(SomeComponent, {}), "/n", _jsx(_components.h2, { + id: "index-section-2", + children: "Index section 2" + }), "/n", _jsx(Partial2, {}), "/n", _jsx(_components.h2, { + id: "unused-partials", + children: "Unused partials" + }), "/n", _jsx(_components.p, { + children: "Unused partials (that are only imported but not rendered) shouldn't alter the TOC" + }), "/n", "/n", _jsx(_components.h2, { + id: "nonexisting-partials", + children: "NonExisting Partials" + }), "/n", _jsx(_components.p, { + children: "Partials that do not exist should alter the TOC" + }), "/n", _jsx(_components.p, { + children: "It's not the responsibility of the Remark plugin to check for their existence" + }), "/n", "/n", _jsx(DoesNotExist, {}), "/n", _jsx(_components.h2, { + id: "duplicate-partials", + children: "Duplicate partials" + }), "/n", _jsx(_components.p, { + children: "It's fine if we use partials at the end" + }), "/n", _jsx(Partial1, {}), "/n", _jsx(_components.p, { + children: "And we can use the partial multiple times!" + }), "/n", _jsx(Partial1, {})] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} +" +`; -## I ♥ unicode. +exports[`toc remark plugin works with partial imported after its usage 1`] = ` +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +import Partial, {toc as __tocPartial} from './_partial.md'; +export const toc = [...__tocPartial]; +function _createMdxContent(props) { + const _components = { + h1: "h1", + p: "p", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h1, { + id: "partial-used-before-import", + children: "Partial used before import" + }), "/n", _jsx(_components.p, { + children: "While it looks weird to import after usage, this remains valid MDX usage." + }), "/n", _jsx(Partial, {})] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} +" +`; -export const c = 1; +exports[`toc remark plugin works with partials importing other partials 1`] = ` +"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; +import Partial2Nested, {toc as __tocPartial2Nested} from './partial2-nested.md'; +export const toc = [{ + "value": "Partial 2", + "id": "partial-2", + "level": 2 +}, { + "value": "Partial 2 Sub Heading", + "id": "partial-2-sub-heading", + "level": 3 +}, ...__tocPartial2Nested]; +function _createMdxContent(props) { + const _components = { + h2: "h2", + h3: "h3", + p: "p", + ...props.components + }; + return _jsxs(_Fragment, { + children: [_jsx(_components.h2, { + id: "partial-2", + children: "Partial 2" + }), "/n", _jsx(_components.p, { + children: "Partial 2" + }), "/n", _jsx(_components.h3, { + id: "partial-2-sub-heading", + children: "Partial 2 Sub Heading" + }), "/n", _jsx(_components.p, { + children: "Content" + }), "/n", "/n", _jsx(Partial2Nested, {})] + }); +} +export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || ({}); + return MDXLayout ? _jsx(MDXLayout, { + ...props, + children: _jsx(_createMdxContent, { + ...props + }) + }) : _createMdxContent(props); +} " `; diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/index.test.ts b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/index.test.ts index 0468feea1d40..87bc5c6793dd 100644 --- a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/index.test.ts +++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/index.test.ts @@ -11,18 +11,23 @@ import plugin from '../index'; import headings from '../../headings/index'; const processFixture = async (name: string) => { - const {remark} = await import('remark'); const {default: gfm} = await import('remark-gfm'); - const {default: mdx} = await import('remark-mdx'); - const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); + const {compile} = await import('@mdx-js/mdx'); + + const filePath = path.join( + __dirname, + '__fixtures__', + name.endsWith('.mdx') ? name : `${name}.md`, + ); + const file = await vfile.read(filePath); - const result = await remark() - .use(headings) - .use(gfm) - .use(mdx) - .use(plugin) - .process(file); + + const result = await compile(file, { + format: 'mdx', + remarkPlugins: [headings, gfm, plugin], + rehypePlugins: [], + }); return result.value; }; @@ -70,4 +75,21 @@ describe('toc remark plugin', () => { const result = await processFixture('empty-headings'); expect(result).toMatchSnapshot(); }); + + it('works with imported markdown', async () => { + const result = await processFixture('partials/index.mdx'); + expect(result).toMatchSnapshot(); + }); + + it('works with partials importing other partials', async () => { + const result = await processFixture('partials/_partial2.mdx'); + expect(result).toMatchSnapshot(); + }); + + it('works with partial imported after its usage', async () => { + const result = await processFixture( + 'partials/partial-used-before-import.mdx', + ); + expect(result).toMatchSnapshot(); + }); }); diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/index.ts b/packages/docusaurus-mdx-loader/src/remark/toc/index.ts index 31081fb73b4f..e457e61bfb06 100644 --- a/packages/docusaurus-mdx-loader/src/remark/toc/index.ts +++ b/packages/docusaurus-mdx-loader/src/remark/toc/index.ts @@ -5,154 +5,183 @@ * LICENSE file in the root directory of this source tree. */ -import {parse, type ParserOptions} from '@babel/parser'; -import traverse from '@babel/traverse'; -import stringifyObject from 'stringify-object'; -import {toValue} from '../utils'; -import type {Identifier} from '@babel/types'; -import type {Node, Parent} from 'unist'; -import type {Heading, Literal} from 'mdast'; +import { + addTocSliceImportIfNeeded, + createTOCExportNodeAST, + findDefaultImportName, + getImportDeclarations, + isMarkdownImport, + isNamedExport, +} from './utils'; +import type {Heading, Root} from 'mdast'; // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 import type {Transformer} from 'unified'; import type { MdxjsEsm, + MdxJsxFlowElement, // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 } from 'mdast-util-mdx'; - -// TODO as of April 2023, no way to import/re-export this ESM type easily :/ -// TODO upgrade to TS 5.3 -// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391 -// import type {Plugin} from 'unified'; -type Plugin = any; // TODO fix this asap - -export type TOCItem = { - readonly value: string; - readonly id: string; - readonly level: number; -}; - -const parseOptions: ParserOptions = { - plugins: ['jsx'], - sourceType: 'module', -}; - -const isImport = (child: any): child is Literal => - child.type === 'mdxjsEsm' && child.value.startsWith('import'); -const hasImports = (index: number) => index > -1; -const isExport = (child: any): child is Literal => - child.type === 'mdxjsEsm' && child.value.startsWith('export'); +import type {TOCItems} from './types'; +import type {ImportDeclaration} from 'estree'; interface PluginOptions { name?: string; } -const isTarget = (child: Literal, name: string) => { - let found = false; - const ast = parse(child.value, parseOptions); - traverse(ast, { - VariableDeclarator: (path) => { - if ((path.node.id as Identifier).name === name) { - found = true; +// ComponentName (default export) => ImportDeclaration mapping +type MarkdownImports = Map; + +// MdxjsEsm node representing an already existing "export const toc" declaration +type ExistingTOCExport = MdxjsEsm | null; + +function createTocSliceImportName({ + tocExportName, + componentName, +}: { + tocExportName: string; + componentName: string; +}) { + // The name of the toc slice import alias doesn't matter much + // We just need to ensure it's valid and won't conflict with other names + return `__${tocExportName}${componentName}`; +} + +async function collectImportsExports({ + root, + tocExportName, +}: { + root: Root; + tocExportName: string; +}): Promise<{ + markdownImports: MarkdownImports; + existingTocExport: ExistingTOCExport; +}> { + const {visit} = await import('unist-util-visit'); + + const markdownImports = new Map(); + let existingTocExport: MdxjsEsm | null = null; + + visit(root, 'mdxjsEsm', (node) => { + if (!node.data?.estree) { + return; + } + if (isNamedExport(node, tocExportName)) { + existingTocExport = node; + } + + getImportDeclarations(node.data.estree).forEach((declaration) => { + if (!isMarkdownImport(declaration)) { + return; + } + const componentName = findDefaultImportName(declaration); + if (!componentName) { + return; } - }, + markdownImports.set(componentName, { + declaration, + }); + }); }); - return found; -}; - -const getOrCreateExistingTargetIndex = async ( - children: Node[], - name: string, -) => { - let importsIndex = -1; - let targetIndex = -1; - - children.forEach((child, index) => { - if (isImport(child)) { - importsIndex = index; - } else if (isExport(child) && isTarget(child, name)) { - targetIndex = index; + + return {markdownImports, existingTocExport}; +} + +async function collectTOCItems({ + root, + tocExportName, + markdownImports, +}: { + root: Root; + tocExportName: string; + markdownImports: MarkdownImports; +}): Promise<{ + // The toc items we collected in the tree + tocItems: TOCItems; +}> { + const {toString} = await import('mdast-util-to-string'); + const {visit} = await import('unist-util-visit'); + + const tocItems: TOCItems = []; + + visit(root, (child) => { + if (child.type === 'heading') { + visitHeading(child); + } else if (child.type === 'mdxJsxFlowElement') { + visitJSXElement(child); } }); - if (targetIndex === -1) { - const target = await createExportNode(name, []); + return {tocItems}; - targetIndex = hasImports(importsIndex) ? importsIndex + 1 : 0; - children.splice(targetIndex, 0, target); + // Visit Markdown headings + function visitHeading(node: Heading) { + const value = toString(node); + // depth:1 headings are titles and not included in the TOC + if (!value || node.depth < 2) { + return; + } + tocItems.push({ + type: 'heading', + heading: node, + }); } - return targetIndex; -}; - -const plugin: Plugin = function plugin( - options: PluginOptions = {}, -): Transformer { - const name = options.name || 'toc'; + // Visit JSX elements, such as + function visitJSXElement(node: MdxJsxFlowElement) { + const componentName = node.name; + if (!componentName) { + return; + } + const importDeclaration = markdownImports.get(componentName)?.declaration; + if (!importDeclaration) { + return; + } - return async (root) => { - const {toString} = await import('mdast-util-to-string'); - const {visit} = await import('unist-util-visit'); + const tocSliceImportName = createTocSliceImportName({ + tocExportName, + componentName, + }); - const headings: TOCItem[] = []; + tocItems.push({ + type: 'slice', + importName: tocSliceImportName, + }); - visit(root, 'heading', (child: Heading) => { - const value = toString(child); + addTocSliceImportIfNeeded({ + importDeclaration, + tocExportName, + tocSliceImportName, + }); + } +} - // depth:1 headings are titles and not included in the TOC - if (!value || child.depth < 2) { - return; - } +export default function plugin(options: PluginOptions = {}): Transformer { + const tocExportName = options.name || 'toc'; - headings.push({ - value: toValue(child, toString), - id: child.data!.id!, - level: child.depth, - }); + return async (root) => { + const {markdownImports, existingTocExport} = await collectImportsExports({ + root, + tocExportName, }); - const {children} = root as Parent; - const targetIndex = await getOrCreateExistingTargetIndex(children, name); - - if (headings?.length) { - children[targetIndex] = await createExportNode(name, headings); + // If user explicitly writes "export const toc" in his mdx file + // We keep it as is do not override their explicit toc structure + // See https://github.com/facebook/docusaurus/pull/7530#discussion_r1458087876 + if (existingTocExport) { + return; } - }; -}; - -export default plugin; - -async function createExportNode(name: string, object: any): Promise { - const {valueToEstree} = await import('estree-util-value-to-estree'); - - return { - type: 'mdxjsEsm', - value: `export const ${name} = ${stringifyObject(object)}`, - data: { - estree: { - type: 'Program', - body: [ - { - type: 'ExportNamedDeclaration', - declaration: { - type: 'VariableDeclaration', - declarations: [ - { - type: 'VariableDeclarator', - id: { - type: 'Identifier', - name, - }, - init: valueToEstree(object), - }, - ], - kind: 'const', - }, - specifiers: [], - source: null, - }, - ], - sourceType: 'module', - }, - }, + + const {tocItems} = await collectTOCItems({ + root, + tocExportName, + markdownImports, + }); + + root.children.push( + await createTOCExportNodeAST({ + tocExportName, + tocItems, + }), + ); }; } diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/types.ts b/packages/docusaurus-mdx-loader/src/remark/toc/types.ts new file mode 100644 index 000000000000..3170dabbba2d --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/types.ts @@ -0,0 +1,29 @@ +/** + * 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 type {Heading} from 'mdast'; + +// Note: this type is exported from mdx-loader and used in theme +// Need to keep it retro compatible +export type TOCItem = { + readonly value: string; + readonly id: string; + readonly level: number; +}; + +export type TOCHeading = { + readonly type: 'heading'; + readonly heading: Heading; +}; + +// A TOC slice represents a TOCItem[] imported from a partial +export type TOCSlice = { + readonly type: 'slice'; + readonly importName: string; +}; + +export type TOCItems = (TOCHeading | TOCSlice)[]; diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/utils.ts b/packages/docusaurus-mdx-loader/src/remark/toc/utils.ts new file mode 100644 index 000000000000..3cedf78ed095 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/toc/utils.ts @@ -0,0 +1,177 @@ +/** + * 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 {toValue} from '../utils'; +import type {Node} from 'unist'; +import type { + MdxjsEsm, + // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 +} from 'mdast-util-mdx'; +import type {TOCHeading, TOCItem, TOCItems, TOCSlice} from './types'; +import type { + Program, + SpreadElement, + ImportDeclaration, + ImportSpecifier, +} from 'estree'; + +export function getImportDeclarations(program: Program): ImportDeclaration[] { + return program.body.filter( + (item): item is ImportDeclaration => item.type === 'ImportDeclaration', + ); +} + +export function isMarkdownImport(node: Node): node is ImportDeclaration { + if (node.type !== 'ImportDeclaration') { + return false; + } + const importPath = (node as ImportDeclaration).source.value; + return typeof importPath === 'string' && /\.mdx?$/.test(importPath); +} + +export function findDefaultImportName( + importDeclaration: ImportDeclaration, +): string | undefined { + return importDeclaration.specifiers.find( + (o: Node) => o.type === 'ImportDefaultSpecifier', + )?.local.name; +} + +export function findNamedImportSpecifier( + importDeclaration: ImportDeclaration, + localName: string, +): ImportSpecifier | undefined { + return importDeclaration?.specifiers.find( + (specifier): specifier is ImportSpecifier => + specifier.type === 'ImportSpecifier' && + specifier.local.name === localName, + ); +} + +// Before: import Partial from "partial" +// After: import Partial, {toc as __tocPartial} from "partial" +export function addTocSliceImportIfNeeded({ + importDeclaration, + tocExportName, + tocSliceImportName, +}: { + importDeclaration: ImportDeclaration; + tocExportName: string; + tocSliceImportName: string; +}): void { + // We only add the toc slice named import if it doesn't exist already + if (!findNamedImportSpecifier(importDeclaration, tocSliceImportName)) { + importDeclaration.specifiers.push({ + type: 'ImportSpecifier', + imported: {type: 'Identifier', name: tocExportName}, + local: {type: 'Identifier', name: tocSliceImportName}, + }); + } +} + +export function isNamedExport( + node: Node, + exportName: string, +): node is MdxjsEsm { + if (node.type !== 'mdxjsEsm') { + return false; + } + const program = (node as MdxjsEsm).data?.estree; + if (!program) { + return false; + } + if (program.body.length !== 1) { + return false; + } + const exportDeclaration = program.body[0]!; + if (exportDeclaration.type !== 'ExportNamedDeclaration') { + return false; + } + const variableDeclaration = exportDeclaration.declaration; + if (variableDeclaration?.type !== 'VariableDeclaration') { + return false; + } + const {id} = variableDeclaration.declarations[0]!; + if (id.type !== 'Identifier') { + return false; + } + + return id.name === exportName; +} + +export async function createTOCExportNodeAST({ + tocExportName, + tocItems, +}: { + tocExportName: string; + tocItems: TOCItems; +}): Promise { + function createTOCSliceAST(tocSlice: TOCSlice): SpreadElement { + return { + type: 'SpreadElement', + argument: {type: 'Identifier', name: tocSlice.importName}, + }; + } + + async function createTOCHeadingAST({heading}: TOCHeading) { + const {toString} = await import('mdast-util-to-string'); + const {valueToEstree} = await import('estree-util-value-to-estree'); + const value: TOCItem = { + value: toValue(heading, toString), + id: heading.data!.id!, + level: heading.depth, + }; + return valueToEstree(value); + } + + async function createTOCItemAST(tocItem: TOCItems[number]) { + switch (tocItem.type) { + case 'slice': + return createTOCSliceAST(tocItem); + case 'heading': + return createTOCHeadingAST(tocItem); + default: { + throw new Error(`unexpected toc item type`); + } + } + } + + return { + type: 'mdxjsEsm', + value: '', // See https://github.com/facebook/docusaurus/pull/9684#discussion_r1457595181 + data: { + estree: { + type: 'Program', + body: [ + { + type: 'ExportNamedDeclaration', + declaration: { + type: 'VariableDeclaration', + declarations: [ + { + type: 'VariableDeclarator', + id: { + type: 'Identifier', + name: tocExportName, + }, + init: { + type: 'ArrayExpression', + elements: await Promise.all(tocItems.map(createTOCItemAST)), + }, + }, + ], + kind: 'const', + }, + specifiers: [], + source: null, + }, + ], + sourceType: 'module', + }, + }, + }; +} diff --git a/website/_dogfooding/_blog tests/2021-08-21-blog-post-toc-tests.mdx b/website/_dogfooding/_blog tests/2021-08-21-blog-post-toc-tests.mdx index fe66256a7427..0d5ae4defdfe 100644 --- a/website/_dogfooding/_blog tests/2021-08-21-blog-post-toc-tests.mdx +++ b/website/_dogfooding/_blog tests/2021-08-21-blog-post-toc-tests.mdx @@ -9,10 +9,6 @@ tags: [paginated-tag] {/* truncate */} -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/tests/toc-partials/_first-level-partial.mdx b/website/_dogfooding/_docs tests/tests/toc-partials/_first-level-partial.mdx new file mode 100644 index 000000000000..2cd6eba6c057 --- /dev/null +++ b/website/_dogfooding/_docs tests/tests/toc-partials/_first-level-partial.mdx @@ -0,0 +1,7 @@ +import SecondLevelPartial from './_second-level-partial.mdx'; + +## 1st level partial + +I'm 1 level deep. + + diff --git a/website/_dogfooding/_docs tests/tests/toc-partials/_partial.mdx b/website/_dogfooding/_docs tests/tests/toc-partials/_partial.mdx new file mode 100644 index 000000000000..168e302bf488 --- /dev/null +++ b/website/_dogfooding/_docs tests/tests/toc-partials/_partial.mdx @@ -0,0 +1,19 @@ +## Partial + +Partial intro + +### Partial Sub Heading 1 + +Partial Sub Heading 1 content + +#### Partial Sub Sub Heading 1 + +Partial Sub Sub Heading 1 content + +### Partial Sub Heading 2 + +Partial Sub Heading 2 content + +#### Partial Sub Sub Heading 2 + +Partial Sub Sub Heading 2 content diff --git a/website/_dogfooding/_docs tests/tests/toc-partials/_second-level-partial.mdx b/website/_dogfooding/_docs tests/tests/toc-partials/_second-level-partial.mdx new file mode 100644 index 000000000000..279cc7d74365 --- /dev/null +++ b/website/_dogfooding/_docs tests/tests/toc-partials/_second-level-partial.mdx @@ -0,0 +1,3 @@ +### 2nd level partial + +I'm 2 levels deep. diff --git a/website/_dogfooding/_docs tests/tests/toc-partials/index.mdx b/website/_dogfooding/_docs tests/tests/toc-partials/index.mdx new file mode 100644 index 000000000000..8e608d78ed16 --- /dev/null +++ b/website/_dogfooding/_docs tests/tests/toc-partials/index.mdx @@ -0,0 +1,46 @@ +import Partial from './_partial.mdx'; + +# TOC partial test + +This page tests that MDX-imported content appears correctly in the table-of-contents + +See also: + +- https://github.com/facebook/docusaurus/issues/3915 +- https://github.com/facebook/docusaurus/pull/9684 + +--- + +**The table of contents should include headings of this partial**: + + + +--- + +**We can import the same partial using a different name and it still works**: + +import WeirdLocalName from './_partial.mdx'; + + + +--- + +**We can import a partial and not use it, the TOC remains unaffected**: + +import UnusedPartial from './_partial.mdx'; + +--- + +import FirstLevelPartial from './_first-level-partial.mdx'; + +**It also works for partials importing other partials** + + + +--- + +**And we can even use the same partial twice!** + +**(although it's useless and not particularly recommended because headings will have the same ids)** + + diff --git a/website/_dogfooding/_docs tests/toc/toc-2-2.mdx b/website/_dogfooding/_docs tests/toc/toc-2-2.mdx index 09a3b607bfaa..0a96916359fb 100644 --- a/website/_dogfooding/_docs tests/toc/toc-2-2.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-2-2.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 2 toc_max_heading_level: 2 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-2-3.mdx b/website/_dogfooding/_docs tests/toc/toc-2-3.mdx index 3a7e3597595d..0839b1081b11 100644 --- a/website/_dogfooding/_docs tests/toc/toc-2-3.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-2-3.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 2 toc_max_heading_level: 3 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-2-4.mdx b/website/_dogfooding/_docs tests/toc/toc-2-4.mdx index 4aa86132bc31..2fc1e4ad3904 100644 --- a/website/_dogfooding/_docs tests/toc/toc-2-4.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-2-4.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 2 toc_max_heading_level: 4 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-2-5.mdx b/website/_dogfooding/_docs tests/toc/toc-2-5.mdx index ccba840f4d1e..e8c995d1ffd1 100644 --- a/website/_dogfooding/_docs tests/toc/toc-2-5.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-2-5.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 2 toc_max_heading_level: 5 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-3-5.mdx b/website/_dogfooding/_docs tests/toc/toc-3-5.mdx index b88fb957226f..94eec4cfd589 100644 --- a/website/_dogfooding/_docs tests/toc/toc-3-5.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-3-5.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 3 toc_max_heading_level: 5 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-3-_.mdx b/website/_dogfooding/_docs tests/toc/toc-3-_.mdx index 11b0acb4e4b6..62e8aacdd473 100644 --- a/website/_dogfooding/_docs tests/toc/toc-3-_.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-3-_.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 3 # toc_max_heading_level: --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-4-5.mdx b/website/_dogfooding/_docs tests/toc/toc-4-5.mdx index 446a08c83759..2a279c48e537 100644 --- a/website/_dogfooding/_docs tests/toc/toc-4-5.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-4-5.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 4 toc_max_heading_level: 5 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-5-5.mdx b/website/_dogfooding/_docs tests/toc/toc-5-5.mdx index 274a056d7d3e..22f0d122bcf0 100644 --- a/website/_dogfooding/_docs tests/toc/toc-5-5.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-5-5.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 5 toc_max_heading_level: 5 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-_-5.mdx b/website/_dogfooding/_docs tests/toc/toc-_-5.mdx index 611dea4f163b..eb2eb05e78eb 100644 --- a/website/_dogfooding/_docs tests/toc/toc-_-5.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-_-5.mdx @@ -3,10 +3,6 @@ toc_max_heading_level: 5 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_docs tests/toc/toc-_-_.mdx b/website/_dogfooding/_docs tests/toc/toc-_-_.mdx index 9fa20b9e2822..0418b85b5864 100644 --- a/website/_dogfooding/_docs tests/toc/toc-_-_.mdx +++ b/website/_dogfooding/_docs tests/toc/toc-_-_.mdx @@ -3,10 +3,6 @@ # toc_max_heading_level: --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/_dogfooding/_pages tests/page-toc-tests.mdx b/website/_dogfooding/_pages tests/page-toc-tests.mdx index 4aa86132bc31..2fc1e4ad3904 100644 --- a/website/_dogfooding/_pages tests/page-toc-tests.mdx +++ b/website/_dogfooding/_pages tests/page-toc-tests.mdx @@ -3,10 +3,6 @@ toc_min_heading_level: 2 toc_max_heading_level: 4 --- -import Content, { - toc as ContentToc, -} from '@site/_dogfooding/_partials/toc-tests.mdx'; +import Content from '@site/_dogfooding/_partials/toc-tests.mdx'; - -export const toc = ContentToc; diff --git a/website/community/3-contributing.mdx b/website/community/3-contributing.mdx index 985859d34cbd..5ea9c6660352 100644 --- a/website/community/3-contributing.mdx +++ b/website/community/3-contributing.mdx @@ -5,9 +5,7 @@ sidebar_label: Contributing --- ```mdx-code-block -import Contributing, {toc as ContributingTOC} from "@site/../CONTRIBUTING.md" +import Contributing from "@site/../CONTRIBUTING.md" - -export const toc = ContributingTOC; ``` diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index afc8fb52b143..c0f83188ed27 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -190,16 +190,21 @@ export default async function createConfigAsync() { preprocessor: ({filePath, fileContent}) => { let result = fileContent; + // This fixes Crowdin bug altering MDX comments on i18n sites... + // https://github.com/facebook/docusaurus/pull/9220 result = result.replaceAll('{/_', '{/*'); result = result.replaceAll('_/}', '*/}'); if (isDev) { - // "vscode://file/${projectPath}${filePath}:${line}:${column}", - // "webstorm://open?file=${projectPath}${filePath}&line=${line}&column=${column}", - const vscodeLink = `vscode://file/${filePath}`; - const webstormLink = `webstorm://open?file=${filePath}`; - const intellijLink = `idea://open?file=${filePath}`; - result = `${result}\n\n---\n\n**DEV**: open this file in [VSCode](<${vscodeLink}>) | [WebStorm](<${webstormLink}>) | [IntelliJ](<${intellijLink}>)\n`; + const isPartial = path.basename(filePath).startsWith('_'); + if (!isPartial) { + // "vscode://file/${projectPath}${filePath}:${line}:${column}", + // "webstorm://open?file=${projectPath}${filePath}&line=${line}&column=${column}", + const vscodeLink = `vscode://file/${filePath}`; + const webstormLink = `webstorm://open?file=${filePath}`; + const intellijLink = `idea://open?file=${filePath}`; + result = `${result}\n\n---\n\n**DEV**: open this file in [VSCode](<${vscodeLink}>) | [WebStorm](<${webstormLink}>) | [IntelliJ](<${intellijLink}>)\n`; + } } return result; diff --git a/yarn.lock b/yarn.lock index 293dbc4f4099..b29ef74e7aa8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -434,7 +434,7 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.22.7", "@babel/parser@^7.23.3": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==