Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/pages/api/[version]/[section]/[page]/props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { APIRoute } from 'astro'
import { createJsonResponse } from '../../../../../utils/apiHelpers'
import { getConfig } from '../../../../../../cli/getConfig'
import { join } from 'node:path'
import { readFileSync } from 'node:fs'
import { sentenceCase } from '../../../../../utils/case'

export const prerender = false

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

if (!version || !section || !page) {
return createJsonResponse(
{ error: 'Version, section, and page parameters are required' },
400,
)
}
Comment on lines +10 to +18
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check the full props.ts file to understand the complete logic
cat -n src/pages/api/[version]/[section]/[page]/props.ts

Repository: patternfly/patternfly-doc-core

Length of output: 1775


🏁 Script executed:

# Check if props.json exists and its structure
find . -name 'props.json' -type f 2>/dev/null | head -10

Repository: patternfly/patternfly-doc-core

Length of output: 56


🏁 Script executed:

# Check the names.ts file to understand parameter usage pattern
if [ -f 'src/pages/api/[version]/[section]/names.ts' ]; then
  cat -n src/pages/api/[version]/[section]/names.ts
else
  echo "names.ts not found at expected location"
fi

Repository: patternfly/patternfly-doc-core

Length of output: 1595


🏁 Script executed:

# Search for any usage of version or section parameters in API route files
rg "version|section" src/pages/api --type ts -A 3 -B 1 | head -100

Repository: patternfly/patternfly-doc-core

Length of output: 5826


version and section parameters are validated but never used in the handler logic.

The handler validates that version, section, and page are all present (lines 13–17), but only page is used to retrieve props data at line 28. The props.json file is loaded as a single file without version/section scoping. Either document this intentional design (the parameters serve only for routing), or refactor the lookup to incorporate version and section for proper multi-version support. Note that names.ts follows the same pattern.

🤖 Prompt for AI Agents
In `@src/pages/api/`[version]/[section]/[page]/props.ts around lines 10 - 18, The
handler currently validates params in GET ({ params }) but never uses version or
section when loading props.json; update the handler to incorporate version and
section into the props lookup path (e.g., build a scoped path using version and
section along with page when reading props.json) so multi-version/section
content is supported, and keep the existing parameter validation and error
response via createJsonResponse; alternatively, if the routing-only intent is
desired, add a clear comment above GET explaining that version and section are
validated for routing but intentionally not used for props resolution (mirror
the same choice in names.ts).


try {
const config = await getConfig(`${process.cwd()}/pf-docs.config.mjs`)
const outputDir = config?.outputDir || join(process.cwd(), 'dist')

const propsFilePath = join(outputDir, 'props.json')
const propsDataFile = readFileSync(propsFilePath)
const props = JSON.parse(propsDataFile.toString())

const propsData = props[sentenceCase(page)]

if(propsData === undefined) {
return createJsonResponse(
{ error: `Props data for ${page} not found` },
404,
)
}

return createJsonResponse(propsData)

} catch (error) {
const details = error instanceof Error ? error.message : String(error)
return createJsonResponse(
{ error: 'Props data not found', details },
500,
)
}
}



41 changes: 41 additions & 0 deletions src/pages/api/[version]/[section]/names.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { APIRoute } from 'astro'
import { createJsonResponse } from '../../../../utils/apiHelpers'
import { getConfig } from '../../../../../cli/getConfig'
import { join } from 'node:path'
import { readFileSync } from 'node:fs'

export const prerender = false

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

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

try {
const config = await getConfig(`${process.cwd()}/pf-docs.config.mjs`)
const outputDir = config?.outputDir || join(process.cwd(), 'dist')

const propsFilePath = join(outputDir, 'props.json')
const propsDataFile = readFileSync(propsFilePath)
const props = JSON.parse(propsDataFile.toString())

const propsKey = new RegExp("Props", 'i'); // ignore ComponentProps objects
const names = Object.keys(props).filter(name => !propsKey.test(name))

return createJsonResponse(names)
} catch (error) {
const details = error instanceof Error ? error.message : String(error)
return createJsonResponse(
{ error: 'Component names data not found', details },
500,
)
}
}



70 changes: 70 additions & 0 deletions src/pages/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,36 @@ export const GET: APIRoute = async () =>
example: ['alert', 'button', 'card'],
},
},
{
path: '/api/{version}/{section}/names',
method: 'GET',
description: 'Get component names that have props data',
parameters: [
{
name: 'version',
in: 'path',
required: true,
type: 'string',
example: 'v6',
},
{
name: 'section',
in: 'path',
required: true,
type: 'string',
example: 'components',
}
],
returns: {
type: 'array',
items: 'string',
description: 'All component names with props data',
example: [
'Alert',
'AlertGroup'
],
},
},
{
path: '/api/{version}/{section}/{page}',
method: 'GET',
Expand Down Expand Up @@ -132,6 +162,46 @@ export const GET: APIRoute = async () =>
example: ['react', 'react-demos', 'html'],
},
},
{
path: '/api/{version}/{section}/{page}/props',
method: 'GET',
description: 'Get props for a specific component',
parameters: [
{
name: 'version',
in: 'path',
required: true,
type: 'string',
example: 'v6',
},
{
name: 'section',
in: 'path',
required: true,
type: 'string',
example: 'components',
},
{
name: 'page',
in: 'path',
required: true,
type: 'string',
example: 'alert',
},
],
returns: {
type: 'array',
items: 'object',
description: 'Props for a specific component',
example: [
{
name: 'actionClose',
type: 'React.ReactNode',
description: 'Close button; use the alert action close button component.',
},
],
},
},
{
path: '/api/{version}/{section}/{page}/{tab}',
method: 'GET',
Expand Down
173 changes: 167 additions & 6 deletions src/pages/api/openapi.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const GET: APIRoute = async ({ url }) => {
const details = error instanceof Error ? error.message : String(error)
return createJsonResponse(
{ error: 'Failed to load API index', details },
500
500,
)
}

Expand Down Expand Up @@ -103,7 +103,8 @@ export const GET: APIRoute = async ({ url }) => {
'/openapi.json': {
get: {
summary: 'Get OpenAPI specification',
description: 'Returns the complete OpenAPI 3.0 specification for this API',
description:
'Returns the complete OpenAPI 3.0 specification for this API',
operationId: 'getOpenApiSpec',
responses: {
'200': {
Expand Down Expand Up @@ -234,6 +235,70 @@ export const GET: APIRoute = async ({ url }) => {
},
},
},
'/{version}/{section}/names': {
get: {
summary: 'Get component names',
description: 'Returns the component names that have props data',
operationId: 'getNames',
parameters: [
{
name: 'version',
in: 'path',
required: true,
description: 'Documentation version',
schema: {
type: 'string',
enum: versions,
},
example: 'v6',
},
{
name: 'section',
in: 'path',
required: true,
description: 'Documentation section',
schema: {
type: 'string',
},
example: 'components',
},
],
responses: {
'200': {
description: 'Component names with props data',
content: {
'application/json': {
schema: {
type: 'array',
items: {
type: 'string'
},
},
example: [
'Alert',
'AlertGroup'
],
},
},
},
'404': {
description: 'Props not found',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: {
type: 'string',
},
},
},
},
},
},
},
},
},
'/{version}/{section}/{page}': {
get: {
summary: 'List tabs for a page',
Expand Down Expand Up @@ -306,6 +371,102 @@ export const GET: APIRoute = async ({ url }) => {
},
},
},
'/{version}/{section}/{page}/props': {
get: {
summary: 'Get component props',
description: 'Returns the props for the specified component',
operationId: 'getProps',
parameters: [
{
name: 'version',
in: 'path',
required: true,
description: 'Documentation version',
schema: {
type: 'string',
enum: versions,
},
example: 'v6',
},
{
name: 'section',
in: 'path',
required: true,
description: 'Documentation section',
schema: {
type: 'string',
},
example: 'components',
},
{
name: 'page',
in: 'path',
required: true,
description: 'Page ID (kebab-cased)',
schema: {
type: 'string',
},
example: 'alert',
},
],
responses: {
'200': {
description: 'Props for the specified component',
content: {
'application/json': {
schema: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
type: { type: 'string' },
description: { type: 'string' },
defaultValue: { type: 'string' },
},
},
},
example: [
{
name: 'actionClose',
type: 'React.ReactNode',
description:
'Close button; use the alert action close button component.',
},
{
name: 'actionLinks',
type: 'React.ReactNode',
description:
'Action links; use a single alert action link component or multiple wrapped in an array\nor React fragment.',
},
{
name: 'children',
type: 'React.ReactNode',
description: 'Content rendered inside the alert.',
defaultValue: "''",
},
],
},
},
},
'404': {
description: 'Props not found',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: {
type: 'string',
},
},
},
},
},
},
},
},
},
'/{version}/{section}/{page}/{tab}': {
get: {
summary: 'Validate and redirect to text endpoint',
Expand Down Expand Up @@ -357,7 +518,8 @@ export const GET: APIRoute = async ({ url }) => {
],
responses: {
'302': {
description: 'Redirects to /{version}/{section}/{page}/{tab}/text',
description:
'Redirects to /{version}/{section}/{page}/{tab}/text',
},
'404': {
description: 'Tab not found',
Expand Down Expand Up @@ -554,8 +716,7 @@ export const GET: APIRoute = async ({ url }) => {
'/{version}/{section}/{page}/{tab}/examples/{example}': {
get: {
summary: 'Get example code',
description:
'Returns the raw source code for a specific example',
description: 'Returns the raw source code for a specific example',
operationId: 'getExampleCode',
parameters: [
{
Expand Down Expand Up @@ -619,7 +780,7 @@ export const GET: APIRoute = async ({ url }) => {
type: 'string',
},
example:
'import React from \'react\';\nimport { Alert } from \'@patternfly/react-core\';\n\nexport const AlertBasic = () => <Alert title="Basic alert" />;',
"import React from 'react';\nimport { Alert } from '@patternfly/react-core';\n\nexport const AlertBasic = () => <Alert title=\"Basic alert\" />;",
},
},
},
Expand Down