mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 02:08:36 +02:00
fix(docs, blog): Markdown link resolution does not support hot reload (#10185)
This commit is contained in:
parent
0eb7b64aac
commit
a7afd9cc87
7 changed files with 93 additions and 44 deletions
|
@ -45,14 +45,6 @@ export function truncate(fileString: string, truncateMarker: RegExp): string {
|
|||
return fileString.split(truncateMarker, 1).shift()!;
|
||||
}
|
||||
|
||||
export function getSourceToPermalink(blogPosts: BlogPost[]): {
|
||||
[aliasedPath: string]: string;
|
||||
} {
|
||||
return Object.fromEntries(
|
||||
blogPosts.map(({metadata: {source, permalink}}) => [source, permalink]),
|
||||
);
|
||||
}
|
||||
|
||||
export function paginateBlogPosts({
|
||||
blogPosts,
|
||||
basePageUrl,
|
||||
|
|
|
@ -19,10 +19,10 @@ import {
|
|||
getDataFilePath,
|
||||
DEFAULT_PLUGIN_ID,
|
||||
resolveMarkdownLinkPathname,
|
||||
type SourceToPermalink,
|
||||
} from '@docusaurus/utils';
|
||||
import {getTagsFilePathsToWatch} from '@docusaurus/utils-validation';
|
||||
import {
|
||||
getSourceToPermalink,
|
||||
getBlogTags,
|
||||
paginateBlogPosts,
|
||||
shouldBeListed,
|
||||
|
@ -50,6 +50,33 @@ import type {RuleSetUseItem} from 'webpack';
|
|||
|
||||
const PluginName = 'docusaurus-plugin-content-blog';
|
||||
|
||||
// TODO this is bad, we should have a better way to do this (new lifecycle?)
|
||||
// The source to permalink is currently a mutable map passed to the mdx loader
|
||||
// for link resolution
|
||||
// see https://github.com/facebook/docusaurus/pull/10185
|
||||
function createSourceToPermalinkHelper() {
|
||||
const sourceToPermalink: SourceToPermalink = new Map();
|
||||
|
||||
function computeSourceToPermalink(content: BlogContent): SourceToPermalink {
|
||||
return new Map(
|
||||
content.blogPosts.map(({metadata: {source, permalink}}) => [
|
||||
source,
|
||||
permalink,
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
// Mutable map update :/
|
||||
function update(content: BlogContent): void {
|
||||
sourceToPermalink.clear();
|
||||
computeSourceToPermalink(content).forEach((value, key) => {
|
||||
sourceToPermalink.set(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
return {get: () => sourceToPermalink, update};
|
||||
}
|
||||
|
||||
export default async function pluginContentBlog(
|
||||
context: LoadContext,
|
||||
options: PluginOptions,
|
||||
|
@ -96,6 +123,8 @@ export default async function pluginContentBlog(
|
|||
contentPaths,
|
||||
});
|
||||
|
||||
const sourceToPermalinkHelper = createSourceToPermalinkHelper();
|
||||
|
||||
return {
|
||||
name: PluginName,
|
||||
|
||||
|
@ -201,6 +230,8 @@ export default async function pluginContentBlog(
|
|||
},
|
||||
|
||||
async contentLoaded({content, actions}) {
|
||||
sourceToPermalinkHelper.update(content);
|
||||
|
||||
await createAllRoutes({
|
||||
baseUrl,
|
||||
content,
|
||||
|
@ -214,7 +245,7 @@ export default async function pluginContentBlog(
|
|||
return translateContent(content, translationFiles);
|
||||
},
|
||||
|
||||
configureWebpack(_config, isServer, utils, content) {
|
||||
configureWebpack() {
|
||||
const {
|
||||
admonitions,
|
||||
rehypePlugins,
|
||||
|
@ -224,7 +255,6 @@ export default async function pluginContentBlog(
|
|||
beforeDefaultRehypePlugins,
|
||||
} = options;
|
||||
|
||||
const sourceToPermalink = getSourceToPermalink(content.blogPosts);
|
||||
const contentDirs = getContentPathList(contentPaths);
|
||||
|
||||
function createMDXLoader(): RuleSetUseItem {
|
||||
|
@ -271,7 +301,7 @@ export default async function pluginContentBlog(
|
|||
resolveMarkdownLink: ({linkPathname, sourceFilePath}) => {
|
||||
const permalink = resolveMarkdownLinkPathname(linkPathname, {
|
||||
sourceFilePath,
|
||||
sourceToPermalink,
|
||||
sourceToPermalink: sourceToPermalinkHelper.get(),
|
||||
siteDir,
|
||||
contentPaths,
|
||||
});
|
||||
|
|
|
@ -19,6 +19,8 @@ import {
|
|||
createSlugger,
|
||||
resolveMarkdownLinkPathname,
|
||||
DEFAULT_PLUGIN_ID,
|
||||
type SourceToPermalink,
|
||||
type TagsFile,
|
||||
} from '@docusaurus/utils';
|
||||
import {
|
||||
getTagsFile,
|
||||
|
@ -47,7 +49,6 @@ import {
|
|||
} from './translations';
|
||||
import {createAllRoutes} from './routes';
|
||||
import {createSidebarsUtils} from './sidebars/utils';
|
||||
import type {TagsFile} from '@docusaurus/utils';
|
||||
import type {Options as MDXLoaderOptions} from '@docusaurus/mdx-loader';
|
||||
|
||||
import type {
|
||||
|
@ -59,9 +60,32 @@ import type {
|
|||
LoadedVersion,
|
||||
} from '@docusaurus/plugin-content-docs';
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import type {SourceToPermalink, DocFile, FullVersion} from './types';
|
||||
import type {DocFile, FullVersion} from './types';
|
||||
import type {RuleSetUseItem} from 'webpack';
|
||||
|
||||
// TODO this is bad, we should have a better way to do this (new lifecycle?)
|
||||
// The source to permalink is currently a mutable map passed to the mdx loader
|
||||
// for link resolution
|
||||
// see https://github.com/facebook/docusaurus/pull/10185
|
||||
function createSourceToPermalinkHelper() {
|
||||
const sourceToPermalink: SourceToPermalink = new Map();
|
||||
|
||||
function computeSourceToPermalink(content: LoadedContent): SourceToPermalink {
|
||||
const allDocs = content.loadedVersions.flatMap((v) => v.docs);
|
||||
return new Map(allDocs.map(({source, permalink}) => [source, permalink]));
|
||||
}
|
||||
|
||||
// Mutable map update :/
|
||||
function update(content: LoadedContent): void {
|
||||
sourceToPermalink.clear();
|
||||
computeSourceToPermalink(content).forEach((value, key) => {
|
||||
sourceToPermalink.set(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
return {get: () => sourceToPermalink, update};
|
||||
}
|
||||
|
||||
export default async function pluginContentDocs(
|
||||
context: LoadContext,
|
||||
options: PluginOptions,
|
||||
|
@ -88,6 +112,8 @@ export default async function pluginContentDocs(
|
|||
// TODO env should be injected into all plugins
|
||||
const env = process.env.NODE_ENV as DocEnv;
|
||||
|
||||
const sourceToPermalinkHelper = createSourceToPermalinkHelper();
|
||||
|
||||
return {
|
||||
name: 'docusaurus-plugin-content-docs',
|
||||
|
||||
|
@ -244,6 +270,8 @@ export default async function pluginContentDocs(
|
|||
},
|
||||
|
||||
async contentLoaded({content, actions}) {
|
||||
sourceToPermalinkHelper.update(content);
|
||||
|
||||
const versions: FullVersion[] = content.loadedVersions.map(toFullVersion);
|
||||
|
||||
await createAllRoutes({
|
||||
|
@ -274,16 +302,6 @@ export default async function pluginContentDocs(
|
|||
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
||||
.map(addTrailingPathSeparator);
|
||||
|
||||
// TODO this does not re-run when content gets updated in dev!
|
||||
// it's probably better to restore a mutable cache in the plugin
|
||||
function getSourceToPermalink(): SourceToPermalink {
|
||||
const allDocs = content.loadedVersions.flatMap((v) => v.docs);
|
||||
return Object.fromEntries(
|
||||
allDocs.map(({source, permalink}) => [source, permalink]),
|
||||
);
|
||||
}
|
||||
const sourceToPermalink = getSourceToPermalink();
|
||||
|
||||
function createMDXLoader(): RuleSetUseItem {
|
||||
const loaderOptions: MDXLoaderOptions = {
|
||||
admonitions: options.admonitions,
|
||||
|
@ -318,7 +336,7 @@ export default async function pluginContentDocs(
|
|||
);
|
||||
const permalink = resolveMarkdownLinkPathname(linkPathname, {
|
||||
sourceFilePath,
|
||||
sourceToPermalink,
|
||||
sourceToPermalink: sourceToPermalinkHelper.get(),
|
||||
siteDir,
|
||||
contentPaths: version,
|
||||
});
|
||||
|
|
|
@ -19,10 +19,6 @@ export type DocFile = {
|
|||
content: string;
|
||||
};
|
||||
|
||||
export type SourceToPermalink = {
|
||||
[source: string]: string;
|
||||
};
|
||||
|
||||
export type VersionTag = TagMetadata & {
|
||||
/** All doc ids having this tag. */
|
||||
docIds: string[];
|
||||
|
|
|
@ -18,12 +18,14 @@ describe('resolveMarkdownLinkPathname', () => {
|
|||
contentPath: 'docs',
|
||||
contentPathLocalized: 'i18n/docs-localized',
|
||||
},
|
||||
sourceToPermalink: {
|
||||
'@site/docs/intro.md': '/docs/intro',
|
||||
'@site/docs/foo.md': '/doc/foo',
|
||||
'@site/docs/bar/baz.md': '/doc/baz',
|
||||
'@site/docs/http.foo.md': '/doc/http',
|
||||
},
|
||||
sourceToPermalink: new Map(
|
||||
Object.entries({
|
||||
'@site/docs/intro.md': '/docs/intro',
|
||||
'@site/docs/foo.md': '/doc/foo',
|
||||
'@site/docs/bar/baz.md': '/doc/baz',
|
||||
'@site/docs/http.foo.md': '/doc/http',
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
function test(linkPathname: string, expectedOutput: string) {
|
||||
|
@ -50,11 +52,13 @@ describe('resolveMarkdownLinkPathname', () => {
|
|||
contentPathLocalized: 'i18n/docs-localized',
|
||||
},
|
||||
|
||||
sourceToPermalink: {
|
||||
'@site/docs/intro/intro.md': '/docs/intro',
|
||||
'@site/docs/intro/another.md': '/docs/another',
|
||||
'@site/docs/api/classes/divine_uri.URI.md': '/docs/api/classes/uri',
|
||||
},
|
||||
sourceToPermalink: new Map(
|
||||
Object.entries({
|
||||
'@site/docs/intro/intro.md': '/docs/intro',
|
||||
'@site/docs/intro/another.md': '/docs/another',
|
||||
'@site/docs/api/classes/divine_uri.URI.md': '/docs/api/classes/uri',
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
function test(linkPathname: string, expectedOutput: string) {
|
||||
|
|
|
@ -77,7 +77,11 @@ export {
|
|||
writeMarkdownHeadingId,
|
||||
type WriteHeadingIDOptions,
|
||||
} from './markdownUtils';
|
||||
export {type ContentPaths, resolveMarkdownLinkPathname} from './markdownLinks';
|
||||
export {
|
||||
type ContentPaths,
|
||||
type SourceToPermalink,
|
||||
resolveMarkdownLinkPathname,
|
||||
} from './markdownLinks';
|
||||
export {type SluggerOptions, type Slugger, createSlugger} from './slugger';
|
||||
export {
|
||||
isNameTooLong,
|
||||
|
|
|
@ -40,6 +40,11 @@ export type BrokenMarkdownLink<T extends ContentPaths> = {
|
|||
link: string;
|
||||
};
|
||||
|
||||
export type SourceToPermalink = Map<
|
||||
string, // Aliased source path: "@site/docs/content.mdx"
|
||||
string // Permalink: "/docs/content"
|
||||
>;
|
||||
|
||||
// Note this is historical logic extracted during a 2024 refactor
|
||||
// The algo has been kept exactly as before for retro compatibility
|
||||
// See also https://github.com/facebook/docusaurus/pull/10168
|
||||
|
@ -47,7 +52,7 @@ export function resolveMarkdownLinkPathname(
|
|||
linkPathname: string,
|
||||
context: {
|
||||
sourceFilePath: string;
|
||||
sourceToPermalink: {[aliasedFilePath: string]: string};
|
||||
sourceToPermalink: SourceToPermalink;
|
||||
contentPaths: ContentPaths;
|
||||
siteDir: string;
|
||||
},
|
||||
|
@ -66,9 +71,9 @@ export function resolveMarkdownLinkPathname(
|
|||
const aliasedSourceMatch = sourceDirsToTry
|
||||
.map((sourceDir) => path.join(sourceDir, decodeURIComponent(linkPathname)))
|
||||
.map((source) => aliasedSitePath(source, siteDir))
|
||||
.find((source) => sourceToPermalink[source]);
|
||||
.find((source) => sourceToPermalink.has(source));
|
||||
|
||||
return aliasedSourceMatch
|
||||
? sourceToPermalink[aliasedSourceMatch] ?? null
|
||||
? sourceToPermalink.get(aliasedSourceMatch) ?? null
|
||||
: null;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue