Skip to content

Commit

Permalink
fix(docs, blog): Markdown link resolution does not support hot reload (
Browse files Browse the repository at this point in the history
  • Loading branch information
slorber authored May 31, 2024
1 parent 0eb7b64 commit a7afd9c
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 44 deletions.
8 changes: 0 additions & 8 deletions packages/docusaurus-plugin-content-blog/src/blogUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
38 changes: 34 additions & 4 deletions packages/docusaurus-plugin-content-blog/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -96,6 +123,8 @@ export default async function pluginContentBlog(
contentPaths,
});

const sourceToPermalinkHelper = createSourceToPermalinkHelper();

return {
name: PluginName,

Expand Down Expand Up @@ -201,6 +230,8 @@ export default async function pluginContentBlog(
},

async contentLoaded({content, actions}) {
sourceToPermalinkHelper.update(content);

await createAllRoutes({
baseUrl,
content,
Expand All @@ -214,7 +245,7 @@ export default async function pluginContentBlog(
return translateContent(content, translationFiles);
},

configureWebpack(_config, isServer, utils, content) {
configureWebpack() {
const {
admonitions,
rehypePlugins,
Expand All @@ -224,7 +255,6 @@ export default async function pluginContentBlog(
beforeDefaultRehypePlugins,
} = options;

const sourceToPermalink = getSourceToPermalink(content.blogPosts);
const contentDirs = getContentPathList(contentPaths);

function createMDXLoader(): RuleSetUseItem {
Expand Down Expand Up @@ -271,7 +301,7 @@ export default async function pluginContentBlog(
resolveMarkdownLink: ({linkPathname, sourceFilePath}) => {
const permalink = resolveMarkdownLinkPathname(linkPathname, {
sourceFilePath,
sourceToPermalink,
sourceToPermalink: sourceToPermalinkHelper.get(),
siteDir,
contentPaths,
});
Expand Down
44 changes: 31 additions & 13 deletions packages/docusaurus-plugin-content-docs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
createSlugger,
resolveMarkdownLinkPathname,
DEFAULT_PLUGIN_ID,
type SourceToPermalink,
type TagsFile,
} from '@docusaurus/utils';
import {
getTagsFile,
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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',

Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -318,7 +336,7 @@ export default async function pluginContentDocs(
);
const permalink = resolveMarkdownLinkPathname(linkPathname, {
sourceFilePath,
sourceToPermalink,
sourceToPermalink: sourceToPermalinkHelper.get(),
siteDir,
contentPaths: version,
});
Expand Down
4 changes: 0 additions & 4 deletions packages/docusaurus-plugin-content-docs/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down
26 changes: 15 additions & 11 deletions packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
6 changes: 5 additions & 1 deletion packages/docusaurus-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 8 additions & 3 deletions packages/docusaurus-utils/src/markdownLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,19 @@ 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
export function resolveMarkdownLinkPathname(
linkPathname: string,
context: {
sourceFilePath: string;
sourceToPermalink: {[aliasedFilePath: string]: string};
sourceToPermalink: SourceToPermalink;
contentPaths: ContentPaths;
siteDir: string;
},
Expand All @@ -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;
}

0 comments on commit a7afd9c

Please sign in to comment.