feat: mdx loader fallback, allow importing mdx docs from anywhere (#5299)

* move deep filepath test

* split markdownPageExample.md

* re-org dogfooding content

* Add mdx partials fallback synthetic plugin by default

* test commit

* hide changelog title as it's already included in the partial file

* trigger CI

* fix changelog sidebar label
This commit is contained in:
Sébastien Lorber 2021-08-06 11:32:22 +02:00 committed by GitHub
parent 780e924a9e
commit 916b82119b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 354 additions and 276 deletions

View File

@ -1309,7 +1309,7 @@ Starting with this release for a proper work of i18n functionality, you need to
#### :rocket: New Feature
- `docusaurus`
- [#3932](https://github.com/facebook/docusaurus/pull/3932) feat(v2): Add <Root> theme element ([@slorber](https://github.com/slorber))
- [#3932](https://github.com/facebook/docusaurus/pull/3932) feat(v2): Add Root theme element ([@slorber](https://github.com/slorber))
- `docusaurus-module-type-aliases`, `docusaurus-theme-classic`, `docusaurus-types`, `docusaurus`
- [#3916](https://github.com/facebook/docusaurus/pull/3916) feat(v2): Add localeDropdown navbar item type + i18n localeConfigs field ([@slorber](https://github.com/slorber))
- `docusaurus-mdx-loader`, `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-theme-bootstrap`, `docusaurus-theme-classic`, `docusaurus-types`

View File

@ -98,11 +98,16 @@ File at ${filePath} contains FrontMatter that will be ignored: \n${JSON.stringif
null,
2,
)}`;
const shouldError = process.env.NODE_ENV === 'test' || process.env.CI;
if (shouldError) {
return callback(new Error(errorMessage));
if (options.isMDXPartialFrontMatterWarningDisabled === true) {
// no warning
} else {
console.warn(chalk.yellow(errorMessage));
const shouldError = process.env.NODE_ENV === 'test' || process.env.CI;
if (shouldError) {
return callback(new Error(errorMessage));
} else {
console.warn(chalk.yellow(errorMessage));
}
}
}

View File

@ -13,6 +13,7 @@ import {
DEFAULT_BUILD_DIR_NAME,
DEFAULT_CONFIG_FILE_NAME,
GENERATED_FILES_DIR_NAME,
STATIC_DIR_NAME,
} from '../constants';
import loadClientModules from './client-modules';
import loadConfig from './config';
@ -24,6 +25,7 @@ import {
DocusaurusSiteMetadata,
HtmlTagObject,
LoadContext,
LoadedPlugin,
PluginConfig,
Props,
} from '@docusaurus/types';
@ -36,6 +38,7 @@ import {
getPluginsDefaultCodeTranslationMessages,
} from './translations/translations';
import {mapValues} from 'lodash';
import {RuleSetRule} from 'webpack';
export type LoadContextOptions = {
customOutDir?: string;
@ -135,6 +138,104 @@ export function loadPluginConfigs(context: LoadContext): PluginConfig[] {
];
}
// Make a fake plugin to:
// - Resolve aliased theme components
// - Inject scripts/stylesheets
function createBootstrapPlugin({
siteConfig,
}: {
siteConfig: DocusaurusConfig;
}): LoadedPlugin {
const {
stylesheets = [],
scripts = [],
clientModules: siteConfigClientModules = [],
} = siteConfig;
return {
name: 'docusaurus-bootstrap-plugin',
content: null,
options: {},
version: {type: 'synthetic'},
getClientModules() {
return siteConfigClientModules;
},
injectHtmlTags: () => {
const stylesheetsTags = stylesheets.map((source) =>
typeof source === 'string'
? `<link rel="stylesheet" href="${source}">`
: ({
tagName: 'link',
attributes: {
rel: 'stylesheet',
...source,
},
} as HtmlTagObject),
);
const scriptsTags = scripts.map((source) =>
typeof source === 'string'
? `<script src="${source}"></script>`
: ({
tagName: 'script',
attributes: {
...source,
},
} as HtmlTagObject),
);
return {
headTags: [...stylesheetsTags, ...scriptsTags],
};
},
};
}
// Configurer Webpack fallback mdx loader for md/mdx files out of content-plugin folders
// Adds a "fallback" mdx loader for mdx files that are not processed by content plugins
// This allows to do things such as importing repo/README.md as a partial from another doc
// Not ideal solution though, but good enough for now
function createMDXFallbackPlugin({siteDir}: {siteDir: string}): LoadedPlugin {
return {
name: 'docusaurus-mdx-fallback-plugin',
content: null,
options: {},
version: {type: 'synthetic'},
configureWebpack(config, isServer, {getJSLoader}) {
// We need the mdx fallback loader to exclude files that were already processed by content plugins mdx loaders
// This works, but a bit hacky...
// Not sure there's a way to handle that differently in webpack :s
function getMDXFallbackExcludedPaths(): string[] {
const rules: RuleSetRule[] = config?.module?.rules as RuleSetRule[];
return rules.flatMap((rule) => {
const isMDXRule =
rule.test instanceof RegExp && rule.test.test('x.mdx');
return isMDXRule ? (rule.include as string[]) : [];
});
}
return {
module: {
rules: [
{
test: /(\.mdx?)$/,
exclude: getMDXFallbackExcludedPaths(),
use: [
getJSLoader({isServer}),
{
loader: require.resolve('@docusaurus/mdx-loader'),
options: {
staticDir: path.join(siteDir, STATIC_DIR_NAME),
isMDXPartial: (_filename) => true, // External mdx files are always meant to be imported as partials
isMDXPartialFrontMatterWarningDisabled: true, // External mdx files might have frontmatter, let's just disable the warning
},
},
],
},
],
},
};
},
};
}
export async function load(
siteDir: string,
options: LoadContextOptions = {},
@ -176,49 +277,8 @@ export async function load(
`export default ${JSON.stringify(siteConfig, null, 2)};`,
);
// Make a fake plugin to:
// - Resolve aliased theme components
// - Inject scripts/stylesheets
const {
stylesheets = [],
scripts = [],
clientModules: siteConfigClientModules = [],
} = siteConfig;
plugins.push({
name: 'docusaurus-bootstrap-plugin',
content: null,
options: {},
version: {type: 'synthetic'},
getClientModules() {
return siteConfigClientModules;
},
injectHtmlTags: () => {
const stylesheetsTags = stylesheets.map((source) =>
typeof source === 'string'
? `<link rel="stylesheet" href="${source}">`
: ({
tagName: 'link',
attributes: {
rel: 'stylesheet',
...source,
},
} as HtmlTagObject),
);
const scriptsTags = scripts.map((source) =>
typeof source === 'string'
? `<script src="${source}"></script>`
: ({
tagName: 'script',
attributes: {
...source,
},
} as HtmlTagObject),
);
return {
headTags: [...stylesheetsTags, ...scriptsTags],
};
},
});
plugins.push(createBootstrapPlugin({siteConfig}));
plugins.push(createMDXFallbackPlugin({siteDir}));
// Load client modules.
const clientModules = loadClientModules(plugins);

View File

@ -1,3 +1,7 @@
---
test: 'some test frontmatter'
---
# Docusaurus website dogfooding
This is where we test edge cases of Docusaurus by using fancy configs, ensuring they all don't fail during a real site build.
@ -12,3 +16,4 @@ Fancy things we can test for here:
- \_ prefix convention
- Huge sidebars impact
- Using folders with spaces on purpose
- Importing md docs that are out of content plugin folders as partials (such as this README file!)

View File

@ -1,9 +1,19 @@
## Page
Let's import some MDX partial:
Let's import a MDX partial at `./_pagePartial.md`:
```mdx-code-block
import PagePartial from "./_pagePartial.md"
<PagePartial />
```
---
Now let's import `../README.md`:
```mdx-code-block
import Readme from "../README.md"
<Readme />
```

View File

@ -0,0 +1,210 @@
---
title: Markdown Page tests title
description: Markdown Page tests description
wrapperClassName: docusaurus-markdown-example
---
# Markdown page tests
This is a page generated from markdown to illustrate the Markdown page feature and test some edge cases.
:::info
Useful information.
:::
```jsx live
function Button() {
return (
<button type="button" onClick={() => alert('hey')}>
Click me!
</button>
);
}
```
### Using absolute path
![](/img/docusaurus.png)
### Tab
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs defaultValue="apple" values={[ {label: 'Apple', value: 'apple'}, {label: 'Orange', value: 'orange'}, {label: 'Banana', value: 'banana'} ]}><TabItem value="apple">This is an apple 🍎</TabItem><TabItem value="orange">This is an orange 🍊</TabItem><TabItem value="banana">This is a banana 🍌</TabItem></Tabs>
## Comments
MDX comments can be used with
```mdx
<!--
My comment
-->
```
See, nothing is displayed:
<!--
My comment
-->
## Import code block from source code file
import MyComponent from "@site/src/pages/examples/\_myComponent"
import BrowserWindow from '@site/src/components/BrowserWindow';
Let's say you have a React component.
You can import and use it in MDX:
```jsx title="myMarkdownFile.mdx"
import MyComponent from './myComponent';
<MyComponent />;
```
<BrowserWindow url="http://localhost:3000">
<MyComponent/>
</BrowserWindow>
But you can also display its source code directly in MDX, thanks to [Webpack raw-loader](https://webpack.js.org/loaders/raw-loader/)
```jsx title="myMarkdownFile.mdx"
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';
<CodeBlock className="language-jsx">{MyComponentSource}</CodeBlock>;
```
import CodeBlock from "@theme/CodeBlock"
import MyComponentSource from '!!raw-loader!@site/src/pages/examples/\_myComponent';
<BrowserWindow url="http://localhost:3000">
<CodeBlock className="language-jsx">{MyComponentSource}</CodeBlock>
</BrowserWindow>
## Test
```jsx live
function Demo() {
React.useEffect(() => console.log('mount'), []);
return null;
}
```
## Code block test
```js title="Title"
function Clock(props) {
const [date, setDate] = useState(new Date());
useEffect(() => {
var timerID = setInterval(() => tick(), 1000);
return function cleanup() {
clearInterval(timerID);
};
});
function tick() {
setDate(new Date());
}
return (
<div>
<h2>It is {date.toLocaleTimeString()}.</h2>
// highlight-start
{/* prettier-ignore */}
long long long long long long long long long long long long line
{/* prettier-ignore */}
// highlight-end
</div>
);
}
```
```jsx live
function Clock(props) {
const [date, setDate] = useState(new Date());
useEffect(() => {
var timerID = setInterval(() => tick(), 1000);
return function cleanup() {
clearInterval(timerID);
};
});
function tick() {
setDate(new Date());
}
return (
<div>
<h2>It is {date.toLocaleTimeString()}.</h2>
</div>
);
}
```
<CodeBlock className="language-yaml" title="test">
test
</CodeBlock>
<code>test</code>
## direct using of `pre`
<pre>test</pre>
<!-- Multi-line text inside `pre` will turn into one-liner, but it's okay (https://github.com/mdx-js/mdx/issues/1095) -->
<pre>
1
2
3
</pre>
## Custom heading id {#custom}
## Children elements inside pre/code elements
See https://github.com/facebook/docusaurus/pull/1584
<pre><code>
<BrowserWindow url="http://localhost:3000" >
Lol bro
</BrowserWindow>
</code></pre>
<code>
<BrowserWindow url="http://localhost:3000" >
Lol bro
</BrowserWindow>
</code>
## Pipe
Code tag + double pipe: <code>&#124;&#124;</code>
Code tag + double pipe: <code>||</code>
## Images edge cases
![](/dogfooding/新控制器空间/图片.png)
![](/dogfooding/4/图片.png)
![](/dogfooding/4/docu.png)

View File

@ -0,0 +1,11 @@
---
title: Docusaurus 2 Changelog
hide_title: true
sidebar_label: Changelog
---
```mdx-code-block
import Changelog from "../../CHANGELOG.md"
<Changelog />
```

View File

@ -1,3 +0,0 @@
# Chapter 1
Lorem ipsum chapter 1

View File

@ -1,3 +0,0 @@
# Chapter 2
Lorem ipsum chapter 2

View File

@ -1,6 +1,6 @@
---
title: Markdown Page example title
description: Markdown Page example description
title: Markdown Page example
description: Markdown Page example
wrapperClassName: docusaurus-markdown-example
---
@ -8,225 +8,8 @@ wrapperClassName: docusaurus-markdown-example
This is a page generated from markdown to illustrate the Markdown page feature.
It supports all the regular MDX features, as you can see:
:::tip
:::info
Useful information.
Use Markdown pages when you just want to focus on content and the default layout is good enough
:::
```jsx live
function Button() {
return (
<button type="button" onClick={() => alert('hey')}>
Click me!
</button>
);
}
```
### Using absolute path
![](/img/docusaurus.png)
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs defaultValue="apple" values={[ {label: 'Apple', value: 'apple'}, {label: 'Orange', value: 'orange'}, {label: 'Banana', value: 'banana'} ]}><TabItem value="apple">This is an apple 🍎</TabItem><TabItem value="orange">This is an orange 🍊</TabItem><TabItem value="banana">This is a banana 🍌</TabItem></Tabs>
## Import Mdx and Md files
```js
// *.md file
import Chapter1 from './_chapter1.md';
<Chapter1 />;
// *.mdx file
import Chapter2 from './_chapter2.mdx';
<Chapter2 />;
```
import Chapter1 from './\_chapter1.md';
<Chapter1/>
import Chapter2 from './\_chapter2.mdx';
<Chapter2/>
## Comments
MDX comments can be used with
```mdx
<!--
My comment
-->
```
See, nothing is displayed:
<!--
My comment
-->
## Import code block from source code file
import MyComponent from "@site/src/pages/examples/\_myComponent"
import BrowserWindow from '@site/src/components/BrowserWindow';
Let's say you have a React component.
You can import and use it in MDX:
```jsx title="myMarkdownFile.mdx"
import MyComponent from './myComponent';
<MyComponent />;
```
<BrowserWindow url="http://localhost:3000">
<MyComponent/>
</BrowserWindow>
But you can also display its source code directly in MDX, thanks to [Webpack raw-loader](https://webpack.js.org/loaders/raw-loader/)
```jsx title="myMarkdownFile.mdx"
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';
<CodeBlock className="language-jsx">{MyComponentSource}</CodeBlock>;
```
import CodeBlock from "@theme/CodeBlock"
import MyComponentSource from '!!raw-loader!@site/src/pages/examples/\_myComponent';
<BrowserWindow url="http://localhost:3000">
<CodeBlock className="language-jsx">{MyComponentSource}</CodeBlock>
</BrowserWindow>
## Test
```jsx live
function Demo() {
React.useEffect(() => console.log('mount'), []);
return null;
}
```
## Code block test
```js title="Title"
function Clock(props) {
const [date, setDate] = useState(new Date());
useEffect(() => {
var timerID = setInterval(() => tick(), 1000);
return function cleanup() {
clearInterval(timerID);
};
});
function tick() {
setDate(new Date());
}
return (
<div>
<h2>It is {date.toLocaleTimeString()}.</h2>
// highlight-start
{/* prettier-ignore */}
long long long long long long long long long long long long line
{/* prettier-ignore */}
// highlight-end
</div>
);
}
```
```jsx live
function Clock(props) {
const [date, setDate] = useState(new Date());
useEffect(() => {
var timerID = setInterval(() => tick(), 1000);
return function cleanup() {
clearInterval(timerID);
};
});
function tick() {
setDate(new Date());
}
return (
<div>
<h2>It is {date.toLocaleTimeString()}.</h2>
</div>
);
}
```
<CodeBlock className="language-yaml" title="test">
test
</CodeBlock>
<code>test</code>
## direct using of `pre`
<pre>test</pre>
<!-- Multi-line text inside `pre` will turn into one-liner, but it's okay (https://github.com/mdx-js/mdx/issues/1095) -->
<pre>
1
2
3
</pre>
## Custom heading id {#custom}
## Children elements inside pre/code elements
See https://github.com/facebook/docusaurus/pull/1584
<pre><code>
<BrowserWindow url="http://localhost:3000" >
Lol bro
</BrowserWindow>
</code></pre>
<code>
<BrowserWindow url="http://localhost:3000" >
Lol bro
</BrowserWindow>
</code>
## Pipe
Code tag + double pipe: <code>&#124;&#124;</code>
Code tag + double pipe: <code>||</code>
## Images edge cases
![](/dogfooding/新控制器空间/图片.png)
![](/dogfooding/4/图片.png)
![](/dogfooding/4/docu.png)