Zeta & MDX: Support custom build page tree options

This commit is contained in:
SonMooSans
2023-10-28 13:14:20 +08:00
parent 1e9be62bde
commit 0cc10cba35
6 changed files with 80 additions and 33 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'next-docs-zeta': patch
'next-docs-mdx': patch
---
Support custom build page tree options
+10 -9
View File
@@ -1,4 +1,4 @@
import type { PageTree } from 'next-docs-zeta/server'
import type { BuildPageTreeOptions, PageTree } from 'next-docs-zeta/server'
import { getPageTreeBuilder, type BuilderOptions } from './build-tree'
import { createPageUtils, type PageUtils } from './page-utils'
import {
@@ -16,11 +16,6 @@ type UtilsOptions<Langs extends string[] | undefined> = {
*/
baseUrl: string
/**
* @deprecated Use `rootDir` instead
*/
root: string
/**
* Root directory, files outside of the root directory will be ignored
*
@@ -28,6 +23,8 @@ type UtilsOptions<Langs extends string[] | undefined> = {
*/
rootDir: string
pageTreeOptions: BuildPageTreeOptions
slugs: ResolveOptions['slugs']
validate: ResolveOptions['validate']
} & BuilderOptions
@@ -50,6 +47,7 @@ function fromMap<Langs extends string[] | undefined = undefined>(
slugs,
getUrl,
resolveIcon,
pageTreeOptions = { root: rootDir },
languages,
validate
}: Partial<UtilsOptions<Langs>> = {}
@@ -73,9 +71,12 @@ function fromMap<Langs extends string[] | undefined = undefined>(
...resolved,
...pageUtils,
tree: (languages == null
? builder.build({ root: rootDir })
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
builder.buildI18n({ languages, root: rootDir })) as any
? builder.build(pageTreeOptions)
: builder.buildI18n({
...pageTreeOptions,
languages
// eslint-disable-next-line @typescript-eslint/no-explicit-any
})) as any
}
}
+1 -5
View File
@@ -52,11 +52,7 @@ export function createPageUtils(
}
/**
* @example
* ```
* ['a','b','c'] // '/a/b/c'
* ['/a'] // '/a'
* ```
* Join paths with leading slash
*/
function joinPaths(...paths: (string | null)[]): string {
const relative = paths
@@ -1,3 +1,4 @@
import type { BuildPageTreeOptions } from '@/server'
import type { PageTree } from '@/server/types'
import { loadContext, type ContextOptions } from './load-context'
import type { DocsPageBase, MetaPageBase, PagesContext } from './types'
@@ -5,6 +6,10 @@ import { createUtils, type ContentlayerUtils } from './utils'
type Options<L extends string[] | undefined> = Partial<
Omit<ContextOptions, 'languages'> & {
/**
* @default { root: 'docs' }
*/
pageTreeOptions: BuildPageTreeOptions
languages: L
}
>
@@ -31,13 +36,17 @@ export const createContentlayer: CreateContentlayer = (
options = {}
) => {
const ctx = loadContext(metaPages, docsPages, options)
const pageTreeOptions = options.pageTreeOptions ?? { root: 'docs' }
return {
__pageContext: ctx,
tree: (options.languages != null
? ctx.builder.buildI18n({ languages: options.languages })
? ctx.builder.buildI18n({
...pageTreeOptions,
languages: options.languages
})
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
ctx.builder.build()) as any,
ctx.builder.build(pageTreeOptions)) as any,
...createUtils(ctx)
}
}
@@ -42,10 +42,7 @@ export function loadContext<Docs extends DocsPageBase>(
icon: meta.icon,
title: meta.title
})),
resolveIcon(icon) {
if (icon == null) return
return resolveIcon(icon)
}
resolveIcon
})
return {
@@ -53,7 +53,7 @@ type PageTreeBuilderContext = {
export type BuildPageTreeOptions = {
/**
* The root folder to scan files
* @default 'docs'
* @default ''
*/
root: string
}
@@ -87,7 +87,6 @@ function buildMeta(
meta: AbstractMeta,
ctx: PageTreeBuilderContext
): FolderNode {
const segments = meta.file.dirname.split('/')
let index: FileNode | undefined = undefined
const filtered = new Set<string>()
@@ -109,7 +108,7 @@ function buildMeta(
item = extract_result[1]
}
const path = meta.file.dirname + '/' + item
const path = joinPaths([meta.file.dirname, item])
const page = ctx.getPageByPath(path)
if (page != null) {
@@ -151,14 +150,14 @@ function buildMeta(
})
if (index == null) {
const page = ctx.getPageByPath(meta.file.dirname + '/index')
const page = ctx.getPageByPath(joinPaths([meta.file.dirname, 'index']))
if (page != null) index = buildFileNode(page, ctx)
}
return {
type: 'folder',
name: meta.title ?? index?.name ?? pathToName(segments),
name: meta.title ?? index?.name ?? pathToName(splitPath(meta.file.dirname)),
icon: ctx.resolveIcon(meta.icon),
index,
children
@@ -193,15 +192,15 @@ function getFolderNodes(
children.push(node)
}
const segments = path.split('/')
const segments = splitPath(path)
const folders = new Set<string>(
ctx.basePages.flatMap(page => {
const dirnameSegments = page.file.dirname.split('/')
const dirnameSegments = splitPath(page.file.dirname)
if (path.length === 0 && path !== page.file.dirname)
return dirnameSegments[0]
if (page.file.dirname.startsWith(path + '/'))
return path + '/' + dirnameSegments[segments.length]
return joinPaths([path, dirnameSegments[segments.length]])
return []
})
@@ -241,8 +240,9 @@ function buildFolderNode(
keepIndex: boolean = false
): FolderNode {
let meta: AbstractMeta | undefined
if (ctx.lang) meta = ctx.getMetaByPath(path + `/meta-${ctx.lang}`)
meta ??= ctx.getMetaByPath(path + '/meta')
if (ctx.lang) meta = ctx.getMetaByPath(joinPaths([path, `meta-${ctx.lang}`]))
meta ??= ctx.getMetaByPath(joinPaths([path, 'meta']))
if (meta != null) {
return buildMeta(meta, ctx)
@@ -252,7 +252,7 @@ function buildFolderNode(
return {
type: 'folder',
name: index?.name ?? pathToName(path.split('/')),
name: index?.name ?? pathToName(splitPath(path)),
index,
children
}
@@ -296,10 +296,10 @@ export function createPageTreeBuilder({
}
return {
build({ root = 'docs' } = {}) {
build({ root = '' } = {}) {
return build(root, context)
},
buildI18n({ root = 'docs', languages = [] } = {}) {
buildI18n({ root = '', languages = [] } = {}) {
const entries = languages.map(lang => {
const tree = build(root, {
...context,
@@ -318,3 +318,41 @@ function pathToName(path: string[]): string {
const name = path[path.length - 1] ?? 'docs'
return name.slice(0, 1).toUpperCase() + name.slice(1)
}
/**
* Split path into segments, trailing/leading slashes are removed
*/
export function splitPath(path: string): string[] {
return path.split('/').filter(p => p.length > 0)
}
/**
* Convert paths to an array, slashes within the path will be ignored
* @param paths
* @param slash Add a trailing/leading slash to path
* @example
* ```
* ['a','b','c'] // 'a/b/c'
* ['/a'] // 'a'
* ['a', '/b'] // 'a/b'
* ['a', 'b/c'] // 'a/b/c'
* ```
*/
export function joinPaths(
paths: string[],
slash: 'leading' | 'trailing' | 'none' = 'none'
): string {
const joined = paths
// avoid slashes in path and filter empty
.flatMap(path => splitPath(path))
.join('/')
switch (slash) {
case 'leading':
return '/' + joined
case 'trailing':
return joined + '/'
default:
return joined
}
}