mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-23 14:06:59 +02:00
refactor(mdx-loader): re-export metadata module instead of serializing it (#10470)
This commit is contained in:
parent
3d69ff3d47
commit
a47e8dda2d
6 changed files with 33 additions and 145 deletions
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import logger from '@docusaurus/logger';
|
import logger from '@docusaurus/logger';
|
||||||
import {
|
import {
|
||||||
|
aliasedSitePath,
|
||||||
DEFAULT_PARSE_FRONT_MATTER,
|
DEFAULT_PARSE_FRONT_MATTER,
|
||||||
getFileLoaderUtils,
|
getFileLoaderUtils,
|
||||||
getWebpackLoaderCompilerName,
|
getWebpackLoaderCompilerName,
|
||||||
|
@ -16,7 +17,6 @@ import {
|
||||||
compileToJSX,
|
compileToJSX,
|
||||||
createAssetsExportCode,
|
createAssetsExportCode,
|
||||||
extractContentTitleData,
|
extractContentTitleData,
|
||||||
readMetadataPath,
|
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import type {
|
import type {
|
||||||
SimpleProcessors,
|
SimpleProcessors,
|
||||||
|
@ -35,12 +35,6 @@ type Pluggable = any; // TODO fix this asap
|
||||||
|
|
||||||
export type MDXPlugin = Pluggable;
|
export type MDXPlugin = Pluggable;
|
||||||
|
|
||||||
// This represents the path to the mdx metadata bundle path + its loaded content
|
|
||||||
export type LoadedMetadata = {
|
|
||||||
metadataPath: string;
|
|
||||||
metadataContent: unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Options = Partial<MDXOptions> & {
|
export type Options = Partial<MDXOptions> & {
|
||||||
markdownConfig: MarkdownConfig;
|
markdownConfig: MarkdownConfig;
|
||||||
staticDirs: string[];
|
staticDirs: string[];
|
||||||
|
@ -48,13 +42,10 @@ export type Options = Partial<MDXOptions> & {
|
||||||
isMDXPartial?: (filePath: string) => boolean;
|
isMDXPartial?: (filePath: string) => boolean;
|
||||||
isMDXPartialFrontMatterWarningDisabled?: boolean;
|
isMDXPartialFrontMatterWarningDisabled?: boolean;
|
||||||
removeContentTitle?: boolean;
|
removeContentTitle?: boolean;
|
||||||
|
metadataPath?: (filePath: string) => string;
|
||||||
// TODO Docusaurus v4: rename to just "metadata"?
|
|
||||||
// We kept retro-compatibility in v3 in case plugins/sites use mdx loader
|
|
||||||
metadataPath?: string | ((filePath: string) => string | LoadedMetadata);
|
|
||||||
createAssets?: (metadata: {
|
createAssets?: (metadata: {
|
||||||
|
filePath: string;
|
||||||
frontMatter: {[key: string]: unknown};
|
frontMatter: {[key: string]: unknown};
|
||||||
metadata: unknown;
|
|
||||||
}) => {[key: string]: unknown};
|
}) => {[key: string]: unknown};
|
||||||
resolveMarkdownLink?: ResolveMarkdownLink;
|
resolveMarkdownLink?: ResolveMarkdownLink;
|
||||||
|
|
||||||
|
@ -112,40 +103,16 @@ ${JSON.stringify(frontMatter, null, 2)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMetadata(): Promise<LoadedMetadata | undefined> {
|
const metadataPath = (function getMetadataPath() {
|
||||||
if (!isMDXPartial) {
|
if (!isMDXPartial) {
|
||||||
// Read metadata for this MDX and export it.
|
return options.metadataPath?.(filePath);
|
||||||
if (options.metadataPath && typeof options.metadataPath === 'function') {
|
|
||||||
const metadata = options.metadataPath(filePath);
|
|
||||||
if (!metadata) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (typeof metadata === 'string') {
|
|
||||||
return {
|
|
||||||
metadataPath: metadata,
|
|
||||||
metadataContent: await readMetadataPath(metadata),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!metadata.metadataPath) {
|
|
||||||
throw new Error(`Metadata path missing for file ${filePath}`);
|
|
||||||
}
|
|
||||||
if (!metadata.metadataContent) {
|
|
||||||
throw new Error(`Metadata content missing for file ${filePath}`);
|
|
||||||
}
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
})();
|
||||||
|
|
||||||
const metadata = await loadMetadata();
|
|
||||||
if (metadata) {
|
|
||||||
this.addDependency(metadata.metadataPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
const assets =
|
const assets =
|
||||||
options.createAssets && metadata
|
options.createAssets && !isMDXPartial
|
||||||
? options.createAssets({frontMatter, metadata: metadata.metadataContent})
|
? options.createAssets({filePath, frontMatter})
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const fileLoaderUtils = getFileLoaderUtils(compilerName === 'server');
|
const fileLoaderUtils = getFileLoaderUtils(compilerName === 'server');
|
||||||
|
@ -156,8 +123,11 @@ ${JSON.stringify(frontMatter, null, 2)}`;
|
||||||
export const frontMatter = ${stringifyObject(frontMatter)};
|
export const frontMatter = ${stringifyObject(frontMatter)};
|
||||||
export const contentTitle = ${stringifyObject(contentTitle)};
|
export const contentTitle = ${stringifyObject(contentTitle)};
|
||||||
${
|
${
|
||||||
metadata
|
metadataPath
|
||||||
? `export const metadata = ${JSON.stringify(metadata.metadataContent)};`
|
? `export {default as metadata} from '${aliasedSitePath(
|
||||||
|
metadataPath,
|
||||||
|
options.siteDir,
|
||||||
|
)}'`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
${
|
${
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs-extra';
|
|
||||||
import logger from '@docusaurus/logger';
|
import logger from '@docusaurus/logger';
|
||||||
import {escapePath, type WebpackCompilerName} from '@docusaurus/utils';
|
import {escapePath, type WebpackCompilerName} from '@docusaurus/utils';
|
||||||
import {getProcessor, type SimpleProcessorResult} from './processor';
|
import {getProcessor, type SimpleProcessorResult} from './processor';
|
||||||
|
@ -13,23 +12,6 @@ import {validateMDXFrontMatter} from './frontMatter';
|
||||||
import preprocessor from './preprocessor';
|
import preprocessor from './preprocessor';
|
||||||
import type {Options} from './loader';
|
import type {Options} from './loader';
|
||||||
|
|
||||||
/**
|
|
||||||
* When this throws, it generally means that there's no metadata file associated
|
|
||||||
* with this MDX document. It can happen when using MDX partials (usually
|
|
||||||
* starting with _). That's why it's important to provide the `isMDXPartial`
|
|
||||||
* function in config
|
|
||||||
*/
|
|
||||||
export async function readMetadataPath(metadataPath: string): Promise<unknown> {
|
|
||||||
try {
|
|
||||||
return await fs.readJSON(metadataPath, 'utf8');
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(
|
|
||||||
logger.interpolate`MDX loader can't read MDX metadata file path=${metadataPath}. Maybe the isMDXPartial option function was not provided?`,
|
|
||||||
{cause: error as Error},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts assets an object with Webpack require calls code.
|
* Converts assets an object with Webpack require calls code.
|
||||||
* This is useful for mdx files to reference co-located assets using relative
|
* This is useful for mdx files to reference co-located assets using relative
|
||||||
|
|
|
@ -44,8 +44,6 @@ import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
|
||||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||||
import type {
|
import type {
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
BlogPostFrontMatter,
|
|
||||||
BlogPostMetadata,
|
|
||||||
Assets,
|
Assets,
|
||||||
BlogTags,
|
BlogTags,
|
||||||
BlogContent,
|
BlogContent,
|
||||||
|
@ -135,33 +133,26 @@ export default async function pluginContentBlog(
|
||||||
// Note that metadataPath must be the same/in-sync as
|
// Note that metadataPath must be the same/in-sync as
|
||||||
// the path from createData for each MDX.
|
// the path from createData for each MDX.
|
||||||
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
||||||
const metadataPath = path.join(
|
return path.join(dataDir, `${docuHash(aliasedPath)}.json`);
|
||||||
dataDir,
|
|
||||||
`${docuHash(aliasedPath)}.json`,
|
|
||||||
);
|
|
||||||
const metadataContent =
|
|
||||||
contentHelpers.sourceToBlogPost.get(aliasedPath)!.metadata;
|
|
||||||
return {
|
|
||||||
metadataPath,
|
|
||||||
metadataContent,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
// For blog posts a title in markdown is always removed
|
// For blog posts a title in markdown is always removed
|
||||||
// Blog posts title are rendered separately
|
// Blog posts title are rendered separately
|
||||||
removeContentTitle: true,
|
removeContentTitle: true,
|
||||||
// Assets allow to convert some relative images paths to
|
// createAssets converts relative paths to require() calls
|
||||||
// require() calls
|
createAssets: ({filePath}: {filePath: string}): Assets => {
|
||||||
// @ts-expect-error: TODO fix typing issue
|
const blogPost = contentHelpers.sourceToBlogPost.get(
|
||||||
createAssets: ({
|
aliasedSitePath(filePath, siteDir),
|
||||||
frontMatter,
|
)!;
|
||||||
metadata,
|
if (!blogPost) {
|
||||||
}: {
|
throw new Error(`Blog post not found for filePath=${filePath}`);
|
||||||
frontMatter: BlogPostFrontMatter;
|
}
|
||||||
metadata: BlogPostMetadata;
|
return {
|
||||||
}): Assets => ({
|
image: blogPost.metadata.frontMatter.image as string,
|
||||||
image: frontMatter.image,
|
authorsImageUrls: blogPost.metadata.authors.map(
|
||||||
authorsImageUrls: metadata.authors.map((author) => author.imageURL),
|
(author) => author.imageURL,
|
||||||
}),
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
markdownConfig: siteConfig.markdown,
|
markdownConfig: siteConfig.markdown,
|
||||||
resolveMarkdownLink: ({linkPathname, sourceFilePath}) => {
|
resolveMarkdownLink: ({linkPathname, sourceFilePath}) => {
|
||||||
const permalink = resolveMarkdownLinkPathname(linkPathname, {
|
const permalink = resolveMarkdownLinkPathname(linkPathname, {
|
||||||
|
|
|
@ -123,18 +123,9 @@ export default async function pluginContentDocs(
|
||||||
// Note that metadataPath must be the same/in-sync as
|
// Note that metadataPath must be the same/in-sync as
|
||||||
// the path from createData for each MDX.
|
// the path from createData for each MDX.
|
||||||
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
||||||
const metadataPath = path.join(
|
return path.join(dataDir, `${docuHash(aliasedPath)}.json`);
|
||||||
dataDir,
|
|
||||||
`${docuHash(aliasedPath)}.json`,
|
|
||||||
);
|
|
||||||
const metadataContent = contentHelpers.sourceToDoc.get(aliasedPath);
|
|
||||||
return {
|
|
||||||
metadataPath,
|
|
||||||
metadataContent,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
// Assets allow to convert some relative images paths to
|
// createAssets converts relative paths to require() calls
|
||||||
// require(...) calls
|
|
||||||
createAssets: ({frontMatter}: {frontMatter: DocFrontMatter}) => ({
|
createAssets: ({frontMatter}: {frontMatter: DocFrontMatter}) => ({
|
||||||
image: frontMatter.image,
|
image: frontMatter.image,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
/**
|
|
||||||
* 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 {LoadedContent, Metadata} from '@docusaurus/plugin-content-pages';
|
|
||||||
|
|
||||||
function indexPagesBySource(content: LoadedContent): Map<string, Metadata> {
|
|
||||||
return new Map(content.map((page) => [page.source, page]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this is bad, we should have a better way to do this (new lifecycle?)
|
|
||||||
// The source to page/permalink is a mutable map passed to the mdx loader
|
|
||||||
// See https://github.com/facebook/docusaurus/pull/10457
|
|
||||||
// See https://github.com/facebook/docusaurus/pull/10185
|
|
||||||
export function createContentHelpers() {
|
|
||||||
const sourceToPage = new Map<string, Metadata>();
|
|
||||||
// const sourceToPermalink = new Map<string, string>();
|
|
||||||
|
|
||||||
// Mutable map update :/
|
|
||||||
function updateContent(content: LoadedContent): void {
|
|
||||||
sourceToPage.clear();
|
|
||||||
// sourceToPermalink.clear();
|
|
||||||
indexPagesBySource(content).forEach((value, key) => {
|
|
||||||
sourceToPage.set(key, value);
|
|
||||||
// sourceToPermalink.set(key, value.metadata.permalink);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {updateContent, sourceToPage};
|
|
||||||
}
|
|
|
@ -24,7 +24,6 @@ import {
|
||||||
getContentPathList,
|
getContentPathList,
|
||||||
loadPagesContent,
|
loadPagesContent,
|
||||||
} from './content';
|
} from './content';
|
||||||
import {createContentHelpers} from './contentHelpers';
|
|
||||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||||
import type {
|
import type {
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
|
@ -47,8 +46,6 @@ export default async function pluginContentPages(
|
||||||
);
|
);
|
||||||
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);
|
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);
|
||||||
|
|
||||||
const contentHelpers = createContentHelpers();
|
|
||||||
|
|
||||||
async function createPagesMDXLoaderRule(): Promise<RuleSetRule> {
|
async function createPagesMDXLoaderRule(): Promise<RuleSetRule> {
|
||||||
const {
|
const {
|
||||||
admonitions,
|
admonitions,
|
||||||
|
@ -76,18 +73,9 @@ export default async function pluginContentPages(
|
||||||
// Note that metadataPath must be the same/in-sync as
|
// Note that metadataPath must be the same/in-sync as
|
||||||
// the path from createData for each MDX.
|
// the path from createData for each MDX.
|
||||||
const aliasedSource = aliasedSitePath(mdxPath, siteDir);
|
const aliasedSource = aliasedSitePath(mdxPath, siteDir);
|
||||||
const metadataPath = path.join(
|
return path.join(dataDir, `${docuHash(aliasedSource)}.json`);
|
||||||
dataDir,
|
|
||||||
`${docuHash(aliasedSource)}.json`,
|
|
||||||
);
|
|
||||||
const metadataContent = contentHelpers.sourceToPage.get(aliasedSource);
|
|
||||||
return {
|
|
||||||
metadataPath,
|
|
||||||
metadataContent,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
// Assets allow to convert some relative images paths to
|
// createAssets converts relative paths to require() calls
|
||||||
// require(...) calls
|
|
||||||
createAssets: ({frontMatter}: {frontMatter: PageFrontMatter}) => ({
|
createAssets: ({frontMatter}: {frontMatter: PageFrontMatter}) => ({
|
||||||
image: frontMatter.image,
|
image: frontMatter.image,
|
||||||
}),
|
}),
|
||||||
|
@ -125,7 +113,6 @@ export default async function pluginContentPages(
|
||||||
if (!content) {
|
if (!content) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
contentHelpers.updateContent(content);
|
|
||||||
await createAllRoutes({content, options, actions});
|
await createAllRoutes({content, options, actions});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue