diff --git a/src/utils/docs.ts b/src/utils/docs.ts index 78eef1510..c71bfac87 100644 --- a/src/utils/docs.ts +++ b/src/utils/docs.ts @@ -15,17 +15,24 @@ import { } from './docs-redirects' import { removeLeadingSlash } from './utils' -export const loadDocs = async ({ - repo, - branch, - docsRoot, - docsPath, -}: { - repo: string - branch: string - docsRoot: string - docsPath: string -}) => { +type FetchDocs = ( + opts: Parameters[0], +) => ReturnType + +export const loadDocs = async ( + { + repo, + branch, + docsRoot, + docsPath, + }: { + repo: string + branch: string + docsRoot: string + docsPath: string + }, + fetchDocsFn: FetchDocs = fetchDocs, +) => { if (!branch || !docsRoot || !docsPath) { throw notFound({ data: { @@ -38,7 +45,7 @@ export const loadDocs = async ({ let doc try { - doc = await fetchDocs({ + doc = await fetchDocsFn({ data: { repo, branch, @@ -50,7 +57,7 @@ export const loadDocs = async ({ throw error } - doc = await fetchDocs({ + doc = await fetchDocsFn({ data: { repo, branch, diff --git a/tests/docs-route-index-fallback.test.ts b/tests/docs-route-index-fallback.test.ts new file mode 100644 index 000000000..b44e412f0 --- /dev/null +++ b/tests/docs-route-index-fallback.test.ts @@ -0,0 +1,91 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { loadDocs } from '../src/utils/docs' + +type FetchDocs = NonNullable[1]> +type FetchedDoc = Awaited> + +const repoFiles: Record = { + 'docs/overview.md': 'Overview', + 'docs/reference/index.md': 'Core API Reference', + 'docs/framework/react/reference/index.md': 'React API Reference', +} + +function createFetchDocs() { + const fetchedFilePaths: Array = [] + + const fetchDocs: FetchDocs = async ({ data }) => { + fetchedFilePaths.push(data.filePath) + const content = repoFiles[data.filePath] + + if (content === undefined) { + throw { isNotFound: true } + } + + const doc: FetchedDoc = { + content, + title: content, + description: '', + keywords: undefined, + frameworks: [], + filePath: data.filePath, + frontmatter: { description: '' }, + } + + return doc + } + + return { fetchDocs, fetchedFilePaths } +} + +function load(docsPath: string, fetchDocs: FetchDocs) { + return loadDocs( + { + repo: 'TanStack/pacer', + branch: 'main', + docsRoot: 'docs', + docsPath, + }, + fetchDocs, + ) +} + +test('canonical reference paths load their index files', async () => { + for (const [docsPath, expectedFilePath] of [ + ['reference', 'docs/reference/index.md'], + ['framework/react/reference', 'docs/framework/react/reference/index.md'], + ]) { + const { fetchDocs, fetchedFilePaths } = createFetchDocs() + const doc = await load(docsPath, fetchDocs) + + assert.equal(doc.filePath, expectedFilePath) + assert.deepEqual(fetchedFilePaths, [ + `docs/${docsPath}.md`, + expectedFilePath, + ]) + } +}) + +test('plain docs files load without an index fallback request', async () => { + const { fetchDocs, fetchedFilePaths } = createFetchDocs() + const doc = await load('overview', fetchDocs) + + assert.equal(doc.filePath, 'docs/overview.md') + assert.deepEqual(fetchedFilePaths, ['docs/overview.md']) +}) + +test('non-404 index fetch failures propagate', async () => { + let fetchCount = 0 + const fetchDocs: FetchDocs = async () => { + fetchCount += 1 + + if (fetchCount === 1) { + throw { isNotFound: true } + } + + throw new Error('GitHub unavailable') + } + + await assert.rejects(() => load('reference', fetchDocs), /GitHub unavailable/) + assert.equal(fetchCount, 2) +})