Skip to content

Conversation

@wise-king-sullyman
Copy link
Contributor

@wise-king-sullyman wise-king-sullyman commented Dec 5, 2025

Closes #184

Assisted by claude code

Summary by CodeRabbit

  • New Features

    • Added --dry-run deploy flag, a clean script, and an API index endpoint plus runtime fetching of the API index.
  • Bug Fixes

    • Improved API endpoints' error handling with clearer 404/500 responses when the API index is missing or fetch fails.
  • Documentation

    • Clarified that API endpoints aren’t available in dev mode on a clean repo until after a build.
  • Chores

    • Deployment now builds first and copies the API index into the docs output; updated .gitignore.
  • Tests

    • Tests updated to use per-test fetch mocks and more precise assertions.

✏️ Tip: You can customize this high-level summary in your review settings.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Dec 5, 2025

Deploying patternfly-doc-core with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8e9d674
Status: ✅  Deploy successful!
Preview URL: https://19c22c95.patternfly-doc-core.pages.dev
Branch Preview URL: https://fix-text-api-monorepo-issues.patternfly-doc-core.pages.dev

View logs

@cloudflare-workers-and-pages
Copy link

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
patternfly-doc-core 9fd87fb Dec 05 2025, 05:49 PM

@wise-king-sullyman wise-king-sullyman requested review from dlabaj and removed request for dlabaj December 8, 2025 16:36
@wise-king-sullyman
Copy link
Contributor Author

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Walkthrough

This PR moves the API index to a generated file placed in the consumer-configured outputDir at build time, adds a prerendered /apiIndex.json route and a runtime fetch utility, updates API routes to fetch that index with error handling, introduces a safe default-tab resolver for API content, and updates CLI/build steps to generate and copy the index.

Changes

Cohort / File(s) Summary
Ignore & Tooling
/.gitignore, tsconfig.json, jest.config.ts, package.json
Add .wrangler/ to ignore; reformat tsconfig includes; reorder Jest moduleNameMapper; add clean script and adjust deploy script; bump astro spec.
CLI / Build Orchestration
cli/cli.ts
CLI handlers now accept program: Command, add --dry-run, use verbose logging, generate/copy apiIndex.json into docs output, and adjust build/deploy/start wiring.
Prerendered API Index Route
src/pages/apiIndex.json.ts
New prerendered GET route that returns the generated ApiIndex JSON or a 500 JSON error with details.
Runtime Index Fetch Utility
src/utils/apiIndex/fetch.ts
New fetchApiIndex(url: URL): Promise<ApiIndex> that fetches /apiIndex.json from the request origin, throws on non-ok responses, and returns parsed ApiIndex.
Output Dir Resolution
src/utils/getOutputDir.ts
New getOutputDir() reads pf-docs.config.mjs via getConfig, validates presence and outputDir, and returns it or throws descriptive errors.
Index Generation & Write
src/utils/apiIndex/generate.ts, src/utils/apiIndex/get.ts
writeApiIndex now uses getOutputDir(), ensures output dir exists, writes apiIndex.json into configured outputDir; get.ts reads from that outputDir and validates structure with clearer ENOENT/JSON errors.
API Routes — dynamic fetch & error handling
src/pages/api/[version].ts, src/pages/api/[version]/[section].ts, src/pages/api/[version]/[section]/[page].ts, src/pages/api/[version]/[section]/[page]/[tab].ts, src/pages/api/openapi.json.ts, src/pages/api/versions.ts
Replace static JSON imports with fetchApiIndex(url); handler signatures now accept { url }; add try/catch to return 500 on fetch failure and 404 for missing entries; tab resolver switched to getDefaultTabForApi.
Tab resolution utilities & tests
src/utils/packageUtils.ts, src/utils/__tests__/packageUtils.test.ts
Add getDefaultTabForApi(filePath?) export that falls back to 'text'; tests added/expanded to cover edge cases including monorepo/local paths and missing inputs.
API tests updated to runtime mocking
src/__tests__/pages/api/__tests__/[version].test.ts, src/__tests__/pages/api/__tests__/versions.test.ts, src/__tests__/pages/api/__tests__/[version]/[section]/[page]/[tab].test.ts
Replace module-level mocks with per-test global.fetch returning inline mockApiIndex; update assertions (headers, shapes, exact sections); restore mocks after each test; adapt mocks for getDefaultTabForApi.
Misc & Utilities
src/utils/apiIndex/*, other small files
Use getOutputDir, ensure directories exist before writing, improve error messages, and ensure safe default tab handling for API content.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client
participant API_Route as "API Route (/api/...)"
participant FetchUtil as "fetchApiIndex()"
participant ApiIndexRoute as "/apiIndex.json (prerendered)"
participant FileSystem as "Build / outputDir"
Note over FileSystem,ApiIndexRoute: Build-time: generator writes apiIndex.json into configured outputDir
Client->>API_Route: GET /api/versions or /api/:version/...
API_Route->>FetchUtil: fetchApiIndex(request.url)
FetchUtil->>ApiIndexRoute: GET /apiIndex.json (same origin)
ApiIndexRoute-->>FetchUtil: 200 JSON (ApiIndex) or 500 error
FetchUtil-->>API_Route: ApiIndex or throws
API_Route-->>Client: 200 with data OR 404/500 error

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to focus on:
    • cli/cli.ts (build sequencing, verbose/dry-run, file copy)
    • getOutputDir.ts (config loading and error messages)
    • apiIndex generation and writeApiIndex (path resolution, mkdir + write)
    • fetchApiIndex() (non-OK handling, thrown errors)
    • Updated API route signatures ({ url }) and consistent status codes
    • Tests using global.fetch — ensure isolation and proper restoration

Poem

🐰 I hopped through code both near and far,

I placed the index where your projects are,
A fallback tab saved a build from a fall,
CLI learned to copy — I answered the call,
Tiny rabbit, tidy build, big carrot for all! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(API): address issues with text API in monorepos' clearly summarizes the main change addressing API issues in monorepo environments, which aligns with the changeset's primary focus.
Linked Issues check ✅ Passed The PR successfully addresses all objectives from issue #184: apiIndex file placement moved to outputDir for monorepo support, fallback mechanism added for missing tab names via getDefaultTabForApi, and error handling implemented throughout fetch operations.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing API functionality in monorepos. Minor formatting changes (tsconfig.json, jest.config.ts, package.json) and dependency updates are incidental to the main objectives.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-text-api-monorepo-issues

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (9)
.gitignore (1)

37-38: Consider reorganizing the .wrangler/ entry for clarity.

The addition of .wrangler/ is correct and necessary (Cloudflare Workers CLI artifacts should be ignored). However, the placement after the "Ignore generated files" section without a grouping comment is slightly inconsistent with the rest of the file's organization.

For better clarity, consider moving it to the top with other build output directories (near dist/ and .astro/) or adding a brief comment explaining why it's being ignored.

Optional refactor:

# build output
dist/
+.wrangler/
# generated types
.astro/

Alternatively, keep it where it is but add a clarifying comment:

coverage/

+# Cloudflare Workers build artifacts
.wrangler/
src/utils/apiIndex/fetch.ts (1)

1-19: Runtime fetch helper matches new apiIndex.json route; consider tightening typing

The URL construction and error handling look good and align with the new /apiIndex.json route.

To get stronger typing on the parsed payload, you could assert the JSON type:

-  return response.json()
+  const body = (await response.json()) as ApiIndex
+  return body

This keeps the function’s Promise<ApiIndex> contract explicit instead of relying on any from Response.json().

package.json (1)

21-29: CLI-driven deploy and clean script are sensible; watch cross-platform rm

Routing deploy through node ./dist/cli/cli.js deploy after build:cli is a good way to ensure the CLI is built before deployment.

The new clean script:

"clean": "rm -rf dist .astro .wrangler"

is fine on POSIX shells but will fail on Windows. If you need cross‑platform support, consider switching to a Node-based tool (e.g., rimraf) and wiring it in here.

README.mdx (1)

36-36: Dev-mode note is helpful; consider naming the build command explicitly

The note about API endpoints not being available on a clean repo until a build has been done accurately reflects the new build‑time index generation.

Optionally, you could make it a bit clearer by referencing the actual command, e.g., “until npm run build (or your equivalent build script) has been run.”

src/utils/packageUtils.ts (1)

64-65: Explicit 'text' fallback for API routes is clear and minimal

  • The simplification of getDefaultTab to:

    return addDemosOrDeprecated(tabBase, filePath)

    is a no-op behaviorally and improves readability.

  • The new API-specific helper:

    export const getDefaultTabForApi = (filePath?: string): string =>
      getDefaultTab(filePath) || 'text'

    cleanly defines 'text' as the fallback when package-based tab inference fails (e.g., for local, non–node_modules content), which directly addresses the “no tab name can be determined” scenario from the issue.

If you expect 'text' to be a long-term, public default, you might eventually want to centralize it as a named constant, but the current implementation is perfectly fine.

Also applies to: 67-69

src/pages/apiIndex.json.ts (1)

1-27: Build-time apiIndex.json route is wired correctly; consider clearer error details

The prerendered /apiIndex.json endpoint that calls getApiIndex() is a good fit with the new fetchApiIndex(url) helper and keeps Cloudflare Worker runtime lean.

In the error branch you currently return:

JSON.stringify({ error: 'Failed to load API index', details: error })

Serializing an Error object usually yields {}; if you want meaningful diagnostics, you could optionally map it to a string:

} catch (error) {
  const details = error instanceof Error ? error.message : String(error)
  return new Response(
    JSON.stringify({ error: 'Failed to load API index', details }),
    { status: 500, headers: { 'Content-Type': 'application/json' } }
  )
}

(Or omit details entirely if you’d rather not expose internal messages.)

src/__tests__/pages/api/__tests__/versions.test.ts (1)

3-66: Fetch-based versions API tests look correct; consider centralizing the fetch mock

The tests correctly adapt to the new fetchApiIndex(url) behavior by mocking global.fetch and passing a real URL into GET, and they assert status, content type, and body shape appropriately.

For readability and to avoid repetition, you could move the global.fetch setup and jest.restoreAllMocks() into beforeEach/afterEach (or a small helper) and optionally add one test that simulates a non‑OK fetch response to exercise the 500 error path.

src/__tests__/pages/api/__tests__/[version]/[section]/[page]/[tab].test.ts (1)

78-132: Test mock for getDefaultTabForApi diverges from real fallback behavior

The mocked getDefaultTabForApi here falls back to 'react' when it can’t infer from the path, whereas the real implementation falls back to 'text'. For the current tests (which always use explicit react/html/react-demos tabs), this doesn’t change the outcome, but aligning the mock’s fallback with the real function would make these tests more representative and future‑proof if you later add scenarios that rely on the fallback.

src/utils/apiIndex/get.ts (1)

74-78: Consider hoisting the dynamic import.

The createIndexKey dynamic import inside getPages and getTabs (line 91) is executed on every call. If there's no circular dependency concern, moving this to a top-level import would be cleaner:

+import { createIndexKey } from '../apiHelpers'
 
 // Then in getPages and getTabs:
-  const { createIndexKey } = await import('../apiHelpers')
   const key = createIndexKey(version, section)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e10053 and fc8173c.

📒 Files selected for processing (22)
  • .gitignore (1 hunks)
  • README.mdx (1 hunks)
  • cli/cli.ts (7 hunks)
  • jest.config.ts (1 hunks)
  • package.json (1 hunks)
  • src/__tests__/pages/api/__tests__/[version].test.ts (2 hunks)
  • src/__tests__/pages/api/__tests__/[version]/[section]/[page]/[tab].test.ts (1 hunks)
  • src/__tests__/pages/api/__tests__/versions.test.ts (1 hunks)
  • src/pages/api/[version].ts (2 hunks)
  • src/pages/api/[version]/[section].ts (2 hunks)
  • src/pages/api/[version]/[section]/[page].ts (2 hunks)
  • src/pages/api/[version]/[section]/[page]/[tab].ts (3 hunks)
  • src/pages/api/openapi.json.ts (1 hunks)
  • src/pages/api/versions.ts (1 hunks)
  • src/pages/apiIndex.json.ts (1 hunks)
  • src/utils/__tests__/packageUtils.test.ts (2 hunks)
  • src/utils/apiIndex/fetch.ts (1 hunks)
  • src/utils/apiIndex/generate.ts (3 hunks)
  • src/utils/apiIndex/get.ts (2 hunks)
  • src/utils/getOutputDir.ts (1 hunks)
  • src/utils/packageUtils.ts (1 hunks)
  • tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (13)
src/utils/__tests__/packageUtils.test.ts (1)
src/utils/packageUtils.ts (1)
  • getDefaultTabForApi (69-69)
src/utils/apiIndex/fetch.ts (1)
src/utils/apiIndex/generate.ts (1)
  • ApiIndex (37-46)
src/utils/getOutputDir.ts (1)
cli/getConfig.ts (1)
  • getConfig (22-36)
src/pages/apiIndex.json.ts (2)
src/pages/api/openapi.json.ts (2)
  • prerender (5-5)
  • GET (7-397)
src/pages/api/versions.ts (2)
  • prerender (5-5)
  • GET (7-17)
src/utils/apiIndex/generate.ts (3)
src/utils/packageUtils.ts (1)
  • getDefaultTabForApi (69-69)
src/utils/apiIndex/index.ts (2)
  • writeApiIndex (3-3)
  • ApiIndex (5-5)
src/utils/getOutputDir.ts (1)
  • getOutputDir (4-19)
src/pages/api/[version]/[section]/[page].ts (3)
src/pages/api/index.ts (1)
  • GET (6-172)
src/utils/apiIndex/fetch.ts (1)
  • fetchApiIndex (10-19)
src/utils/apiHelpers.ts (2)
  • createIndexKey (48-50)
  • createJsonResponse (18-27)
src/__tests__/pages/api/__tests__/[version].test.ts (2)
src/pages/api/[version].ts (1)
  • GET (7-32)
src/pages/api/versions.ts (1)
  • GET (7-17)
src/utils/apiIndex/get.ts (1)
src/utils/getOutputDir.ts (1)
  • getOutputDir (4-19)
src/pages/api/[version]/[section]/[page]/[tab].ts (1)
src/utils/packageUtils.ts (1)
  • getDefaultTabForApi (69-69)
src/pages/api/openapi.json.ts (2)
src/utils/apiIndex/fetch.ts (1)
  • fetchApiIndex (10-19)
src/utils/apiHelpers.ts (1)
  • createJsonResponse (18-27)
src/__tests__/pages/api/__tests__/versions.test.ts (1)
src/pages/api/versions.ts (1)
  • GET (7-17)
cli/cli.ts (4)
cli/fileExists.ts (1)
  • fileExists (3-7)
cli/getConfig.ts (1)
  • DocsConfig (15-20)
pf-docs.config.mjs (2)
  • config (1-33)
  • config (1-33)
cli/templates/pf-docs.config.mjs (2)
  • config (1-32)
  • config (1-32)
src/pages/api/versions.ts (3)
src/pages/apiIndex.json.ts (2)
  • prerender (5-5)
  • GET (7-27)
src/utils/apiIndex/fetch.ts (1)
  • fetchApiIndex (10-19)
src/utils/apiHelpers.ts (1)
  • createJsonResponse (18-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (13)
jest.config.ts (1)

19-23: outputDir Jest alias looks correct and ordering is sane

The new moduleNameMapper for '^outputDir/(.*)$' -> '<rootDir>/dist/$1' is consistent with using outputDir/* as a dist alias and is placed before the generic (.+)\\.js matcher, so it won’t be shadowed.

Once the tsconfig paths entry is made relative to dist/*, this should keep TypeScript and Jest resolution in sync.

src/pages/api/[version]/[section]/[page]/[tab].ts (1)

9-10: Fallback tab handling and tabless-path filtering look robust

  • Importing and using getDefaultTabForApi for the data.tab default:

    tab: data.tab || data.source || getDefaultTabForApi(filePath),

    ensures that API content always has a tab value, falling back to 'text' when package-based logic doesn’t apply (e.g., local files in a consuming repo).

  • The additional safety pass over paths:

    paths.forEach((path) => {
      if (!path.params.tab) {
        console.warn(`[API Warning] Tab not found for path: ...`)
      }
    })
    
    return paths.filter((path) => !!path.params.tab)

    guards against any tabless entries that might slip through index generation, preventing build-time crashes while still surfacing a clear warning.

This aligns well with the goal of handling tabless edge cases gracefully without breaking prerendering.

Also applies to: 47-52, 55-55, 120-120

src/pages/api/[version].ts (1)

2-31: Dynamic API index fetch and version-specific error handling look solid

The handler cleanly validates version, fetches the index with fetchApiIndex(url), differentiates between missing param (400), unknown version (404), and underlying fetch failures (500), and uses createJsonResponse consistently. This aligns well with the updated tests and the new index-fetching utility.

src/pages/api/versions.ts (1)

3-16: /api/versions now correctly delegates to fetchApiIndex with robust error handling

The route cleanly switches from static data to fetchApiIndex(url), returning index.versions on success and a structured 500 response on failure. This is consistent with the helper utilities and the updated tests mocking global.fetch.

src/pages/api/[version]/[section]/[page].ts (1)

3-37: Page-level route correctly uses the shared index and distinguishes 400/404/500 paths

The updated handler validates version/section/page, fetches the shared index via fetchApiIndex(url), uses createIndexKey to look up tabs, and returns clear 404 and 500 JSON responses in failure cases. This matches the generator’s keying strategy and the tests that assert not-found behavior.

src/utils/getOutputDir.ts (1)

1-19: getOutputDir cleanly validates configuration and surfaces helpful errors

The new helper correctly reuses getConfig, distinguishes between “no config file” and “no outputDir in config”, and returns the configured outputDir string. This is a good central point for monorepo-aware path resolution and will make failures much easier to diagnose.

src/utils/__tests__/packageUtils.test.ts (1)

1-261: Comprehensive tests for getDefaultTabForApi accurately capture primary and fallback behavior

The new test suite thoroughly exercises getDefaultTabForApi, covering standard PatternFly/react-core paths as well as all the fallback cases where getDefaultTab returns an empty string. This matches the implementation’s || 'text' behavior and should prevent regressions in default tab resolution for the API.

src/utils/apiIndex/get.ts (1)

13-26: LGTM - Dynamic output directory resolution with proper validation.

The structure validation ensures the index file has the expected format before use, and the error handling provides clear guidance for common failure modes (missing file, invalid JSON).

src/utils/apiIndex/generate.ts (2)

110-113: Good fallback implementation for tab resolution.

Using getDefaultTabForApi ensures a 'text' fallback when neither tab nor source is available, directly addressing the PR objective to prevent build crashes when no tab name can be determined.


145-151: Write failure is logged as warning but doesn't propagate.

The catch block logs a warning and continues silently. If the API index file fails to write, downstream processes that depend on it will fail with a less clear error. Consider whether this should throw or at least return a failure indicator.

   } catch (error) {
-    console.warn('Warning: Could not write API index file:', error)
+    console.error('Error: Could not write API index file:', error)
+    throw error
   }

If silent failure is intentional for resilience, a comment explaining why would help future maintainers.

cli/cli.ts (3)

166-173: LGTM - API index copy to docs directory.

Copying the generated apiIndex.json into the docs output directory ensures it's available as a static asset for the runtime fetchApiIndex calls. This correctly bridges the build-time generation with runtime consumption.


232-241: Start command flow looks correct.

The sequence of updateTsConfigOutputDirPathupdateContentinitializeApiIndexgeneratePropsdev properly sets up the environment before starting the dev server.


190-198: No changes needed. The wrangler.jsonc file is already configured with pages_build_output_dir: "dist/docs", which matches the output location where the build function places the compiled documentation. The wrangler pages deploy command will correctly use this configuration, so an explicit directory argument is unnecessary.

tsconfig.json Outdated
Comment on lines 11 to 24
"paths": {
"outputDir/*": [
"/Users/ausulliv/repos/patternfly-doc-core/dist/*"
]
}
},
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}
"include": [
".astro/types.d.ts",
"**/*"
],
"exclude": [
"dist"
]
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Avoid committing machine-specific absolute paths in compilerOptions.paths

"outputDir/*" currently maps to "/Users/ausulliv/repos/patternfly-doc-core/dist/*", which will only work on the original author’s machine and will break in CI and for other contributors.

You likely want a repo‑relative alias that matches the Jest moduleNameMapper ('<rootDir>/dist/$1') instead:

-    "paths": {
-      "outputDir/*": [
-        "/Users/ausulliv/repos/patternfly-doc-core/dist/*"
-      ]
-    }
+    "paths": {
+      "outputDir/*": [
+        "dist/*"
+      ]
+    }

This keeps the alias portable and consistent with the runtime/test resolution.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"paths": {
"outputDir/*": [
"/Users/ausulliv/repos/patternfly-doc-core/dist/*"
]
}
},
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}
"include": [
".astro/types.d.ts",
"**/*"
],
"exclude": [
"dist"
]
}
"paths": {
"outputDir/*": [
"dist/*"
]
}
},
"include": [
".astro/types.d.ts",
"**/*"
],
"exclude": [
"dist"
]
}
🤖 Prompt for AI Agents
In tsconfig.json around lines 11–24, the compilerOptions.paths entry uses an
absolute, machine-specific path ("/Users/ausulliv/.../dist/*"); replace that
with a repo-relative path so the alias works for CI and other
contributors—change the mapping to point at the repository's dist folder (e.g.
"./dist/*" or "dist/*") so it matches your Jest moduleNameMapper
('<rootDir>/dist/$1') and is portable across machines.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cli/cli.ts (2)

115-153: API index population is missing from the build flow.

The buildProject function initializes an API index template (line 133) but never populates it with actual data before copying it to the docs directory (line 146). The generateAndWriteApiIndex function exists in src/utils/apiIndex/generate.ts but is not called during the build process.

The current flow copies only the template file (cli/templates/apiIndex.json) to the docs directory without generating actual API data. To serve a functional API index as a static asset, add a call to generateAndWriteApiIndex() between transformMDContentToMDX() and the build() call, or ensure the build process populates the index during content processing.


155-181: Add validation or documentation for Wrangler configuration before deployment.

The wrangler pages deploy command at line 171 requires wrangler.toml to be configured in the consumer's project, but:

  1. The setup and init commands don't create or configure wrangler.toml
  2. The README doesn't document that Wrangler setup is required
  3. The code doesn't validate that wrangler.toml exists before attempting deployment

Either:

  • Add validation to check if wrangler.toml exists and provide a helpful error message, or
  • Document the Wrangler setup requirement in the README with example configuration, or
  • Modify the deploy command to accept and pass an explicit output directory to wrangler pages deploy
🧹 Nitpick comments (6)
src/pages/api/[version]/[section].ts (1)

17-36: Error serialization properly addressed; consider caching for performance.

The error handling on line 31 correctly serializes the error object, resolving the issue flagged in the past review comment. The logic flow is solid: fetch → lookup → return 404 if missing → return pages on success → catch errors with proper serialization.

However, fetching the API index on every request could impact performance in high-traffic scenarios. Consider adding a caching layer if this becomes a bottleneck.

Example caching approach using Astro's Astro.locals or a simple in-memory cache:

// At module level
let cachedIndex: { data: ApiIndex; timestamp: number } | null = null
const CACHE_TTL = 60000 // 1 minute

export const GET: APIRoute = async ({ params, url }) => {
  const { version, section } = params

  if (!version || !section) {
    return createJsonResponse(
      { error: 'Version and section parameters are required' },
      400,
    )
  }

  try {
    const now = Date.now()
    if (!cachedIndex || now - cachedIndex.timestamp > CACHE_TTL) {
      cachedIndex = { data: await fetchApiIndex(url), timestamp: now }
    }
    const index = cachedIndex.data
    const key = createIndexKey(version, section)
    const pages = index.pages[key]

    if (!pages) {
      return createJsonResponse(
        { error: `Section '${section}' not found for version '${version}'` },
        404,
      )
    }

    return createJsonResponse(pages)
  } catch (error) {
    const details = error instanceof Error ? error.message : String(error)
    return createJsonResponse(
      { error: 'Failed to load API index', details },
      500
    )
  }
}
src/__tests__/pages/api/__tests__/[version].test.ts (3)

3-11: Type the mockApiIndex fixture to stay aligned with the real index schema

Consider annotating mockApiIndex with the shared API index type (e.g., ApiIndex from your apiIndex utilities) so test data stays in lockstep with the runtime shape and any schema drift is caught at compile time.


140-160: Simplify redundant expectations in the final test

The checks

expect(body).toEqual(mockApiIndex.sections.v6)
expect(body).toEqual(['components', 'layouts', 'utilities'])

are effectively asserting the same thing. You can keep either the typed/fixture-based check or the explicit literal array, but you don’t need both.


14-20: Factor out fetch mocking and use jest.spyOn(global, 'fetch') with beforeEach/afterEach to ensure proper cleanup

Each test directly assigns global.fetch = jest.fn(...) then calls jest.restoreAllMocks(), but direct property assignments are not restored by restoreAllMocks()—only mocks created via jest.spyOn() or jest.replaceProperty() are reverted. This pattern repeats across all six tests (lines 14–20, 38–44, 58–64, 78–84, 98–104, 120–126, 141–147).

Refactor to use jest.spyOn(global, 'fetch').mockResolvedValue(...) in a shared beforeEach, with a single afterEach(() => jest.restoreAllMocks()). This both eliminates duplication and ensures the original fetch is properly restored between tests.

cli/cli.ts (2)

15-15: Remove unused imports: readFile and writeFile.

These imports from fs/promises are not used anywhere in the file. Only copyFile is actually utilized.

Apply this diff:

-import { mkdir, copyFile, readFile, writeFile } from 'fs/promises'
+import { mkdir, copyFile } from 'fs/promises'

188-188: Consider extending dry-run support to build command.

The --dry-run flag is currently only used by the deploy command. Consider whether the build command should also respect this flag to enable a complete dry-run workflow that validates the entire build and deployment process without side effects.

If deploy-only dry-run is intentional, this can be safely ignored.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc8173c and cd5c2a0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • .gitignore (1 hunks)
  • cli/cli.ts (8 hunks)
  • jest.config.ts (1 hunks)
  • package.json (2 hunks)
  • src/__tests__/pages/api/__tests__/[version].test.ts (2 hunks)
  • src/pages/api/[version]/[section].ts (2 hunks)
  • src/pages/api/openapi.json.ts (1 hunks)
  • tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • .gitignore
  • src/pages/api/openapi.json.ts
🧰 Additional context used
🧬 Code graph analysis (3)
src/__tests__/pages/api/__tests__/[version].test.ts (2)
src/pages/api/versions.ts (1)
  • GET (7-17)
src/pages/api/[version].ts (1)
  • GET (7-32)
cli/cli.ts (2)
cli/fileExists.ts (1)
  • fileExists (3-7)
cli/getConfig.ts (1)
  • DocsConfig (15-20)
src/pages/api/[version]/[section].ts (4)
src/pages/apiIndex.json.ts (1)
  • GET (7-27)
src/pages/api/[version].ts (1)
  • GET (7-32)
src/utils/apiIndex/fetch.ts (1)
  • fetchApiIndex (10-19)
src/utils/apiHelpers.ts (2)
  • createIndexKey (48-50)
  • createJsonResponse (18-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (22.x)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (10)
src/pages/api/[version]/[section].ts (3)

1-3: LGTM! Imports are clean and support the runtime fetch pattern.

The addition of fetchApiIndex aligns with the PR objective to move from static import to runtime-fetched API index for monorepo support.


5-5: Correct prerender setting for runtime fetch.

Setting prerender = false is appropriate here since the route fetches the API index at request time.


7-15: LGTM! Parameter validation is comprehensive.

The handler correctly validates both version and section parameters and returns a clear error message when they're missing. The addition of the url parameter enables the runtime fetch pattern.

src/__tests__/pages/api/__tests__/[version].test.ts (1)

23-23: URL wiring for the route handler looks correct

Passing a concrete URL instance that matches the requested path into GET mirrors Astro’s APIRoute context well and keeps these tests close to real runtime behavior.

Also applies to: 47-47, 67-67, 87-87, 107-107, 129-129, 150-150

jest.config.ts (1)

21-21: LGTM: Standard ESM compatibility mapping.

This mapping correctly handles TypeScript files imported with .js extensions (as required by ESM standards), allowing Jest to resolve them to the actual .ts files during testing.

package.json (3)

21-21: LGTM: Deploy script now uses programmatic CLI.

The updated deploy script correctly invokes the CLI's deploy command, aligning with the broader refactoring to support monorepo workflows.


28-29: LGTM: Clean script addition.

The new clean script provides a convenient way to remove build artifacts and the trailing comma maintains JSON formatting consistency.


68-68: Verify caret range compatibility with Astro v5 breaking changes.

Changing from an exact version (5.15.9) to a caret range (^5.15.9) allows automatic minor and patch updates. Given the significant breaking changes in Astro v5 (removed experimental flags, Content Layer migration, changed rendering modes, etc.), minor version updates could introduce incompatibilities.

Consider whether a tilde range (~5.15.9) limiting updates to patches only would be safer, or if the caret range has been tested with expected minor version updates.

Based on library documentation for Astro v5 breaking changes.

cli/cli.ts (2)

91-113: LGTM: API index initialization now supports monorepos.

This function correctly writes the API index template to the consumer's outputDir instead of the package directory, addressing the primary issue described in #184. The conditional verbose logging and early return when the file exists improve the developer experience.


209-246: LGTM: Command handlers correctly updated.

All command handlers have been properly updated to pass the program parameter to refactored functions, maintaining consistency across the CLI interface.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cli/cli.ts (1)

44-46: Add error handling for critical directory creation.

The output directory creation at module initialization lacks error handling. If this fails (e.g., due to permission issues), the entire CLI will crash with an unhandled promise rejection before any commands can execute.

Apply this diff to add error handling:

 const absoluteOutputDir = resolve(currentDir, config.outputDir)
 
-await mkdir(absoluteOutputDir, { recursive: true })
+try {
+  await mkdir(absoluteOutputDir, { recursive: true })
+} catch (e: any) {
+  console.error(`Failed to create output directory ${absoluteOutputDir}:`, e.message)
+  process.exit(1)
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd5c2a0 and 062ae90.

📒 Files selected for processing (1)
  • cli/cli.ts (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
cli/cli.ts (2)
cli/fileExists.ts (1)
  • fileExists (3-7)
cli/getConfig.ts (1)
  • DocsConfig (15-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (22.x)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (3)
cli/cli.ts (3)

91-113: LGTM! API index now correctly targets consumer's output directory.

The changes correctly address the monorepo issue by placing the API index in the consumer-configured outputDir rather than the package directory. The early return optimization and verbose logging are appropriate.


188-188: LGTM! CLI command wiring is consistent and correct.

The addition of the --dry-run option and the consistent updates to pass the program object to all command handlers ensure proper option handling throughout the CLI. The start command now properly initializes the API index for the dev server.

Also applies to: 209-211, 221-221, 246-246


136-141: No action required. The Astro v5 build configuration is correct.

The outDir parameter in the build() call is standard Astro API usage and works properly with Astro v5. The astro.config.mjs contains no conflicting build.outDir, build.client, or build.server configurations. Static assets and routes resolve correctly with this setup.

Likely an incorrect or invalid review comment.

Comment on lines 167 to +174
try {
// First build the project
const config = await buildProject()
if (config) {
if (verbose) {
console.log('Build complete, deploying to Cloudflare...')
}

// Deploy using Wrangler
const { execSync } = await import('child_process')
const outputPath = join(absoluteOutputDir, 'docs')

execSync(`npx wrangler pages deploy ${outputPath}`, {
stdio: 'inherit',
cwd: currentDir,
})

console.log('Successfully deployed to Cloudflare Pages!')
}
// Deploy using Wrangler
const { execSync } = await import('child_process')

execSync(`wrangler pages deploy`, {
stdio: 'inherit',
cwd: currentDir,
})
Copy link

@coderabbitai coderabbitai bot Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check for wrangler.toml files and examine the deploy function in detail
echo "=== Finding wrangler.toml files ==="
fd -t f "wrangler.toml"

echo -e "\n=== Checking cli/cli.ts deploy function context ==="
# Read the deploy function in full (lines 155-181 as mentioned in comment)
if [ -f "cli/cli.ts" ]; then
  sed -n '155,181p' cli/cli.ts
else
  echo "cli/cli.ts not found, searching for it"
  fd -t f "cli.ts"
fi

Repository: patternfly/patternfly-doc-core

Length of output: 761


🏁 Script executed:

echo "=== Searching for currentDir definition in cli/cli.ts ==="
rg "currentDir" cli/cli.ts -B 2 -A 2

echo -e "\n=== Checking full deploy function and surrounding context ==="
wc -l cli/cli.ts

echo -e "\n=== Search for where deploy() is called ==="
rg "deploy\(" cli/cli.ts -B 3 -A 3

echo -e "\n=== Check if outputDir is used in deploy or related functions ==="
rg "outputDir" cli/cli.ts -B 1 -A 1

Repository: patternfly/patternfly-doc-core

Length of output: 1997


🏁 Script executed:

echo "=== Search for README or deployment documentation ==="
fd -t f -i "readme" | head -5

echo -e "\n=== Check for any comments/docs around deploy function ==="
sed -n '145,180p' cli/cli.ts

echo -e "\n=== Search for test files related to deploy ==="
fd -t f "test\|spec" | grep -i deploy

echo -e "\n=== Check if there are any example configs or docs ==="
fd -t f "example\|sample\|template" | head -10

echo -e "\n=== Search for wrangler usage in comments or docs ==="
rg "wrangler" --type md --type txt

Repository: patternfly/patternfly-doc-core

Length of output: 1180


Pass the build output directory explicitly to Wrangler.

The deployment command executes wrangler pages deploy without specifying which directory to deploy. Since docs are built to absoluteOutputDir (derived from config.outputDir), but this path is not passed to the deploy function, the deploy command needs to be updated to:

wrangler pages deploy ${absoluteOutputDir}

Or the deploy() function should receive absoluteOutputDir as a parameter and pass it to the wrangler command. Without this, Wrangler may deploy from the wrong directory or fail if the consumer's wrangler.toml is not correctly configured.

🤖 Prompt for AI Agents
In cli/cli.ts around lines 167-174, the wrangler deploy call does not specify
the build output directory; update the execSync invocation to pass the resolved
absoluteOutputDir to Wrangler (e.g., run "wrangler pages deploy
<absoluteOutputDir>") or change the surrounding deploy() function to accept
absoluteOutputDir and forward it to execSync; ensure you use the
already-imported execSync, pass cwd appropriately or quote the path to handle
spaces, and validate absoluteOutputDir is resolved before calling execSync.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intended, we want the deploy dir to come from the wrangler config file.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wise-king-sullyman, understood! Using the wrangler.toml configuration for the deployment directory is a valid approach and keeps the deployment configuration centralized. Thank you for clarifying the intent.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cli/cli.ts (1)

110-112: Consider re-throwing or exiting on initialization failure.

The catch block logs the error but doesn't propagate it. If the template copy fails during initialization (e.g., missing template file, permission issues), the build will continue without the API index, potentially causing runtime errors later. This differs from the error handling in buildProject (line 154) which re-throws.

   } catch (e: any) {
     console.error('Error copying apiIndex.json template:', e)
+    throw e
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 062ae90 and 8e9d674.

📒 Files selected for processing (3)
  • README.mdx (1 hunks)
  • cli/cli.ts (7 hunks)
  • src/utils/apiIndex/fetch.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/utils/apiIndex/fetch.ts
  • README.mdx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (22.x)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (7)
cli/cli.ts (7)

37-37: LGTM!

Using console.log for this informational message about fallback behavior is appropriate since it's not an error condition.


143-155: LGTM!

The error handling for the apiIndex.json copy operation is properly implemented with try/catch, error logging, and re-throwing to fail the build on error. This addresses the previously identified concern.


167-170: Nice addition of dry-run mode.

The --dry-run flag implementation is clean and allows users to test the deployment flow without actually deploying. This is helpful for CI/CD testing scenarios.


191-193: LGTM!

The CLI options are well-defined with appropriate defaults. The --dry-run flag follows standard CLI conventions.


214-223: LGTM!

The start command properly initializes the API index with the program context, ensuring verbose logging works consistently across all commands.


225-227: LGTM!

The build command properly delegates to buildProject with the program context.


250-252: Verify: Deploy command doesn't trigger a build.

The deploy command only runs the deployment without first building the project. Users must run build separately before deploy. If this is intentional for flexibility, consider adding a check or warning if the docs directory doesn't exist to provide clearer feedback.

@wise-king-sullyman wise-king-sullyman merged commit 14af6cb into main Dec 11, 2025
5 checks passed
@github-actions
Copy link

🎉 This PR is included in version 1.15.5 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Text content API doesn't properly support monorepos, errors out during build if no tab name can be determined

3 participants