From 751e6b25783d40303bb9d45008135db0f46cf56b Mon Sep 17 00:00:00 2001 From: alexandrosang21 Date: Thu, 30 Jul 2026 14:26:21 +0300 Subject: [PATCH] fix(utils): resolve site-relative paths in the eager Git VCS The eager strategy keys its file map by absolute paths, but looks up whatever path it is handed. RouteMetadata.sourceFilePath is documented as relative to the site directory, and the sitemap plugin forwards it unchanged, so the lookup missed and returned null. The result was a silently missing in sitemap.xml under experimental_vcs: 'default-v2', which selects the eager strategy in production. 'git-ad-hoc' and 'default-v1' were unaffected because they shell out to git log, which resolves relative paths itself. Resolves the path against the site dir when it is not already absolute. Every existing eager test passed an absolute path, which is why this was not caught. --- .../src/vcs/__tests__/gitUtils.test.ts | 17 +++++++++++++++++ .../docusaurus-utils/src/vcs/vcsGitEager.ts | 16 ++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts b/packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts index 6a58c7d95b90..00b939c2b071 100644 --- a/packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts +++ b/packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts @@ -808,6 +808,23 @@ describe('VSC strategies', () => { }); }); + it('can read repo file info from a site-relative path', async () => { + const {vcs} = await initVsc(); + + // RouteMetadata.sourceFilePath is documented as relative to the site dir, + // and the sitemap plugin forwards it here unchanged. The map is keyed by + // absolute paths, so a relative path used to miss and silently return + // null, dropping from the sitemap. + await expect(vcs.getFileLastUpdateInfo('rootFile.md')).resolves.toEqual({ + author: 'Seb', + timestamp: new Date('2020-06-19').getTime(), + }); + await expect(vcs.getFileCreationInfo('rootFile.md')).resolves.toEqual({ + author: 'Seb', + timestamp: new Date('2020-06-19').getTime(), + }); + }); + it('can read submodule file', async () => { const {vcs, repoDir} = await initVsc(); diff --git a/packages/docusaurus-utils/src/vcs/vcsGitEager.ts b/packages/docusaurus-utils/src/vcs/vcsGitEager.ts index c250877808c7..a505b485c1f9 100644 --- a/packages/docusaurus-utils/src/vcs/vcsGitEager.ts +++ b/packages/docusaurus-utils/src/vcs/vcsGitEager.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {resolve, basename} from 'node:path'; +import {resolve, basename, isAbsolute} from 'node:path'; import logger, {PerfLogger} from '@docusaurus/logger'; import { getGitAllRepoRoots, @@ -82,11 +82,22 @@ async function initialize({ export function createVcsGitEagerConfig(): VcsConfig { let initPromise: Promise | null = null; + let initSiteDir: string | null = null; async function getGitFileInfo(filePath: string): Promise { const init = (await initPromise)!; if (init.type === 'success') { - return init.filesMap.get(filePath) ?? null; + // The map is keyed by absolute paths, but callers may pass a path + // relative to the site dir. RouteMetadata.sourceFilePath in particular + // "is expected to be relative to the site directory", which is what the + // sitemap plugin forwards here. The ad-hoc strategy tolerates both + // because it shells out to `git log`, so a relative path silently + // returned null here instead. + const key = + isAbsolute(filePath) || !initSiteDir + ? filePath + : resolve(initSiteDir, filePath); + return init.filesMap.get(key) ?? null; } else if (init.reason === 'not-in-worktree') { throw new Error( `This Docusaurus site is outside any Git worktree. @@ -114,6 +125,7 @@ Unable to read Git info for file ${logger.path(filePath)} `, return; } + initSiteDir = siteDir; initPromise = PerfLogger.async('Git Eager VCS init', () => initialize({siteDir}), );