diff --git a/src/pages/api/[version]/[section]/[page]/props.ts b/src/pages/api/[version]/[section]/[page]/props.ts new file mode 100644 index 0000000..53adaf7 --- /dev/null +++ b/src/pages/api/[version]/[section]/[page]/props.ts @@ -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, + ) + } + + 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, + ) + } +} + + + diff --git a/src/pages/api/[version]/[section]/names.ts b/src/pages/api/[version]/[section]/names.ts new file mode 100644 index 0000000..9558119 --- /dev/null +++ b/src/pages/api/[version]/[section]/names.ts @@ -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, + ) + } +} + + + diff --git a/src/pages/api/index.ts b/src/pages/api/index.ts index cead58f..cdd59c4 100644 --- a/src/pages/api/index.ts +++ b/src/pages/api/index.ts @@ -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', @@ -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', diff --git a/src/pages/api/openapi.json.ts b/src/pages/api/openapi.json.ts index 0262f0d..babd078 100644 --- a/src/pages/api/openapi.json.ts +++ b/src/pages/api/openapi.json.ts @@ -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, ) } @@ -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': { @@ -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', @@ -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', @@ -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', @@ -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: [ { @@ -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 = () => ;', + "import React from 'react';\nimport { Alert } from '@patternfly/react-core';\n\nexport const AlertBasic = () => ;", }, }, },