mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-10 23:02:56 +02:00
refactor(mdx-loader): use vfile.path to access Markdown file path (#6443)
This commit is contained in:
parent
e40cafccd5
commit
3d7ba337c2
4 changed files with 48 additions and 22 deletions
|
@ -128,7 +128,6 @@ export default async function mdxLoader(
|
||||||
transformImage,
|
transformImage,
|
||||||
{
|
{
|
||||||
staticDirs: reqOptions.staticDirs,
|
staticDirs: reqOptions.staticDirs,
|
||||||
filePath,
|
|
||||||
siteDir: reqOptions.siteDir,
|
siteDir: reqOptions.siteDir,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -136,7 +135,6 @@ export default async function mdxLoader(
|
||||||
transformLinks,
|
transformLinks,
|
||||||
{
|
{
|
||||||
staticDirs: reqOptions.staticDirs,
|
staticDirs: reqOptions.staticDirs,
|
||||||
filePath,
|
|
||||||
siteDir: reqOptions.siteDir,
|
siteDir: reqOptions.siteDir,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -27,11 +27,14 @@ const {
|
||||||
loaders: {inlineMarkdownImageFileLoader},
|
loaders: {inlineMarkdownImageFileLoader},
|
||||||
} = getFileLoaderUtils();
|
} = getFileLoaderUtils();
|
||||||
|
|
||||||
interface PluginOptions {
|
type PluginOptions = {
|
||||||
filePath: string;
|
|
||||||
staticDirs: string[];
|
staticDirs: string[];
|
||||||
siteDir: string;
|
siteDir: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
type Context = PluginOptions & {
|
||||||
|
filePath: string;
|
||||||
|
};
|
||||||
|
|
||||||
async function toImageRequireNode(
|
async function toImageRequireNode(
|
||||||
node: Image,
|
node: Image,
|
||||||
|
@ -89,7 +92,7 @@ async function ensureImageFileExist(imagePath: string, sourceFilePath: string) {
|
||||||
|
|
||||||
async function getImageAbsolutePath(
|
async function getImageAbsolutePath(
|
||||||
imagePath: string,
|
imagePath: string,
|
||||||
{siteDir, filePath, staticDirs}: PluginOptions,
|
{siteDir, filePath, staticDirs}: Context,
|
||||||
) {
|
) {
|
||||||
if (imagePath.startsWith('@site/')) {
|
if (imagePath.startsWith('@site/')) {
|
||||||
const imageFilePath = path.join(siteDir, imagePath.replace('@site/', ''));
|
const imageFilePath = path.join(siteDir, imagePath.replace('@site/', ''));
|
||||||
|
@ -123,11 +126,11 @@ async function getImageAbsolutePath(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processImageNode(node: Image, options: PluginOptions) {
|
async function processImageNode(node: Image, context: Context) {
|
||||||
if (!node.url) {
|
if (!node.url) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Markdown image URL is mandatory in "${toMessageRelativeFilePath(
|
`Markdown image URL is mandatory in "${toMessageRelativeFilePath(
|
||||||
options.filePath,
|
context.filePath,
|
||||||
)}" file`,
|
)}" file`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -144,15 +147,17 @@ async function processImageNode(node: Image, options: PluginOptions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imagePath = await getImageAbsolutePath(parsedUrl.pathname, options);
|
const imagePath = await getImageAbsolutePath(parsedUrl.pathname, context);
|
||||||
await toImageRequireNode(node, imagePath, options.filePath);
|
await toImageRequireNode(node, imagePath, context.filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugin: Plugin<[PluginOptions]> = (options) => {
|
const plugin: Plugin<[PluginOptions]> = (options) => {
|
||||||
const transformer: Transformer = async (root) => {
|
const transformer: Transformer = async (root, vfile) => {
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
visit(root, 'image', (node: Image) => {
|
visit(root, 'image', (node: Image) => {
|
||||||
promises.push(processImageNode(node, options));
|
promises.push(
|
||||||
|
processImageNode(node, {...options, filePath: vfile.path!}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,11 +25,14 @@ const {
|
||||||
loaders: {inlineMarkdownLinkFileLoader},
|
loaders: {inlineMarkdownLinkFileLoader},
|
||||||
} = getFileLoaderUtils();
|
} = getFileLoaderUtils();
|
||||||
|
|
||||||
interface PluginOptions {
|
type PluginOptions = {
|
||||||
filePath: string;
|
|
||||||
staticDirs: string[];
|
staticDirs: string[];
|
||||||
siteDir: string;
|
siteDir: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
type Context = PluginOptions & {
|
||||||
|
filePath: string;
|
||||||
|
};
|
||||||
|
|
||||||
// transform the link node to a jsx link with a require() call
|
// transform the link node to a jsx link with a require() call
|
||||||
function toAssetRequireNode(node: Link, assetPath: string, filePath: string) {
|
function toAssetRequireNode(node: Link, assetPath: string, filePath: string) {
|
||||||
|
@ -71,7 +74,7 @@ async function ensureAssetFileExist(assetPath: string, sourceFilePath: string) {
|
||||||
|
|
||||||
async function getAssetAbsolutePath(
|
async function getAssetAbsolutePath(
|
||||||
assetPath: string,
|
assetPath: string,
|
||||||
{siteDir, filePath, staticDirs}: PluginOptions,
|
{siteDir, filePath, staticDirs}: Context,
|
||||||
) {
|
) {
|
||||||
if (assetPath.startsWith('@site/')) {
|
if (assetPath.startsWith('@site/')) {
|
||||||
const assetFilePath = path.join(siteDir, assetPath.replace('@site/', ''));
|
const assetFilePath = path.join(siteDir, assetPath.replace('@site/', ''));
|
||||||
|
@ -96,7 +99,7 @@ async function getAssetAbsolutePath(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processLinkNode(node: Link, options: PluginOptions) {
|
async function processLinkNode(node: Link, context: Context) {
|
||||||
if (!node.url) {
|
if (!node.url) {
|
||||||
// try to improve error feedback
|
// try to improve error feedback
|
||||||
// see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675
|
// see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675
|
||||||
|
@ -104,7 +107,7 @@ async function processLinkNode(node: Link, options: PluginOptions) {
|
||||||
const line = node?.position?.start?.line || '?';
|
const line = node?.position?.start?.line || '?';
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Markdown link URL is mandatory in "${toMessageRelativeFilePath(
|
`Markdown link URL is mandatory in "${toMessageRelativeFilePath(
|
||||||
options.filePath,
|
context.filePath,
|
||||||
)}" file (title: ${title}, line: ${line}).`,
|
)}" file (title: ${title}, line: ${line}).`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -122,17 +125,17 @@ async function processLinkNode(node: Link, options: PluginOptions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetPath = await getAssetAbsolutePath(parsedUrl.pathname, options);
|
const assetPath = await getAssetAbsolutePath(parsedUrl.pathname, context);
|
||||||
if (assetPath) {
|
if (assetPath) {
|
||||||
toAssetRequireNode(node, assetPath, options.filePath);
|
toAssetRequireNode(node, assetPath, context.filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugin: Plugin<[PluginOptions]> = (options) => {
|
const plugin: Plugin<[PluginOptions]> = (options) => {
|
||||||
const transformer: Transformer = async (root) => {
|
const transformer: Transformer = async (root, vfile) => {
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
visit(root, 'link', (node: Link) => {
|
visit(root, 'link', (node: Link) => {
|
||||||
promises.push(processLinkNode(node, options));
|
promises.push(processLinkNode(node, {...options, filePath: vfile.path!}));
|
||||||
});
|
});
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
|
@ -169,6 +169,26 @@ module.exports = {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
|
||||||
|
The `transformer` has a second parameter [`vfile`](https://github.com/vfile/vfile) which is useful if you need to access the current Markdown file's path.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const plugin = (options) => {
|
||||||
|
const transformer = async (ast, vfile) => {
|
||||||
|
ast.children.unshift({
|
||||||
|
type: 'text',
|
||||||
|
value: `The current file path is ${vfile.path}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return transformer;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Our `transformImage` plugin uses this parameter, for example, to transform relative image references to `require()` calls.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|
||||||
The default plugins of Docusaurus would operate before the custom remark plugins, and that means the images or links have been converted to JSX with `require()` calls already. For example, in the example above, the table of contents generated is still the same even when all `h2` headings are now prefixed by `Section X.`, because the TOC-generating plugin is called before our custom plugin. If you need to process the MDAST before the default plugins do, use the `beforeDefaultRemarkPlugins` and `beforeDefaultRehypePlugins`.
|
The default plugins of Docusaurus would operate before the custom remark plugins, and that means the images or links have been converted to JSX with `require()` calls already. For example, in the example above, the table of contents generated is still the same even when all `h2` headings are now prefixed by `Section X.`, because the TOC-generating plugin is called before our custom plugin. If you need to process the MDAST before the default plugins do, use the `beforeDefaultRemarkPlugins` and `beforeDefaultRehypePlugins`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue