Skip to content

Commit fb500e3

Browse files
committed
Updated with review comments.
1 parent 1ebf628 commit fb500e3

File tree

6 files changed

+86
-49
lines changed

6 files changed

+86
-49
lines changed

src/__tests__/pages/api/__tests__/[version]/icons/[iconName].test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function createFetchMock(): typeof fetch {
2828
json: () => Promise.resolve(mockIconsIndex),
2929
} as Response)
3030
}
31-
const match = url.match(/\/iconsSvgs\/([^/]+)\.json/)
31+
const match = url.match(/\/api\/[^/]+\/icons\/([^/]+)\.json/)
3232
if (match) {
3333
const setId = match[1]
3434
const svgs = mockIconSvgs[setId] ?? {}

src/pages/api/[version]/icons/[iconName].ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const GET: APIRoute = async ({ params, url, locals }) => {
4949
)
5050
}
5151

52-
const svgs = await fetchIconSvgs(url, 'pf', assetsFetch)
52+
const svgs = await fetchIconSvgs(url, version, 'pf', assetsFetch)
5353
const svg = svgs?.[reactName] ?? null
5454

5555
if (!svg) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { APIRoute, GetStaticPaths } from 'astro'
2+
import { getVersionsFromIndexFile } from '../../../../utils/apiIndex/get'
3+
import { createJsonResponse } from '../../../../utils/apiHelpers'
4+
import { getIconSvgsForSet } from '../../../../utils/icons/reactIcons'
5+
6+
/**
7+
* Prerender at build time so this doesn't run in the Cloudflare Worker.
8+
* getIconSvgsForSet() reads from @patternfly/react-icons/dist/static (Node fs).
9+
* Serves JSON of all icon SVGs for a set (e.g. /api/v5/icons/pf.json).
10+
*/
11+
export const prerender = true
12+
13+
export const getStaticPaths: GetStaticPaths = async () => {
14+
const versions = await getVersionsFromIndexFile()
15+
return versions.flatMap((version) => [
16+
{ params: { version, setId: 'pf' } },
17+
])
18+
}
19+
20+
export const GET: APIRoute = async ({ params }) => {
21+
const { version, setId } = params
22+
if (!version) {
23+
return createJsonResponse(
24+
{ error: 'Version parameter is required' },
25+
400,
26+
)
27+
}
28+
if (!setId) {
29+
return createJsonResponse({ error: 'Set ID is required' }, 400)
30+
}
31+
32+
try {
33+
const svgs = await getIconSvgsForSet(setId)
34+
return createJsonResponse(svgs)
35+
} catch (error) {
36+
const details = error instanceof Error ? error.message : String(error)
37+
return createJsonResponse(
38+
{ error: 'Failed to load icon SVGs', details },
39+
500,
40+
)
41+
}
42+
}

src/pages/iconsSvgs/[setId].json.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/utils/apiIndex/get.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,45 @@ export async function getApiIndex(): Promise<ApiIndex> {
5252
}
5353
}
5454

55+
/**
56+
* Reads only the versions array from the API index file.
57+
* Use this when the full index may not be generated yet or has a minimal structure
58+
* (e.g. during getStaticPaths for icon routes). Does not validate examples/css.
59+
*
60+
* @returns Promise resolving to array of version strings (e.g., ['v5', 'v6'])
61+
* @throws Error if the index file is missing or has no valid "versions" array
62+
*/
63+
export async function getVersionsFromIndexFile(): Promise<string[]> {
64+
const outputDir = await getOutputDir()
65+
const indexPath = join(outputDir, 'apiIndex.json')
66+
67+
try {
68+
const content = await readFile(indexPath, 'utf-8')
69+
const parsed = JSON.parse(content)
70+
71+
if (!parsed.versions || !Array.isArray(parsed.versions)) {
72+
throw new Error(
73+
`Invalid API index structure at ${indexPath}: missing or invalid "versions" array`,
74+
)
75+
}
76+
77+
return parsed.versions
78+
} catch (error) {
79+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
80+
throw new Error(
81+
`API index file not found at ${indexPath}. ` +
82+
'Please run the build process to generate the index.',
83+
)
84+
}
85+
if (error instanceof SyntaxError) {
86+
throw new Error(
87+
`API index contains invalid JSON at ${indexPath}. Please rebuild to regenerate the index file.`,
88+
)
89+
}
90+
throw error
91+
}
92+
}
93+
5594
/**
5695
* Gets all available documentation versions
5796
*

src/utils/icons/fetch.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,17 @@ export async function fetchIconsIndex(
4040
* On Cloudflare, pass assetsFetch so the Worker loads the asset via ASSETS binding.
4141
*
4242
* @param url - The URL object from the API route context
43+
* @param version - Docs version (e.g. "v5")
4344
* @param setId - Icon set id (e.g. "pf")
4445
* @param assetsFetch - Optional; when provided (e.g. locals.runtime.env.ASSETS.fetch on Cloudflare), use it to fetch the asset
4546
*/
4647
export async function fetchIconSvgs(
4748
url: URL,
49+
version: string,
4850
setId: string,
4951
assetsFetch?: AssetsFetcher,
5052
): Promise<Record<string, string> | null> {
51-
const iconsSvgsUrl = new URL(`/iconsSvgs/${setId}.json`, url.origin)
53+
const iconsSvgsUrl = new URL(`/api/${version}/icons/${setId}.json`, url.origin)
5254
const response = assetsFetch
5355
? await assetsFetch(new Request(iconsSvgsUrl))
5456
: await fetch(iconsSvgsUrl)

0 commit comments

Comments
 (0)