From d4ad1ca10558863d97ccfa7b5bd94ec86ce90651 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 31 May 2024 16:48:34 +0200 Subject: [PATCH 1/4] use a Map type for sourceToPermalink lookup perf --- .../src/blogUtils.ts | 7 +++-- .../src/index.ts | 5 ++-- .../src/types.ts | 4 --- .../src/__tests__/markdownLinks.test.ts | 26 +++++++++++-------- packages/docusaurus-utils/src/index.ts | 6 ++++- .../docusaurus-utils/src/markdownLinks.ts | 11 +++++--- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 89f1c8f36d4f..b7c76c031d81 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -26,6 +26,7 @@ import { isUnlisted, isDraft, readLastUpdateData, + type SourceToPermalink, } from '@docusaurus/utils'; import {validateBlogPostFrontMatter} from './frontMatter'; import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors'; @@ -43,10 +44,8 @@ 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( +export function getSourceToPermalink(blogPosts: BlogPost[]): SourceToPermalink { + return new Map( blogPosts.map(({metadata: {source, permalink}}) => [source, permalink]), ); } diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index baf8b8a4f05a..9e8a253b4708 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -19,6 +19,7 @@ import { createSlugger, resolveMarkdownLinkPathname, DEFAULT_PLUGIN_ID, + type SourceToPermalink, } from '@docusaurus/utils'; import {loadSidebars, resolveSidebarPathOption} from './sidebars'; import {CategoryMetadataFilenamePattern} from './sidebars/generator'; @@ -54,7 +55,7 @@ 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'; export default async function pluginContentDocs( @@ -261,7 +262,7 @@ export default async function pluginContentDocs( // 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( + return new Map( allDocs.map(({source, permalink}) => [source, permalink]), ); } diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index c44c60896a80..170cecdfb7ca 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -19,10 +19,6 @@ export type DocFile = { content: string; }; -export type SourceToPermalink = { - [source: string]: string; -}; - export type VersionTag = Tag & { /** All doc ids having this tag. */ docIds: string[]; diff --git a/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts b/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts index c9526c12dcd3..9a3e0ddfaa77 100644 --- a/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts +++ b/packages/docusaurus-utils/src/__tests__/markdownLinks.test.ts @@ -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) { diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 6e6c3c67188e..536144ce1fe1 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -74,7 +74,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, diff --git a/packages/docusaurus-utils/src/markdownLinks.ts b/packages/docusaurus-utils/src/markdownLinks.ts index 1b65776187f8..1183656aa182 100644 --- a/packages/docusaurus-utils/src/markdownLinks.ts +++ b/packages/docusaurus-utils/src/markdownLinks.ts @@ -40,6 +40,11 @@ export type BrokenMarkdownLink = { 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; } From 112ff4fe6b79a038fcf256be05928d0c97efd0af Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 31 May 2024 17:08:09 +0200 Subject: [PATCH 2/4] fix sourceToPermalink resolution bug --- .../src/blogUtils.ts | 7 ---- .../src/index.ts | 37 ++++++++++++++++-- .../src/index.ts | 38 +++++++++++++------ 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index b7c76c031d81..3215c9da279b 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -26,7 +26,6 @@ import { isUnlisted, isDraft, readLastUpdateData, - type SourceToPermalink, } from '@docusaurus/utils'; import {validateBlogPostFrontMatter} from './frontMatter'; import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors'; @@ -44,12 +43,6 @@ export function truncate(fileString: string, truncateMarker: RegExp): string { return fileString.split(truncateMarker, 1).shift()!; } -export function getSourceToPermalink(blogPosts: BlogPost[]): SourceToPermalink { - return new Map( - blogPosts.map(({metadata: {source, permalink}}) => [source, permalink]), - ); -} - export function paginateBlogPosts({ blogPosts, basePageUrl, diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index dc64b8b45155..aa6b8034256e 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -19,9 +19,9 @@ import { getDataFilePath, DEFAULT_PLUGIN_ID, resolveMarkdownLinkPathname, + type SourceToPermalink, } from '@docusaurus/utils'; import { - getSourceToPermalink, getBlogTags, paginateBlogPosts, shouldBeListed, @@ -49,6 +49,32 @@ 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 +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, @@ -95,6 +121,8 @@ export default async function pluginContentBlog( contentPaths, }); + const sourceToPermalinkHelper = createSourceToPermalinkHelper(); + return { name: PluginName, @@ -193,6 +221,8 @@ export default async function pluginContentBlog( }, async contentLoaded({content, actions}) { + sourceToPermalinkHelper.update(content); + await createAllRoutes({ baseUrl, content, @@ -206,7 +236,7 @@ export default async function pluginContentBlog( return translateContent(content, translationFiles); }, - configureWebpack(_config, isServer, utils, content) { + configureWebpack() { const { admonitions, rehypePlugins, @@ -216,7 +246,6 @@ export default async function pluginContentBlog( beforeDefaultRehypePlugins, } = options; - const sourceToPermalink = getSourceToPermalink(content.blogPosts); const contentDirs = getContentPathList(contentPaths); function createMDXLoader(): RuleSetUseItem { @@ -263,7 +292,7 @@ export default async function pluginContentBlog( resolveMarkdownLink: ({linkPathname, sourceFilePath}) => { const permalink = resolveMarkdownLinkPathname(linkPathname, { sourceFilePath, - sourceToPermalink, + sourceToPermalink: sourceToPermalinkHelper.get(), siteDir, contentPaths, }); diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 9e8a253b4708..63081dc84a0f 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -58,6 +58,28 @@ import type {LoadContext, Plugin} from '@docusaurus/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 +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, @@ -84,6 +106,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', @@ -228,6 +252,8 @@ export default async function pluginContentDocs( }, async contentLoaded({content, actions}) { + sourceToPermalinkHelper.update(content); + const versions: FullVersion[] = content.loadedVersions.map(toFullVersion); await createAllRoutes({ @@ -258,16 +284,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 new Map( - allDocs.map(({source, permalink}) => [source, permalink]), - ); - } - const sourceToPermalink = getSourceToPermalink(); - function createMDXLoader(): RuleSetUseItem { const loaderOptions: MDXLoaderOptions = { admonitions: options.admonitions, @@ -302,7 +318,7 @@ export default async function pluginContentDocs( ); const permalink = resolveMarkdownLinkPathname(linkPathname, { sourceFilePath, - sourceToPermalink, + sourceToPermalink: sourceToPermalinkHelper.get(), siteDir, contentPaths: version, }); From d4a55c4ea94f2dd9ca3b71e03e6ad3c5107ca319 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 31 May 2024 17:15:26 +0200 Subject: [PATCH 3/4] add pr comment --- packages/docusaurus-plugin-content-blog/src/index.ts | 1 + packages/docusaurus-plugin-content-docs/src/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index aa6b8034256e..bed7f998d5f0 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -52,6 +52,7 @@ 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(); diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 63081dc84a0f..ab077255ffa6 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -61,6 +61,7 @@ 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(); From 39caf6b8c7d09f250fe20d30767cf3131ae98c7f Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 31 May 2024 17:40:48 +0200 Subject: [PATCH 4/4] Fix merge issue --- packages/docusaurus-plugin-content-docs/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 7ae0093fecb6..937f7f7fa9d0 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -20,6 +20,7 @@ import { resolveMarkdownLinkPathname, DEFAULT_PLUGIN_ID, type SourceToPermalink, + type TagsFile, } from '@docusaurus/utils'; import { getTagsFile, @@ -48,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 {