Skip to content

Commit

Permalink
feat: auto generated author page
Browse files Browse the repository at this point in the history
  • Loading branch information
jeangovil committed Jun 6, 2023
1 parent b23eecc commit 090813f
Show file tree
Hide file tree
Showing 12 changed files with 382 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Typography } from '@acid-info/lsd-react'
import Link from '@docusaurus/Link'
import clsx from 'clsx'
import React from 'react'
import { useDocThemeOptions } from '../../../lib/useThemeOptions'
import './DocMetadata.scss'
import { useDocMetadata } from './useDocMetadata'

Expand All @@ -12,14 +14,25 @@ export const DocMetadata: React.FC<DocMetadataProps> = ({
...props
}) => {
const { date, authors } = useDocMetadata()
const { content: { authorPage } = {} } = useDocThemeOptions()

return (
<div className={clsx(className, 'mdx-doc-metadata')} {...(props as any)}>
{date && <Typography variant="body2">{date}</Typography>}
{authors && authors.length > 0 && (
<>
<Typography variant="body2">
by {authors.map((author) => author!.name).join(', ')}
by{' '}
{authors.map((author, index) => (
<React.Fragment key={author!.key}>
{authorPage ? (
<Link to={`author/${author!.key}`}>{author!.name}</Link>
) : (
author!.name
)}
{index < authors.length - 1 && ', '}
</React.Fragment>
))}
</Typography>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ export const useThemeOptions = (): ThemeOptions => {
export const useDocThemeOptions = (): DocConfig => {
const activePlugin = useActivePlugin()
const themeOptions = useThemeOptions()
return activePlugin ? themeOptions?.docs?.[activePlugin?.pluginId] ?? {} : {}
return (
activePlugin ? themeOptions?.docs?.[activePlugin?.pluginId] ?? {} : {}
) as DocConfig
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
@use '../../css/utils';

.root {
max-width: 80%;
}

.links {
padding-top: 0.5rem;

display: flex;
flex-direction: row;

& > a:not(:last-child) {
&::after {
content: '';
display: inline-block;
margin-inline: 0.75rem;
}
}
}

.docs {
margin-top: 1rem;

display: flex;
flex-direction: column;
gap: 1rem;
}

.date {
margin-bottom: 0.5rem;
font-style: italic;
opacity: 0.8;
}

.title {
display: block;
text-decoration: underline;
}

.description {
display: block;
margin-top: 0.25rem;
}

@include utils.responsive('lg', 'down') {
.root {
max-width: unset;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Typography } from '@acid-info/lsd-react'
import Link from '@docusaurus/Link'
import formatDate from 'date-fns/format'
import React from 'react'
import { Author } from '../../types/theme.types'
import styles from './AuthorPage.module.scss'

export type AuthorPageProps = {
data: {
author: Author
docs: {
id: string
title: string
description: string
permalink: string
frontMatter: {
date?: string
}
}[]
}
}

export const AuthorPage: React.FC<AuthorPageProps> = ({
data: { author, docs },
}) => {
return (
<div className={styles.root}>
<Typography className={styles.name} variant="h3">
{author.name}
</Typography>
<div className={styles.links}>
{author.github && (
<Typography
variant="body2"
component="a"
target="_blank"
href={`https://github.com/${author.github}`}
>
Github
</Typography>
)}
{author.twitter && (
<Typography
variant="body2"
component="a"
target="_blank"
href={`https://twitter.com/${author.twitter}`}
>
Twitter
</Typography>
)}
{author.website && (
<Typography
variant="body2"
component="a"
target="_blank"
href={author.website}
>
Website
</Typography>
)}
</div>
<div className={styles.docs}>
{docs.map((doc, index) => (
<div className={styles.doc} key={index}>
{doc.frontMatter.date && (
<Typography
className={styles.date}
variant="body2"
component="div"
>
{formatDate(new Date(doc.frontMatter.date), 'MMM d yyyy')}
</Typography>
)}
<Link className={styles.title} href={doc.permalink}>
{doc.title}
</Link>
<Typography className={styles.description} variant="body1">
{doc.description}
</Typography>
</div>
))}
</div>
</div>
)
}

export default AuthorPage
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './AuthorPage'
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ export type Author = {
website?: string
}

export type AuthorPageConfig = {
sidebar?: string
}

export type DocContent = {
authors?: Author[]
authorPage?: AuthorPageConfig | boolean
}

export type DocSidebarConfig = {
Expand Down
9 changes: 7 additions & 2 deletions packages/logos-docusaurus-theme/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import type { LoadContext, Plugin } from '@docusaurus/types'
import _ from 'lodash'
import path from 'path'
import type { ThemeOptions } from './client/types/theme.types'
import { createAuthorRoutes } from './server/utils/author.utils'

export default function logosTheme(
context: LoadContext,
options: ThemeOptions,
): Plugin<undefined> {
): Plugin<any> {
const clientModules: string[] = [
path.resolve(__dirname, './client/css/custom.scss'),
]
Expand Down Expand Up @@ -42,8 +43,12 @@ export default function logosTheme(
path.resolve(__dirname, '../src/client/theme'),

getClientModules: () => clientModules,

async contentLoaded(args) {
await createAuthorRoutes(context, args)
},
}
}

export type { ThemeOptions }
export { validateOptions } from './server/utils/validateOptions'
export type { ThemeOptions }
109 changes: 109 additions & 0 deletions packages/logos-docusaurus-theme/src/server/utils/author.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { LoadedContent } from '@docusaurus/plugin-content-docs'
import {
AllContent,
LoadContext,
PluginContentLoadedActions,
} from '@docusaurus/types'
import { docuHash } from '@docusaurus/utils'
import path from 'path'
import { ensureTrailingSlash } from '../../client/lib/string.utils'
import { getVersionMetadataPath } from './doc.utils'
import { getDocConfig } from './option.utils'

export const createAuthorRoutes = async (
context: LoadContext,
args: {
allContent: AllContent
actions: PluginContentLoadedActions
},
) => {
const docs = args.allContent['docusaurus-plugin-content-docs'] ?? {}

const docContents = Object.entries(docs)

for (const [id, loadedContent] of docContents) {
const { loadedVersions } = loadedContent as LoadedContent

for (const loadedVersion of loadedVersions) {
const { path: versionPath } = loadedVersion

const versionMetadataPath = getVersionMetadataPath(
context,
id,
loadedVersion,
)

const conf = getDocConfig(context, id)
const authorPageConf = conf?.content?.authorPage
if (!authorPageConf) return

const authors = conf?.content?.authors ?? []

const authorDocs: Record<string, any[]> = {}

loadedVersion.docs.forEach((doc) => {
const { author } = doc.frontMatter as any
const docAuthors = !author
? []
: Array.isArray(author)
? author
: [author]
const validAuthors = docAuthors.map((authorKey) =>
authors.find((a) => a.key === authorKey),
)
validAuthors.forEach((author) => {
if (!author) return

authorDocs[author.key] = [
...(authorDocs[author.key] ?? []),
{
id: doc.id,
title: doc.title,
description: doc.description,
frontMatter: doc.frontMatter,
permalink: doc.permalink,
},
]
})
})

const routes = await Promise.all(
authors.map(async (author) => {
const dataPath = await args.actions.createData(
`${docuHash(
`author-${author.key}-${loadedVersion.versionName}`,
)}.json`,
JSON.stringify({ author, docs: authorDocs[author.key] ?? [] }),
)

return {
path: `${ensureTrailingSlash(versionPath)}author/${author.key}`,
component: path.resolve(
__dirname,
'../../client/theme/AuthorPage/AuthorPage',
),
exact: true,
sidebar:
typeof authorPageConf === 'boolean'
? null
: authorPageConf!.sidebar,
modules: {
data: dataPath,
},
}
}),
)

args.actions.addRoute({
path: `${ensureTrailingSlash(versionPath)}author`,
exact: false,
component: '@theme/DocPage',
routes: routes ?? [],
modules: {
versionMetadata: versionMetadataPath,
},
priority: 1000,
})
}
}
}
21 changes: 21 additions & 0 deletions packages/logos-docusaurus-theme/src/server/utils/doc.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { LoadedVersion } from '@docusaurus/plugin-content-docs'
import { LoadContext } from '@docusaurus/types'
import { docuHash } from '@docusaurus/utils'
import path from 'path'

export const getVersionMetadataPath = (
context: LoadContext,
pluginId: string,
loadedVersion: LoadedVersion,
) => {
const pluginDataDirRoot = path.join(
context.generatedFilesDir,
'docusaurus-plugin-content-docs',
)
const dataDir = path.join(pluginDataDirRoot, pluginId)

return path.join(
dataDir,
`${docuHash(`version-${loadedVersion.versionName}-metadata-prop`)}.json`,
)
}
16 changes: 16 additions & 0 deletions packages/logos-docusaurus-theme/src/server/utils/option.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { LoadContext } from '@docusaurus/types'
import _ from 'lodash'
import type { DocConfig, ThemeOptions } from '../../client/types/theme.types'

export const getDocConfig = (
context: LoadContext,
pluginId: string,
): DocConfig => {
const themeOptions = _.get(
context,
'siteConfig.customFields.logos-docusaurus-theme',
{},
) as ThemeOptions

return themeOptions?.docs?.[pluginId] ?? {}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Joi } from '@docusaurus/utils-validation'
import {
Author,
AuthorPageConfig,
DocConfig,
DocContent,
DocSidebarConfig,
Expand All @@ -16,6 +17,12 @@ const schema = Joi.object<ThemeOptions>({
hide: Joi.bool().default(true),
}).default({}),
content: Joi.object<DocContent>({
authorPage: Joi.alternatives(
Joi.boolean(),
Joi.object<AuthorPageConfig>({
sidebar: Joi.string().optional(),
}),
).default({}),
authors: Joi.array()
.items(
Joi.object<Author>({
Expand Down

0 comments on commit 090813f

Please sign in to comment.