diff --git a/cli/__tests__/buildPropsData.test.ts b/cli/__tests__/buildPropsData.test.ts index 4fd3f43..7bcf349 100644 --- a/cli/__tests__/buildPropsData.test.ts +++ b/cli/__tests__/buildPropsData.test.ts @@ -12,6 +12,10 @@ jest.mock('../tsDocGen') // suppress console logs so that it doesn't clutter the test output jest.spyOn(console, 'log').mockImplementation(() => {}) +beforeEach(() => { + jest.clearAllMocks() +}) + const validConfigResponse = { propsGlobs: [ { @@ -90,8 +94,12 @@ it('should call getConfig with the passed config file location', async () => { it('should not proceed if config is not found', async () => { ;(getConfig as jest.Mock).mockResolvedValue(undefined) + const mockConsoleError = jest.fn() + jest.spyOn(console, 'error').mockImplementation(mockConsoleError) + await buildPropsData('/root/', '/config', false) + expect(mockConsoleError).toHaveBeenCalledWith('No config found, please run the `setup` command or manually create a pf-docs.config.mjs file') expect(writeFile).not.toHaveBeenCalled() }) @@ -117,11 +125,12 @@ it('should call glob with the propGlobs in the config file and the cwd set to th expect(glob).toHaveBeenNthCalledWith( 1, ['**/include/files/*', '**/include/other/files/*'], - { cwd: '/root/', ignore: ['**/exclude/files/*'] }, + { cwd: '/root/', ignore: ['**/exclude/files/*'], absolute: true }, ) expect(glob).toHaveBeenNthCalledWith(2, ['**/one/more/include/*'], { cwd: '/root/', ignore: [], + absolute: true, }) expect(glob).toHaveBeenCalledTimes(2) }) @@ -163,3 +172,39 @@ it('should call writeFile with the returned prop data in JSON form', async () => JSON.stringify(propsData), ) }) + +it('should log verbose messages when run in verbose mode', async () => { + ;(getConfig as jest.Mock).mockResolvedValue(validConfigResponse) + ;(glob as unknown as jest.Mock).mockResolvedValueOnce(['files/one']) + ;(glob as unknown as jest.Mock).mockResolvedValueOnce(['files/two']) + ;(tsDocgen as jest.Mock).mockResolvedValueOnce(validTsDocGenResponseOne) + ;(tsDocgen as jest.Mock).mockResolvedValueOnce(validTsDocGenResponseTwo) + + const mockConsoleLog = jest.fn() + jest.spyOn(console, 'log').mockImplementation(mockConsoleLog) + + await buildPropsData('/root/', '/config', true) + + // Check verbose logging messages + expect(mockConsoleLog).toHaveBeenCalledWith('Beginning props data build') + expect(mockConsoleLog).toHaveBeenCalledWith('Found 2 files to parse') + expect(mockConsoleLog).toHaveBeenCalledWith('Parsing props from files/one') + expect(mockConsoleLog).toHaveBeenCalledWith('Parsing props from files/two') + expect(mockConsoleLog).toHaveBeenCalledWith(`Writing props data to ${process.cwd()}/output/dir/props.json`) +}) + +it('should not log verbose messages when not run in verbose mode', async () => { + ;(getConfig as jest.Mock).mockResolvedValue(validConfigResponse) + ;(glob as unknown as jest.Mock).mockResolvedValueOnce(['files/one']) + ;(glob as unknown as jest.Mock).mockResolvedValueOnce(['files/two']) + ;(tsDocgen as jest.Mock).mockResolvedValueOnce(validTsDocGenResponseOne) + ;(tsDocgen as jest.Mock).mockResolvedValueOnce(validTsDocGenResponseTwo) + + const mockConsoleLog = jest.fn() + jest.spyOn(console, 'log').mockImplementation(mockConsoleLog) + + await buildPropsData('/root/', '/config', false) + + // Should not have any verbose logging calls + expect(mockConsoleLog).not.toHaveBeenCalled() +}) diff --git a/cli/__tests__/createCollectionContent.test.ts b/cli/__tests__/createCollectionContent.test.ts index e9f0a88..4482c85 100644 --- a/cli/__tests__/createCollectionContent.test.ts +++ b/cli/__tests__/createCollectionContent.test.ts @@ -1,24 +1,39 @@ import { createCollectionContent } from '../createCollectionContent' import { getConfig } from '../getConfig' import { writeFile } from 'fs/promises' +import { existsSync } from 'fs' jest.mock('../getConfig') jest.mock('fs/promises') +jest.mock('fs') // suppress console.log so that it doesn't clutter the test output jest.spyOn(console, 'log').mockImplementation(() => {}) +beforeEach(() => { + jest.clearAllMocks() +}) + it('should call getConfig with the passed config file location', async () => { - await createCollectionContent('/foo/', 'bar', false) + jest.spyOn(console, 'error').mockImplementation(() => {}) + ;(getConfig as jest.Mock).mockResolvedValue(undefined) - expect(getConfig).toHaveBeenCalledWith('bar') + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) + + expect(getConfig).toHaveBeenCalledWith('/config/dir/pf-docs.config.mjs') }) it('should not proceed if config is not found', async () => { ;(getConfig as jest.Mock).mockResolvedValue(undefined) - await createCollectionContent('/foo/', 'bar', false) + const mockConsoleError = jest.fn() + jest.spyOn(console, 'error').mockImplementation(mockConsoleError) + + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) + expect(mockConsoleError).toHaveBeenCalledWith( + 'No config found, please run the `setup` command or manually create a pf-docs.config.mjs file', + ) expect(writeFile).not.toHaveBeenCalled() }) @@ -28,29 +43,45 @@ it('should log error if content is not found in config', async () => { const mockConsoleError = jest.fn() jest.spyOn(console, 'error').mockImplementation(mockConsoleError) - await createCollectionContent('/foo/', 'bar', false) + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) expect(mockConsoleError).toHaveBeenCalledWith('No content found in config') expect(writeFile).not.toHaveBeenCalled() }) it('should call writeFile with the expected file location and content without throwing any errors', async () => { - ;(getConfig as jest.Mock).mockResolvedValue({ content: { key: 'value' } }) + const mockContent = [ + { name: 'test', base: 'src/docs', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '.' + }) const mockConsoleError = jest.fn() jest.spyOn(console, 'error').mockImplementation(mockConsoleError) - await createCollectionContent('/foo/', 'bar', false) + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) + + const expectedContent = [ + { name: 'test', base: '/config/dir/src/docs', pattern: '**/*.md' } + ] expect(writeFile).toHaveBeenCalledWith( '/foo/src/content.ts', - `export const content = ${JSON.stringify({ key: 'value' })}`, + `export const content = ${JSON.stringify(expectedContent)}`, ) expect(mockConsoleError).not.toHaveBeenCalled() }) it('should log error if writeFile throws an error', async () => { - ;(getConfig as jest.Mock).mockResolvedValue({ content: { key: 'value' } }) + const mockContent = [ + { name: 'test', base: 'src/docs', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '.' + }) const mockConsoleError = jest.fn() jest.spyOn(console, 'error').mockImplementation(mockConsoleError) @@ -58,7 +89,7 @@ it('should log error if writeFile throws an error', async () => { const error = new Error('error') ;(writeFile as jest.Mock).mockRejectedValue(error) - await createCollectionContent('/foo/', 'bar', false) + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) expect(mockConsoleError).toHaveBeenCalledWith( 'Error writing content file', @@ -66,20 +97,228 @@ it('should log error if writeFile throws an error', async () => { ) }) -it('should log that content file was created when run in verbose mode', async () => { +it('should log all verbose messages when run in verbose mode', async () => { + const mockContent = [ + { name: 'docs', base: 'src/docs', pattern: '**/*.md' }, + { name: 'components', packageName: '@patternfly/react-core', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '../' + }) + ;(existsSync as jest.Mock).mockReturnValue(true) + ;(writeFile as jest.Mock).mockResolvedValue(undefined) + const mockConsoleLog = jest.fn() jest.spyOn(console, 'log').mockImplementation(mockConsoleLog) - await createCollectionContent('/foo/', 'bar', true) + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', true) + // Check that all verbose logs were called + expect(mockConsoleLog).toHaveBeenCalledWith('configuration content entry: ', mockContent, '\n') + expect(mockConsoleLog).toHaveBeenCalledWith('Creating content file', '/foo/src/content.ts', '\n') + expect(mockConsoleLog).toHaveBeenCalledWith('repoRootDir: ', '/config', '\n') + + // For the base entry + expect(mockConsoleLog).toHaveBeenCalledWith('relative path: ', 'src/docs') + expect(mockConsoleLog).toHaveBeenCalledWith('absolute path: ', '/config/dir/src/docs', '\n') + + // For the packageName entry + expect(mockConsoleLog).toHaveBeenCalledWith('looking for package in ', '/config/dir/node_modules', '\n') + expect(mockConsoleLog).toHaveBeenCalledWith('found package at ', '/config/dir/node_modules/@patternfly/react-core', '\n') + + // Final log expect(mockConsoleLog).toHaveBeenCalledWith('Content file created') }) -it('should not log that content file was created when not run in verbose mode', async () => { +it('should log verbose message when no package name or base path is found', async () => { + const mockContent = [ + { name: 'invalid-entry', pattern: '**/*.md' } // no base or packageName + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '.' + }) + ;(writeFile as jest.Mock).mockResolvedValue(undefined) + + const mockConsoleLog = jest.fn() + jest.spyOn(console, 'log').mockImplementation(mockConsoleLog) + + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', true) + + expect(mockConsoleLog).toHaveBeenCalledWith( + 'no package name or base path found, skipping entry ', + 'invalid-entry', + '\n', + ) +}) + +it('should log verbose message when package is not found and reaches repoRootDir', async () => { + const mockContent = [ + { name: 'test', packageName: '@patternfly/react-core', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '.' // Same as configDir, so search will stop immediately + }) + ;(existsSync as jest.Mock).mockReturnValue(false) // Package not found + ;(writeFile as jest.Mock).mockResolvedValue(undefined) + + const mockConsoleLog = jest.fn() + jest.spyOn(console, 'log').mockImplementation(mockConsoleLog) + + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', true) + + expect(mockConsoleLog).toHaveBeenCalledWith('looking for package in ', '/config/dir/node_modules', '\n') + expect(mockConsoleLog).toHaveBeenCalledWith('reached repoRootDir, stopping search') +}) + +it('should not log to the console when not run in verbose mode', async () => { + const mockContent = [ + { name: 'test', base: 'src/docs', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '.' + }) + ;(writeFile as jest.Mock).mockResolvedValue(undefined) + const mockConsoleLog = jest.fn() jest.spyOn(console, 'log').mockImplementation(mockConsoleLog) - await createCollectionContent('/foo/', 'bar', false) + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) expect(mockConsoleLog).not.toHaveBeenCalled() }) + +it('should handle content with packageName by finding package in node_modules', async () => { + const mockContent = [ + { name: 'test', packageName: '@patternfly/react-core', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '.' + }) + ;(existsSync as jest.Mock).mockReturnValue(true) + ;(writeFile as jest.Mock).mockResolvedValue(undefined) + + const mockConsoleError = jest.fn() + jest.spyOn(console, 'error').mockImplementation(mockConsoleError) + + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) + + const expectedContent = [ + { + base: '/config/dir/node_modules/@patternfly/react-core', + name: 'test', + packageName: '@patternfly/react-core', + pattern: '**/*.md' + } + ] + + expect(writeFile).toHaveBeenCalledWith( + '/foo/src/content.ts', + `export const content = ${JSON.stringify(expectedContent)}`, + ) + expect(mockConsoleError).not.toHaveBeenCalled() +}) + +it('should handle content with packageName when package is not found locally but found in parent directory', async () => { + const mockContent = [ + { name: 'test', packageName: '@patternfly/react-core', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '../../' + }) + ;(existsSync as jest.Mock) + .mockReturnValueOnce(false) // not found in /config/dir/node_modules + .mockReturnValueOnce(true) // found in /config/node_modules + ;(writeFile as jest.Mock).mockResolvedValue(undefined) + + const mockConsoleError = jest.fn() + jest.spyOn(console, 'error').mockImplementation(mockConsoleError) + + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) + + const expectedContent = [ + { + base: '/config/node_modules/@patternfly/react-core', + name: 'test', + packageName: '@patternfly/react-core', + pattern: '**/*.md' + } + ] + + expect(writeFile).toHaveBeenCalledWith( + '/foo/src/content.ts', + `export const content = ${JSON.stringify(expectedContent)}`, + ) + expect(mockConsoleError).not.toHaveBeenCalled() +}) + +it('should handle content with packageName when package is not found anywhere', async () => { + const mockContent = [ + { name: 'test', packageName: '@patternfly/react-core', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '../' + }) + ;(existsSync as jest.Mock).mockReturnValue(false) + ;(writeFile as jest.Mock).mockResolvedValue(undefined) + + const mockConsoleError = jest.fn() + jest.spyOn(console, 'error').mockImplementation(mockConsoleError) + + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) + + const expectedContent = [ + { + base: null, + name: 'test', + packageName: '@patternfly/react-core', + pattern: '**/*.md' + } + ] + + expect(writeFile).toHaveBeenCalledWith( + '/foo/src/content.ts', + `export const content = ${JSON.stringify(expectedContent)}`, + ) + expect(mockConsoleError).not.toHaveBeenCalled() +}) + +it('should handle mixed content with both base and packageName entries', async () => { + const mockContent = [ + { name: 'docs', base: 'src/docs', pattern: '**/*.md' }, + { name: 'components', packageName: '@patternfly/react-core', pattern: '**/*.md' } + ] + ;(getConfig as jest.Mock).mockResolvedValue({ + content: mockContent, + repoRoot: '../' + }) + ;(existsSync as jest.Mock).mockReturnValue(true) + ;(writeFile as jest.Mock).mockResolvedValue(undefined) + + const mockConsoleError = jest.fn() + jest.spyOn(console, 'error').mockImplementation(mockConsoleError) + + await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false) + + const expectedContent = [ + { name: 'docs', base: '/config/dir/src/docs', pattern: '**/*.md' }, + { + base: '/config/dir/node_modules/@patternfly/react-core', + name: 'components', + packageName: '@patternfly/react-core', + pattern: '**/*.md' + } + ] + + expect(writeFile).toHaveBeenCalledWith( + '/foo/src/content.ts', + `export const content = ${JSON.stringify(expectedContent)}`, + ) + expect(mockConsoleError).not.toHaveBeenCalled() +}) diff --git a/cli/buildPropsData.ts b/cli/buildPropsData.ts index 8e9b0a4..aa2ba49 100644 --- a/cli/buildPropsData.ts +++ b/cli/buildPropsData.ts @@ -43,7 +43,7 @@ function getTsDocNameVariant(source: string) { async function getFiles(root: string, globs: PropsGlobs[]) { const files = await Promise.all( globs.map(async ({ include, exclude }) => { - const files = await glob(include, { cwd: root, ignore: exclude }) + const files = await glob(include, { cwd: root, ignore: exclude, absolute: true }) return files }), ) @@ -88,8 +88,17 @@ export async function buildPropsData( configFile: string, verbose: boolean, ) { + const verboseModeLog = (...messages: any) => { + if (verbose) { + console.log(...messages) + } + } + + verboseModeLog('Beginning props data build') + const config = await getConfig(configFile) if (!config) { + console.error('No config found, please run the `setup` command or manually create a pf-docs.config.mjs file') return } @@ -100,18 +109,14 @@ export async function buildPropsData( } const files = await getFiles(rootDir, propsGlobs) - if (verbose) { - console.log(`Found ${files.length} files to parse`) - } + verboseModeLog(`Found ${files.length} files to parse`) const propsData = await getPropsData(files, verbose) const propsFile = join(outputDir, 'props.json') - if (verbose) { - const absolutePropsFilePath = join(process.cwd(), propsFile) - console.log(`Writing props data to ${absolutePropsFilePath}`) - } + const absolutePropsFilePath = join(process.cwd(), propsFile) + verboseModeLog(`Writing props data to ${absolutePropsFilePath}`) await writeFile(propsFile, JSON.stringify(propsData)) } diff --git a/cli/cli.ts b/cli/cli.ts index b6ec892..53b7507 100755 --- a/cli/cli.ts +++ b/cli/cli.ts @@ -2,7 +2,7 @@ /* eslint-disable no-console */ import { Command } from 'commander' import { build, dev, preview, sync } from 'astro' -import { join } from 'path' +import { join, resolve } from 'path' import { createCollectionContent } from './createCollectionContent.js' import { setFsRootDir } from './setFsRootDir.js' import { createConfigFile } from './createConfigFile.js' @@ -12,23 +12,60 @@ import { symLinkConfig } from './symLinkConfig.js' import { buildPropsData } from './buildPropsData.js' import { hasFile } from './hasFile.js' import { convertToMDX } from './convertToMDX.js' +import { mkdir } from 'fs/promises' + +const currentDir = process.cwd() +const config = await getConfig(`${currentDir}/pf-docs.config.mjs`) + +if (!config) { + console.error( + 'No config found, please run the `setup` command or manually create a pf-docs.config.mjs file', + ) + process.exit(1) +} + +let astroRoot = '' + +try { + astroRoot = import.meta + .resolve('@patternfly/patternfly-doc-core') + .replace('dist/cli/cli.js', '') + .replace('file://', '') +} catch (e: any) { + if (e.code === 'ERR_MODULE_NOT_FOUND') { + astroRoot = process.cwd() + } else { + console.error('Error resolving astroRoot', e) + } +} + +const absoluteOutputDir = resolve(currentDir, config.outputDir) + +await mkdir(absoluteOutputDir, { recursive: true }) async function updateContent(program: Command) { const { verbose } = program.opts() - + if (!config) { + console.error( + 'No config found, please run the `setup` command or manually create a pf-docs.config.mjs file', + ) + return config + } if (verbose) { console.log('Verbose mode enabled') } await createCollectionContent( astroRoot, - `${process.cwd()}/pf-docs.config.mjs`, + `${currentDir}/pf-docs.config.mjs`, verbose, ) } async function generateProps(program: Command, forceProps: boolean = false) { const { verbose, props } = program.opts() + const { repoRoot } = config + const rootDir = repoRoot ? resolve(currentDir, repoRoot) : currentDir if (!props && !forceProps) { return @@ -38,18 +75,10 @@ async function generateProps(program: Command, forceProps: boolean = false) { console.log('Verbose mode enabled') } - buildPropsData(currentDir, `${currentDir}/pf-docs.config.mjs`, verbose) + buildPropsData(rootDir, `${currentDir}/pf-docs.config.mjs`, verbose) } async function transformMDContentToMDX() { - const config = await getConfig(`${currentDir}/pf-docs.config.mjs`) - if (!config) { - console.error( - 'No config found, please run the `setup` command or manually create a pf-docs.config.mjs file', - ) - return config - } - if (config.content) { await Promise.all( config.content.map((contentObj) => convertToMDX(contentObj.pattern)), @@ -60,7 +89,6 @@ async function transformMDContentToMDX() { async function buildProject(): Promise { await updateContent(program) await generateProps(program, true) - const config = await getConfig(`${currentDir}/pf-docs.config.mjs`) if (!config) { console.error( 'No config found, please run the `setup` command or manually create a pf-docs.config.mjs file', @@ -79,7 +107,7 @@ async function buildProject(): Promise { build({ root: astroRoot, - outDir: join(currentDir, config.outputDir, 'docs'), + outDir: join(absoluteOutputDir, 'docs'), }) return config @@ -102,7 +130,7 @@ async function deploy() { // Deploy using Wrangler const { execSync } = await import('child_process') - const outputPath = join(currentDir, config.outputDir, 'docs') + const outputPath = join(absoluteOutputDir, 'docs') execSync(`npx wrangler pages deploy ${outputPath}`, { stdio: 'inherit', @@ -117,23 +145,6 @@ async function deploy() { } } -let astroRoot = '' - -try { - astroRoot = import.meta - .resolve('@patternfly/patternfly-doc-core') - .replace('dist/cli/cli.js', '') - .replace('file://', '') -} catch (e: any) { - if (e.code === 'ERR_MODULE_NOT_FOUND') { - astroRoot = process.cwd() - } else { - console.error('Error resolving astroRoot', e) - } -} - -const currentDir = process.cwd() - const program = new Command() program.name('pf-doc-core') @@ -164,7 +175,7 @@ program.command('start').action(async () => { // if a props file hasn't been generated yet, but the consumer has propsData, it will cause a runtime error so to // prevent that we're just creating a props file regardless of what they say if one doesn't exist yet - const hasPropsFile = await hasFile(join(astroRoot, 'dist', 'props.json')) + const hasPropsFile = await hasFile(join(absoluteOutputDir, 'props.json')) await generateProps(program, !hasPropsFile) dev({ mode: 'development', root: astroRoot }) }) diff --git a/cli/createCollectionContent.ts b/cli/createCollectionContent.ts index 58b600a..b5eb2e4 100644 --- a/cli/createCollectionContent.ts +++ b/cli/createCollectionContent.ts @@ -1,32 +1,132 @@ /* eslint-disable no-console */ import { writeFile } from 'fs/promises' -import { join } from 'path' +import { dirname, join, resolve } from 'path' import { getConfig } from './getConfig.js' +import { existsSync } from 'fs' -export async function createCollectionContent(rootDir: string, configFile: string, verbose: boolean) { - const config = await getConfig(configFile) +/** + * Looks for the specified package in the node_modules directory in the configDir, then recursively + * looks for the package in the parent directories until it is found, stopping if it is not found + * and the repoRootDir is reached. + */ +const findPackage = ( + dir: string, + packageName: string, + repoRootDir: string, + verbose?: boolean, +) => { + const nodeModulesPath = join(dir, 'node_modules') + + if (verbose) { + console.log('looking for package in ', nodeModulesPath, '\n') + } + + const packagePath = join(nodeModulesPath, packageName) + const packageJsonPath = join(packagePath, 'package.json') + + if (existsSync(packageJsonPath)) { + if (verbose) { + console.log('found package at ', packagePath, '\n') + } + + return packagePath + } + if (dir === repoRootDir) { + if (verbose) { + console.log('reached repoRootDir, stopping search') + } + return null + } + + const parentDir = dirname(dir) + return findPackage(parentDir, packageName, repoRootDir, verbose) +} + +export async function createCollectionContent( + astroRoot: string, + configFileLocation: string, + verbose: boolean, +) { + const verboseModeLog = (...messages: any) => { + if (verbose) { + console.log(...messages) + } + } + + const config = await getConfig(configFileLocation) if (!config) { + console.error( + 'No config found, please run the `setup` command or manually create a pf-docs.config.mjs file', + ) return } - const { content } = config + const { content, repoRoot } = config if (!content) { console.error('No content found in config') return } - const contentFile = join(rootDir, 'src', 'content.ts') + verboseModeLog('configuration content entry: ', content, '\n') + + const contentFile = join(astroRoot, 'src', 'content.ts') + + verboseModeLog('Creating content file', contentFile, '\n') + + const configDir = dirname(configFileLocation) + + const repoRootDir = repoRoot ? resolve(configDir, repoRoot) : configDir + + verboseModeLog('repoRootDir: ', repoRootDir, '\n') + + const contentWithAbsolutePaths = content.map((contentEntry) => { + if (contentEntry.base) { + const absoluteBase = resolve(configDir, contentEntry.base) + + verboseModeLog('relative path: ', contentEntry.base) + verboseModeLog('absolute path: ', absoluteBase, '\n') + + return { + ...contentEntry, + base: absoluteBase, + } + } + + const { packageName } = contentEntry + + if (!packageName) { + verboseModeLog( + 'no package name or base path found, skipping entry ', + contentEntry.name, + '\n', + ) + return { + ...contentEntry, + base: null, + } + } + + const packagePath = findPackage( + configDir, + packageName, + repoRootDir, + verbose, + ) + + return { + base: packagePath, + ...contentEntry, + } + }) try { await writeFile( contentFile, - `export const content = ${JSON.stringify(content)}`, + `export const content = ${JSON.stringify(contentWithAbsolutePaths)}`, ) } catch (e: any) { console.error('Error writing content file', e) } finally { - if (verbose) { - console.log('Content file created') - } + verboseModeLog('Content file created') } } diff --git a/cli/getConfig.ts b/cli/getConfig.ts index 944768c..25ffe6a 100644 --- a/cli/getConfig.ts +++ b/cli/getConfig.ts @@ -15,6 +15,7 @@ export interface DocsConfig { content: CollectionDefinition[]; outputDir: string; propsGlobs: PropsGlobs[]; + repoRoot?: string; } export async function getConfig(fileLocation: string): Promise { diff --git a/src/content.config.ts b/src/content.config.ts index 29f09dc..35b6068 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -3,10 +3,11 @@ import { glob } from 'astro/loaders' import { content } from './content' import type { CollectionDefinition } from '../cli/getConfig' +import { convertToMDX } from '../cli/convertToMDX' function defineContent(contentObj: CollectionDefinition) { const { base, packageName, pattern, name } = contentObj - const dir = `${process.cwd()}/${base || `node_modules/${packageName}`}` + if (!base && !packageName) { // eslint-disable-next-line no-console @@ -20,10 +21,11 @@ function defineContent(contentObj: CollectionDefinition) { 'core-component-docs': 'html', } + convertToMDX(`${base}/${pattern}`) const mdxPattern = pattern.replace(/\.md$/, '.mdx') return defineCollection({ - loader: glob({ base: dir, pattern: mdxPattern }), + loader: glob({ base, pattern: mdxPattern }), schema: z.object({ id: z.string(), section: z.string(), diff --git a/src/pages/props.ts b/src/pages/props.ts index 7bef271..8b8373f 100644 --- a/src/pages/props.ts +++ b/src/pages/props.ts @@ -1,13 +1,17 @@ import { readFileSync } from 'node:fs' import { join } from 'node:path' - -const propsFilePath = join(process.cwd(), 'dist', 'props.json') -const propsData = readFileSync(propsFilePath) -const props = JSON.parse(propsData.toString()) +import { getConfig } from '../../cli/getConfig' export const prerender = false export async function GET({ request }: { request: Request }) { + 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 queryParams = new URL(request.url).searchParams const components = queryParams.get('components') const componentsArray = components?.split(',')