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
1 change: 1 addition & 0 deletions README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ All commands are run from the root of the project, from a terminal:
| `npm run build:cli` | Create a JS build of the documentation core CLI |
| `npm run build:cli:watch` | Run the CLI builder in watch mode |
| `npm run build:props` | Create a json file of your TsDoc compatible in code documentation |
| `patternfly-doc-core generate-package-props` | Create a package metadata artifact at `schema/props.json` containing its format version, package identity, and components |
39 changes: 37 additions & 2 deletions cli/__tests__/buildPropsData.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { writeFile } from 'fs/promises'
import { mkdir, readFile, writeFile } from 'fs/promises'
import { glob } from 'glob'
import { buildPropsData } from '../buildPropsData'
import {
buildPackagePropsData,
buildPropsData,
} from '../buildPropsData'
import { getConfig } from '../getConfig'
import { tsDocgen } from '../tsDocGen'

Expand Down Expand Up @@ -208,3 +211,35 @@ it('should not log verbose messages when not run in verbose mode', async () => {
// Should not have any verbose logging calls
expect(mockConsoleLog).not.toHaveBeenCalled()
})

it('should write package metadata with package identity and components', async () => {
;(getConfig as jest.Mock).mockResolvedValue(validConfigResponse)
;(glob as unknown as jest.Mock).mockResolvedValueOnce(['files/one'])
;(glob as unknown as jest.Mock).mockResolvedValueOnce([])
;(tsDocgen as jest.Mock).mockResolvedValue(validTsDocGenResponseOne)
;(readFile as jest.Mock).mockResolvedValue(
JSON.stringify({ name: '@patternfly/test-package', version: '1.2.3' }),
)

const result = await buildPackagePropsData({
rootDir: '/root',
configFile: '/config',
outputFile: 'schema/props.json',
verbose: false,
})

expect(result).toEqual({
formatVersion: 1,
package: '@patternfly/test-package',
packageVersion: '1.2.3',
components: {
ComponentOne: validTsDocGenResponseOne[0],
ComponentTwo: validTsDocGenResponseOne[1],
},
})
expect(mkdir).toHaveBeenCalledWith('/root/schema', { recursive: true })
expect(writeFile).toHaveBeenCalledWith(
'/root/schema/props.json',
`${JSON.stringify(result, null, 2)}\n`,
)
})
83 changes: 73 additions & 10 deletions cli/buildPropsData.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
/* eslint-disable no-console */

import { glob } from 'glob'
import { writeFile } from 'fs/promises'
import { join } from 'path'
import { mkdir, readFile, writeFile } from 'fs/promises'
import { dirname, join, resolve } from 'path'

import { tsDocgen } from './tsDocGen.js'
import { getConfig, PropsGlobs } from './getConfig.js'

interface Prop {
export interface Prop {
name: string
type: string
description?: string
required?: boolean
defaultValue?: string
hide?: boolean
}
interface TsDoc {
export interface TsDoc {
name: string
description: string
props: Prop[]
}
interface PropsData {
export interface PropsData {
[key: string]: TsDoc
}

export interface PackagePropsData {
formatVersion: 1
package: string
packageVersion: string
components: PropsData
}

// Build unique names for components with a "variant" extension
type TsDocVariants = 'next' | 'deprecated' | undefined
function getTsDocName(name: string, variant: TsDocVariants) {
Expand All @@ -50,7 +57,7 @@ async function getFiles(root: string, globs: PropsGlobs[]) {
return files.flat()
}

async function getPropsData(files: string[], verbose: boolean) {
async function getPropsData(files: string[], verbose: boolean): Promise<PropsData> {
const perFilePropsData = await Promise.all(
files.map(async (file) => {
if (verbose) {
Expand Down Expand Up @@ -83,7 +90,14 @@ async function getPropsData(files: string[], verbose: boolean) {
return combinedPropsData
}

export async function buildPropsData(
interface BuildPropsOptions {
rootDir: string
configFile: string
outputFile: string
verbose: boolean
}

async function getConfiguredPropsData(
rootDir: string,
configFile: string,
verbose: boolean,
Expand All @@ -102,7 +116,7 @@ export async function buildPropsData(
return
}

const { propsGlobs, outputDir } = config
const { propsGlobs } = config
if (!propsGlobs) {
console.error('No props data found in config')
return
Expand All @@ -111,12 +125,61 @@ export async function buildPropsData(
const files = await getFiles(rootDir, propsGlobs)
verboseModeLog(`Found ${files.length} files to parse`)

const propsData = await getPropsData(files, verbose)
return {
propsData: await getPropsData(files, verbose),
outputDir: config.outputDir,
verboseModeLog,
}
}

const propsFile = join(outputDir, 'props.json')
export async function buildPropsData(
rootDir: string,
configFile: string,
verbose: boolean,
) {
const configuredData = await getConfiguredPropsData(rootDir, configFile, verbose)
if (!configuredData) {
return
}

const { propsData, outputDir, verboseModeLog } = configuredData
const propsFile = join(outputDir, 'props.json')
const absolutePropsFilePath = join(process.cwd(), propsFile)
verboseModeLog(`Writing props data to ${absolutePropsFilePath}`)

await writeFile(propsFile, JSON.stringify(propsData))
}

export async function buildPackagePropsData({
rootDir,
configFile,
outputFile,
verbose,
}: BuildPropsOptions): Promise<PackagePropsData | undefined> {
const configuredData = await getConfiguredPropsData(rootDir, configFile, verbose)
if (!configuredData) {
return
}

const packageJson = JSON.parse(
await readFile(join(rootDir, 'package.json'), 'utf8'),
) as { name?: string; version?: string }

if (!packageJson.name || !packageJson.version) {
throw new Error('Package metadata must include name and version in package.json')
}

const packagePropsData: PackagePropsData = {
formatVersion: 1,
package: packageJson.name,
packageVersion: packageJson.version,
components: configuredData.propsData,
}

const outputPath = resolve(rootDir, outputFile)
await mkdir(dirname(outputPath), { recursive: true })
configuredData.verboseModeLog(`Writing package props data to ${outputPath}`)
await writeFile(outputPath, `${JSON.stringify(packagePropsData, null, 2)}\n`)

return packagePropsData
}
23 changes: 22 additions & 1 deletion cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { createConfigFile } from './createConfigFile.js'
import { updatePackageFile } from './updatePackageFile.js'
import { DocsConfig, getConfig } from './getConfig.js'
import { symLinkConfig } from './symLinkConfig.js'
import { buildPropsData } from './buildPropsData.js'
import {
buildPackagePropsData,
buildPropsData,
} from './buildPropsData.js'
import { hasFile } from './hasFile.js'
import { convertToMDX } from './convertToMDX.js'
import { mkdir, copyFile } from 'fs/promises'
Expand Down Expand Up @@ -244,6 +247,24 @@ program.command('generate-props').action(async () => {
console.log('\nProps data generated')
})

program
.command('generate-package-props')
.argument('[outputFile]', 'package metadata output path', 'schema/props.json')
.action(async (outputFile: string) => {
const { verbose } = program.opts()
const { repoRoot } = config
const rootDir = repoRoot ? resolve(currentDir, repoRoot) : currentDir

await buildPackagePropsData({
rootDir,
configFile: `${currentDir}/pf-docs.config.mjs`,
outputFile,
verbose,
})

console.log(`\nPackage props data generated at ${outputFile}`)
})

program.command('serve').action(async () => {
await updateContent(program)
preview({ root: astroRoot })
Expand Down
Loading