diff --git a/packages/docusaurus-utils/src/__tests__/emitUtils.test.ts b/packages/docusaurus-utils/src/__tests__/emitUtils.test.ts index 0d6e700721f4..d8a5c2f137b2 100644 --- a/packages/docusaurus-utils/src/__tests__/emitUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/emitUtils.test.ts @@ -7,6 +7,7 @@ import {describe, expect, it, vi} from 'vitest'; import path from 'path'; +import os from 'os'; import fs from 'fs-extra'; import {readOutputHTMLFile, generate} from '../emitUtils'; @@ -89,6 +90,15 @@ describe('readOutputHTMLFile', () => { ).then(String), ).resolves.toBe('htmlFile.html\n'); }); + it('reads file ending in .html with trailing slash true', async () => { + await expect( + readOutputHTMLFile( + '/htmlFile.html', + path.join(__dirname, '__fixtures__/build-snap'), + true, + ).then(String), + ).resolves.toBe('htmlFile.html\n'); + }); it('reads file ending in .html in folder containing .html', async () => { await expect( readOutputHTMLFile( @@ -105,6 +115,20 @@ describe('readOutputHTMLFile', () => { ).then(String), ).resolves.toBe('nestedHtmlFile.html\n'); }); + it('reads index.html when the permalink path is a directory', async () => { + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'docusaurus-')); + try { + const dirPath = path.join(tempDir, 'dir.html'); + await fs.ensureDir(dirPath); + await fs.writeFile(path.join(dirPath, 'index.html'), 'dir-index\n'); + + await expect( + readOutputHTMLFile('/dir.html', tempDir, undefined).then(String), + ).resolves.toBe('dir-index\n'); + } finally { + await fs.remove(tempDir); + } + }); // Can it ever happen? it('throws if file does not exist', async () => { await expect( diff --git a/packages/docusaurus-utils/src/emitUtils.ts b/packages/docusaurus-utils/src/emitUtils.ts index 239569defb01..5db2904b9176 100644 --- a/packages/docusaurus-utils/src/emitUtils.ts +++ b/packages/docusaurus-utils/src/emitUtils.ts @@ -82,24 +82,60 @@ export async function readOutputHTMLFile( outDir: string, trailingSlash: boolean | undefined, ): Promise { - const withTrailingSlashPath = path.join(outDir, permalink, 'index.html'); - const withoutTrailingSlashPath = (() => { - const basePath = path.join(outDir, permalink.replace(/\/$/, '')); - const htmlSuffix = /\.html?$/i.test(basePath) ? '' : '.html'; - return `${basePath}${htmlSuffix}`; - })(); + const normalizedPermalink = permalink.replace(/\/$/, ''); + const withTrailingSlashPath = path.join( + outDir, + normalizedPermalink, + 'index.html', + ); + const withoutTrailingSlashPath = path.join(outDir, normalizedPermalink); + const isHtmlPath = /\.html?$/i.test(normalizedPermalink); - const possibleHtmlPaths = [ - trailingSlash !== false && withTrailingSlashPath, - trailingSlash !== true && withoutTrailingSlashPath, - ].filter((p): p is string => Boolean(p)); + const possibleHtmlPaths = isHtmlPath + ? [withoutTrailingSlashPath] + : trailingSlash === true + ? [withTrailingSlashPath] + : trailingSlash === false + ? [`${withoutTrailingSlashPath}.html`] + : [withTrailingSlashPath, `${withoutTrailingSlashPath}.html`]; - const HTMLPath = await findAsyncSequential(possibleHtmlPaths, fs.pathExists); + async function readCandidateHTMLFile( + candidatePath: string, + ): Promise { + if (!(await fs.pathExists(candidatePath))) { + return null; + } + try { + return await fs.readFile(candidatePath); + } catch (error) { + if ((error as NodeJS.ErrnoException)?.code !== 'EISDIR') { + throw error; + } + } + + const candidateIndexPath = path.join(candidatePath, 'index.html'); + if (await fs.pathExists(candidateIndexPath)) { + return fs.readFile(candidateIndexPath); + } + + return null; + } + + const HTMLPath = await findAsyncSequential(possibleHtmlPaths, async (p) => + Boolean(await readCandidateHTMLFile(p)), + ); if (!HTMLPath) { throw new Error( - `Expected output HTML file to be found at ${withTrailingSlashPath} for permalink ${permalink}.`, + `Expected output HTML file to be found at ${possibleHtmlPaths[0]} for permalink ${permalink}.`, + ); + } + + const content = await readCandidateHTMLFile(HTMLPath); + if (!content) { + throw new Error( + `Expected output HTML file to be found at ${HTMLPath} for permalink ${permalink}.`, ); } - return fs.readFile(HTMLPath); + return content; }