mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 23:49:51 +00:00
Breaking: Pre-bundle page urls into raw pages
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'next-docs-zeta': major
|
||||
'next-docs-mdx': major
|
||||
'next-docs-ui': major
|
||||
---
|
||||
|
||||
**Pre-bundle page urls into raw pages.**
|
||||
|
||||
This means you don't need `getPageUrl` anymore for built-in adapters, including `next-docs-mdx` and Contentlayer. It is now replaced by the `url` property from the pages array provided by your adapter.
|
||||
@@ -24,7 +24,7 @@ export default async function Page({ params }: { params: Param }) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const neighbours = findNeighbour(tab.tree, tab.getPageUrl(page.slugs))
|
||||
const neighbours = findNeighbour(tab.tree, page.url)
|
||||
|
||||
const path = resolve('apps/docs/content/docs/', page.file.path)
|
||||
const time = await getGithubLastEdit({
|
||||
@@ -87,7 +87,7 @@ function Category({ page, tab }: { page: Page; tab: Utils }) {
|
||||
key={page.file.id}
|
||||
title={page.matter.title}
|
||||
description={page.matter.description ?? 'No Description'}
|
||||
href={tab.getPageUrl(page.slugs)}
|
||||
href={page.url}
|
||||
/>
|
||||
))}
|
||||
</Cards>
|
||||
|
||||
@@ -12,7 +12,7 @@ const frontmatterSchema = defaultValidators.frontmatter.extend({
|
||||
index: z.boolean().default(false)
|
||||
})
|
||||
|
||||
const slugs = (file: FileInfo) =>
|
||||
const getSlugs = (file: FileInfo) =>
|
||||
file.flattenedPath
|
||||
.split('/')
|
||||
.filter(p => !['index', ''].includes(p))
|
||||
@@ -22,7 +22,7 @@ export const tabs: Record<string, Utils> = {
|
||||
ui: fromMap(map, {
|
||||
rootDir: 'ui',
|
||||
baseUrl: '/docs/ui',
|
||||
slugs,
|
||||
getSlugs,
|
||||
validate: {
|
||||
frontmatter: frontmatterSchema
|
||||
}
|
||||
@@ -30,7 +30,7 @@ export const tabs: Record<string, Utils> = {
|
||||
headless: fromMap(map, {
|
||||
rootDir: 'headless',
|
||||
baseUrl: '/docs/headless',
|
||||
slugs,
|
||||
getSlugs,
|
||||
validate: {
|
||||
frontmatter: frontmatterSchema
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export const tabs: Record<string, Utils> = {
|
||||
mdx: fromMap(map, {
|
||||
rootDir: 'mdx',
|
||||
baseUrl: '/docs/mdx',
|
||||
slugs,
|
||||
getSlugs,
|
||||
validate: {
|
||||
frontmatter: frontmatterSchema
|
||||
}
|
||||
@@ -80,7 +80,7 @@ if (
|
||||
id: page.file.id,
|
||||
title: page.matter.title,
|
||||
description: page.matter.description,
|
||||
url: tab.getPageUrl(page.slugs, page.file.locale),
|
||||
url: page.url,
|
||||
structuredData: page.data.structuredData
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getPageUrl } from '@/app/source'
|
||||
import { allDocs } from 'contentlayer/generated'
|
||||
import { createSearchAPI } from 'next-docs-zeta/search/server'
|
||||
|
||||
@@ -7,7 +6,7 @@ export const { GET } = createSearchAPI('advanced', {
|
||||
id: page._id,
|
||||
title: page.title,
|
||||
content: page.body.raw,
|
||||
url: getPageUrl(page.slug),
|
||||
url: page.url,
|
||||
structuredData: page.structuredData,
|
||||
tag: ''
|
||||
})),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPage, getPageUrl, tree } from '@/app/source'
|
||||
import { getPage, tree } from '@/app/source'
|
||||
import { allDocs } from 'contentlayer/generated'
|
||||
import type { Metadata } from 'next'
|
||||
import { MDXContent } from 'next-docs-ui/mdx'
|
||||
@@ -19,7 +19,7 @@ export default async function Page({
|
||||
}
|
||||
|
||||
const toc = await getTableOfContents(page.body.raw)
|
||||
const neighbour = findNeighbour(tree, getPageUrl(params.slug))
|
||||
const neighbour = findNeighbour(tree, page.url)
|
||||
|
||||
return (
|
||||
<DocsPage toc={toc} footer={neighbour}>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { allDocs, allMeta } from 'contentlayer/generated'
|
||||
import { createContentlayer } from 'next-docs-zeta/contentlayer'
|
||||
|
||||
export const { tree, getPage, getPageUrl } = createContentlayer(
|
||||
allMeta,
|
||||
allDocs
|
||||
)
|
||||
export const { tree, getPage } = createContentlayer(allMeta, allDocs)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageUrl, pages } from '@/app/source'
|
||||
import { pages } from '@/app/source'
|
||||
import { createSearchAPI } from 'next-docs-zeta/search/server'
|
||||
|
||||
export const { GET } = createSearchAPI('advanced', {
|
||||
@@ -6,6 +6,6 @@ export const { GET } = createSearchAPI('advanced', {
|
||||
title: page.matter.title,
|
||||
structuredData: page.data.structuredData,
|
||||
id: page.file.id,
|
||||
url: getPageUrl(page.slugs)
|
||||
url: page.url
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPage, getPageUrl, pages, tree } from '@/app/source'
|
||||
import { getPage, pages, tree } from '@/app/source'
|
||||
import type { Metadata } from 'next'
|
||||
import { MDXContent } from 'next-docs-ui/mdx'
|
||||
import { DocsPage } from 'next-docs-ui/page'
|
||||
@@ -17,7 +17,7 @@ export default async function Page({
|
||||
}
|
||||
|
||||
const MDX = page.data.default
|
||||
const neighbour = findNeighbour(tree, getPageUrl(params.slug))
|
||||
const neighbour = findNeighbour(tree, page.url)
|
||||
|
||||
return (
|
||||
<DocsPage toc={page.data.toc} footer={neighbour}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { map } from '@/_map'
|
||||
import { fromMap } from 'next-docs-mdx/map'
|
||||
|
||||
export const { getPage, getPageUrl, pages, tree } = fromMap(map)
|
||||
export const { getPage, pages, tree } = fromMap(map)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getPageUrl } from '@/app/source'
|
||||
import { allDocs } from 'contentlayer/generated'
|
||||
import { createSearchAPI } from 'next-docs-zeta/search/server'
|
||||
|
||||
@@ -6,6 +5,6 @@ export const { GET } = createSearchAPI('simple', {
|
||||
indexes: allDocs.map(page => ({
|
||||
title: page.title,
|
||||
content: page.body.raw,
|
||||
url: getPageUrl(page.slug)
|
||||
url: page.url
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPage, getPageUrl, tree } from '@/app/source'
|
||||
import { getPage, tree } from '@/app/source'
|
||||
import { allDocs } from 'contentlayer/generated'
|
||||
import type { Metadata } from 'next'
|
||||
import { MDXContent } from 'next-docs-ui/mdx'
|
||||
@@ -19,7 +19,7 @@ export default async function Page({
|
||||
}
|
||||
|
||||
const toc = await getTableOfContents(page.body.raw)
|
||||
const neighbour = findNeighbour(tree, getPageUrl(params.slug))
|
||||
const neighbour = findNeighbour(tree, page.url)
|
||||
|
||||
return (
|
||||
<DocsPage toc={toc} footer={neighbour}>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { allDocs, allMeta } from 'contentlayer/generated'
|
||||
import { createContentlayer } from 'next-docs-zeta/contentlayer'
|
||||
|
||||
export const { getPage, getPageUrl, tree } = createContentlayer(
|
||||
allMeta,
|
||||
allDocs
|
||||
)
|
||||
export const { getPage, tree } = createContentlayer(allMeta, allDocs)
|
||||
|
||||
@@ -6,17 +6,12 @@ import type { ReactElement } from 'react'
|
||||
import type { ResolvedFiles } from './resolve-files'
|
||||
|
||||
export type BuilderOptions = {
|
||||
/**
|
||||
* Get url from slugs and locale, override the default getUrl function
|
||||
*/
|
||||
getUrl: (slugs: string[], locale?: string) => string
|
||||
|
||||
resolveIcon?: (icon: string) => ReactElement | undefined
|
||||
}
|
||||
|
||||
export function getPageTreeBuilder(
|
||||
{ metas, pages }: ResolvedFiles,
|
||||
{ getUrl, resolveIcon = () => undefined }: BuilderOptions
|
||||
{ resolveIcon = () => undefined }: BuilderOptions
|
||||
): PageTreeBuilder {
|
||||
return createPageTreeBuilder({
|
||||
metas: metas.map(meta => ({
|
||||
@@ -28,7 +23,7 @@ export function getPageTreeBuilder(
|
||||
pages: pages.map(page => ({
|
||||
file: page.file,
|
||||
title: page.matter.title,
|
||||
url: getUrl(page.slugs, page.file.locale),
|
||||
url: page.url,
|
||||
icon: page.matter.icon
|
||||
})),
|
||||
resolveIcon
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { BuildPageTreeOptions, PageTree } from 'next-docs-zeta/server'
|
||||
import {
|
||||
createGetUrl,
|
||||
type BuildPageTreeOptions,
|
||||
type PageTree
|
||||
} from 'next-docs-zeta/server'
|
||||
import { getPageTreeBuilder, type BuilderOptions } from './build-tree'
|
||||
import { createPageUtils, type PageUtils } from './page-utils'
|
||||
import {
|
||||
@@ -25,7 +29,8 @@ type UtilsOptions<Langs extends string[] | undefined> = {
|
||||
|
||||
pageTreeOptions: BuildPageTreeOptions
|
||||
|
||||
slugs: ResolveOptions['slugs']
|
||||
getSlugs: ResolveOptions['getSlugs']
|
||||
getUrl?: ResolveOptions['getUrl']
|
||||
validate: ResolveOptions['validate']
|
||||
} & BuilderOptions
|
||||
|
||||
@@ -44,8 +49,8 @@ function fromMap<Langs extends string[] | undefined = undefined>(
|
||||
{
|
||||
baseUrl = '/docs',
|
||||
rootDir = '',
|
||||
slugs,
|
||||
getUrl,
|
||||
getSlugs,
|
||||
getUrl = createGetUrl(baseUrl),
|
||||
resolveIcon,
|
||||
pageTreeOptions = { root: rootDir },
|
||||
languages,
|
||||
@@ -55,15 +60,14 @@ function fromMap<Langs extends string[] | undefined = undefined>(
|
||||
const resolved = resolveFiles({
|
||||
map,
|
||||
rootDir,
|
||||
slugs,
|
||||
getSlugs,
|
||||
getUrl,
|
||||
validate
|
||||
})
|
||||
|
||||
const pageUtils = createPageUtils(resolved, baseUrl, languages ?? [])
|
||||
if (getUrl) pageUtils.getPageUrl = getUrl
|
||||
const pageUtils = createPageUtils(resolved, languages ?? [])
|
||||
|
||||
const builder = getPageTreeBuilder(resolved, {
|
||||
getUrl: pageUtils.getPageUrl,
|
||||
resolveIcon
|
||||
})
|
||||
|
||||
|
||||
@@ -4,12 +4,10 @@ import type { Page } from './types'
|
||||
export type PageUtils = {
|
||||
getPages(locale?: string): Page[]
|
||||
getPage(slugs: string[] | undefined, locale?: string): Page | null
|
||||
getPageUrl(slugs: string[] | undefined, locale?: string): string
|
||||
}
|
||||
|
||||
export function createPageUtils(
|
||||
{ pages }: ResolvedFiles,
|
||||
baseUrl: string,
|
||||
languages: string[]
|
||||
): PageUtils {
|
||||
const pageMap = new Map<string, Page>()
|
||||
@@ -42,28 +40,12 @@ export function createPageUtils(
|
||||
getPages(locale = '') {
|
||||
return i18nMap.get(locale)!
|
||||
},
|
||||
getPageUrl(slugs = [], locale) {
|
||||
return joinPaths(baseUrl, ...slugs, locale ?? null)
|
||||
},
|
||||
getPage(slugs, locale) {
|
||||
return getPage(pages, slugs, locale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join paths with leading slash
|
||||
*/
|
||||
function joinPaths(...paths: (string | null)[]): string {
|
||||
const relative = paths
|
||||
// filter slashes & empty
|
||||
.filter(path => path != null && path.length !== 0 && path !== '/')
|
||||
.join('/')
|
||||
|
||||
if (relative.startsWith('/')) return relative
|
||||
return '/' + relative
|
||||
}
|
||||
|
||||
function getPage(
|
||||
pages: Page[],
|
||||
slugs: string[] | undefined = [],
|
||||
|
||||
@@ -29,12 +29,17 @@ export type ResolveOptions = {
|
||||
/**
|
||||
* Generate slugs from file info
|
||||
*/
|
||||
slugs?: (file: FileInfo) => string[]
|
||||
getSlugs?: (file: FileInfo) => string[]
|
||||
|
||||
/**
|
||||
* Check frontmatter/meta objects, transform allowed
|
||||
*/
|
||||
validate?: ValidateOptions
|
||||
|
||||
/**
|
||||
* Get url from slugs and locale, override the default getUrl function
|
||||
*/
|
||||
getUrl: (slugs: string[], locale?: string) => string
|
||||
}
|
||||
|
||||
type ValidateOptions = Partial<{
|
||||
@@ -86,7 +91,8 @@ function parse<T>(schema: AnyZodObject, object: unknown, errorName: string): T {
|
||||
|
||||
export function resolveFiles({
|
||||
map,
|
||||
slugs = pathToSlugs,
|
||||
getSlugs = pathToSlugs,
|
||||
getUrl,
|
||||
rootDir = '',
|
||||
validate = defaultValidators
|
||||
}: ResolveOptions): ResolvedFiles {
|
||||
@@ -119,9 +125,12 @@ export function resolveFiles({
|
||||
|
||||
if (pageTypes.includes(file.type)) {
|
||||
const data = v as MDXExport
|
||||
const slugs = getSlugs(file)
|
||||
|
||||
const page: Page = {
|
||||
file,
|
||||
slugs: slugs(file),
|
||||
slugs,
|
||||
url: getUrl(slugs, file.locale),
|
||||
matter: parse<Frontmatter>(
|
||||
validate.frontmatter ?? defaultValidators.frontmatter,
|
||||
data.frontmatter,
|
||||
|
||||
@@ -66,6 +66,7 @@ export interface Meta {
|
||||
|
||||
export interface Page {
|
||||
file: FileInfo
|
||||
url: string
|
||||
slugs: string[]
|
||||
data: MDXExport
|
||||
matter: Frontmatter
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
|
||||
import { rehypeImgSize, rehypeNextDocs, remarkGfm } from '@/mdx-plugins'
|
||||
import type { RehypeNextDocsOptions } from '@/mdx-plugins/rehype-next-docs'
|
||||
import type { Args, ComputedFields, FieldDef } from 'contentlayer/source-files'
|
||||
import { createGetUrl } from '@/server/utils'
|
||||
import type {
|
||||
Args,
|
||||
ComputedFields,
|
||||
FieldDef,
|
||||
LocalDocument
|
||||
} from 'contentlayer/source-files'
|
||||
import { defineDocumentType } from 'contentlayer/source-files'
|
||||
import type { Options as ImgSizeOptions } from 'rehype-img-size'
|
||||
|
||||
@@ -33,7 +39,7 @@ function removePattern(path: string, pattern: string): string {
|
||||
return removeSlash(path.slice(pattern.length))
|
||||
}
|
||||
|
||||
type Options = {
|
||||
export type Options = Partial<{
|
||||
/**
|
||||
* Where the docs files located
|
||||
* @default "docs"
|
||||
@@ -51,26 +57,43 @@ type Options = {
|
||||
*/
|
||||
imgDirPath: string
|
||||
|
||||
baseUrl: string
|
||||
|
||||
/**
|
||||
* Get url from slugs and locale, override the default getUrl function
|
||||
*/
|
||||
getUrl: (slugs: string[], locale?: string) => string
|
||||
|
||||
pluginOptions: RehypeNextDocsOptions
|
||||
|
||||
docFields: Record<string, FieldDef>
|
||||
docsComputedFields: ComputedFields<'Docs'>
|
||||
metaFields: Record<string, FieldDef>
|
||||
metaComputedFields: ComputedFields<'Meta'>
|
||||
}
|
||||
}>
|
||||
|
||||
export function createConfig(options: Partial<Options> = {}): Args {
|
||||
export function createConfig(options: Options = {}): Args {
|
||||
const {
|
||||
docsPattern = 'docs',
|
||||
contentDirPath = 'content',
|
||||
imgDirPath = './public',
|
||||
docFields,
|
||||
metaFields,
|
||||
baseUrl = '/docs',
|
||||
getUrl = createGetUrl(baseUrl),
|
||||
docsComputedFields,
|
||||
pluginOptions,
|
||||
metaComputedFields
|
||||
} = options
|
||||
|
||||
function getSlugs(doc: LocalDocument): string {
|
||||
return removePattern(doc._raw.flattenedPath.split('.')[0], docsPattern)
|
||||
}
|
||||
|
||||
function getLocale(doc: LocalDocument): string {
|
||||
return doc._raw.flattenedPath.split('.')[1]
|
||||
}
|
||||
|
||||
const Docs = defineDocumentType(() => ({
|
||||
name: 'Docs',
|
||||
filePathPattern: `${docsPattern}/**/*.mdx`,
|
||||
@@ -95,17 +118,16 @@ export function createConfig(options: Partial<Options> = {}): Args {
|
||||
computedFields: {
|
||||
locale: {
|
||||
type: 'string',
|
||||
resolve: post => {
|
||||
return post._raw.flattenedPath.split('.')[1]
|
||||
}
|
||||
resolve: post => getLocale(post)
|
||||
},
|
||||
slug: {
|
||||
type: 'string',
|
||||
resolve: post => getSlugs(post)
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
resolve: post => {
|
||||
return removePattern(
|
||||
post._raw.flattenedPath.split('.')[0],
|
||||
docsPattern
|
||||
)
|
||||
return getUrl(getSlugs(post).split('/'), getLocale(post))
|
||||
}
|
||||
},
|
||||
...docsComputedFields
|
||||
|
||||
@@ -5,17 +5,10 @@ import {
|
||||
import type { RawDocumentData } from 'contentlayer/source-files'
|
||||
import type { ReactElement } from 'react'
|
||||
import type { DocsPageBase, MetaPageBase, PagesContext } from './types'
|
||||
import { getPageUrl } from './utils'
|
||||
|
||||
export type ContextOptions = {
|
||||
languages: string[]
|
||||
|
||||
baseUrl: string
|
||||
/**
|
||||
* Get url from slugs and locale, override the default getUrl function
|
||||
*/
|
||||
getUrl: (slugs: string[], locale?: string) => string
|
||||
|
||||
resolveIcon: (icon: string) => ReactElement | undefined
|
||||
}
|
||||
|
||||
@@ -24,8 +17,6 @@ export function loadContext<Docs extends DocsPageBase>(
|
||||
docsPages: Docs[],
|
||||
{
|
||||
languages = [],
|
||||
baseUrl = '/docs',
|
||||
getUrl = (slugs, locale) => getPageUrl(slugs, baseUrl, locale),
|
||||
resolveIcon = () => undefined
|
||||
}: Partial<ContextOptions> = {}
|
||||
): PagesContext<Docs> {
|
||||
@@ -33,7 +24,7 @@ export function loadContext<Docs extends DocsPageBase>(
|
||||
pages: docsPages.map(page => ({
|
||||
file: getFileData(page._raw, page.locale),
|
||||
title: page.title,
|
||||
url: getUrl(page.slug.split('/'), page.locale),
|
||||
url: page.url,
|
||||
icon: page.icon
|
||||
})),
|
||||
metas: metaPages.map(meta => ({
|
||||
@@ -47,8 +38,7 @@ export function loadContext<Docs extends DocsPageBase>(
|
||||
|
||||
return {
|
||||
builder,
|
||||
i18nMap: getI18nPages(docsPages, languages),
|
||||
getUrl
|
||||
i18nMap: getI18nPages(docsPages, languages)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export type DocsPageBase = {
|
||||
description?: string | undefined
|
||||
locale?: string
|
||||
icon?: string
|
||||
url: string
|
||||
slug: string
|
||||
}
|
||||
|
||||
@@ -35,5 +36,4 @@ export type PagesContext<Docs extends DocsPageBase = DocsPageBase> = {
|
||||
* Language -> Page[]
|
||||
*/
|
||||
i18nMap: Map<string, Docs[]>
|
||||
getUrl: (slugs: string[], locale?: string) => string
|
||||
}
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
import type { DocsPageBase, PagesContext } from './types'
|
||||
|
||||
export function getPageUrl(slug: string[], baseUrl: string, locale?: string) {
|
||||
const url = [baseUrl, locale, ...slug]
|
||||
.filter(segment => segment != null && segment.length > 0)
|
||||
.join('/')
|
||||
|
||||
if (url.startsWith('//')) {
|
||||
return url.slice(1)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
export type ContentlayerUtils<Docs extends DocsPageBase> = {
|
||||
/**
|
||||
* Get list of pages from language
|
||||
@@ -24,11 +12,6 @@ export type ContentlayerUtils<Docs extends DocsPageBase> = {
|
||||
* @param language If empty, the default language will be used
|
||||
*/
|
||||
getPage: (slugs: string[] | undefined, language?: string) => Docs | undefined
|
||||
|
||||
getPageUrl: (
|
||||
slugs: string | string[] | undefined,
|
||||
language?: string
|
||||
) => string
|
||||
}
|
||||
|
||||
export function createUtils<Docs extends DocsPageBase>(
|
||||
@@ -42,11 +25,6 @@ export function createUtils<Docs extends DocsPageBase>(
|
||||
const path = slugs.join('/')
|
||||
|
||||
return context.i18nMap.get(language)?.find(page => page.slug === path)
|
||||
},
|
||||
getPageUrl(slug = [], language) {
|
||||
const slugs = typeof slug === 'string' ? slug.split('/') : slug
|
||||
|
||||
return context.getUrl(slugs, language)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export * from './server/get-toc'
|
||||
export * from './server/types'
|
||||
export { getTableOfContentsFromPortableText } from './server/get-toc-sanity'
|
||||
export * from './server/tree-utils'
|
||||
export * from './server/utils'
|
||||
export * from './server/git-api'
|
||||
export {
|
||||
createPageTreeBuilder,
|
||||
|
||||
@@ -6,26 +6,6 @@ type Response = {
|
||||
}
|
||||
}[]
|
||||
|
||||
/**
|
||||
* @param repo The repository, example: "fuma/next-docs"
|
||||
* @param path the path to file
|
||||
* @deprecated use `getGithubLastEdit` instead
|
||||
*/
|
||||
export async function getGitLastEditTime(
|
||||
repo: string,
|
||||
path: string,
|
||||
customParams: Record<string, string> = {},
|
||||
init: RequestInit = {}
|
||||
): Promise<Date | null> {
|
||||
return getGithubLastEdit({
|
||||
repo: repo.split('/')[1],
|
||||
owner: repo.split('/')[0],
|
||||
path,
|
||||
options: init,
|
||||
params: customParams
|
||||
})
|
||||
}
|
||||
|
||||
export type GetGithubLastCommitOptions = {
|
||||
/**
|
||||
* Repository name, like "next-docs"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ReactElement } from 'react'
|
||||
import type { FileNode, FolderNode, PageTree, TreeNode } from './types'
|
||||
import { joinPaths, splitPath } from './utils'
|
||||
|
||||
export type AbstractFile = {
|
||||
locale?: string
|
||||
@@ -318,41 +319,3 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import type { FileNode, PageTree, TreeNode } from './types'
|
||||
|
||||
/**
|
||||
* Flatten tree to an array of page nodes
|
||||
*/
|
||||
export function flattenTree(tree: TreeNode[]): FileNode[] {
|
||||
return tree.flatMap(node => {
|
||||
if (node.type === 'separator') return []
|
||||
if (node.type === 'folder') {
|
||||
const child = flattenTree(node.children)
|
||||
|
||||
if (node.index) return [node.index, ...child]
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
return [node]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get neighbours of a page, useful for implementing "previous & next" buttons
|
||||
*/
|
||||
export function findNeighbour(
|
||||
tree: PageTree,
|
||||
url: string
|
||||
): {
|
||||
previous?: FileNode
|
||||
next?: FileNode
|
||||
} {
|
||||
const list = flattenTree(tree.children)
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].url === url) {
|
||||
return {
|
||||
next: list[i + 1],
|
||||
previous: list[i - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import type { FileNode, PageTree, TreeNode } from './types'
|
||||
|
||||
/**
|
||||
* Flatten tree to an array of page nodes
|
||||
*/
|
||||
export function flattenTree(tree: TreeNode[]): FileNode[] {
|
||||
return tree.flatMap(node => {
|
||||
if (node.type === 'separator') return []
|
||||
if (node.type === 'folder') {
|
||||
const child = flattenTree(node.children)
|
||||
|
||||
if (node.index) return [node.index, ...child]
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
return [node]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get neighbours of a page, useful for implementing "previous & next" buttons
|
||||
*/
|
||||
export function findNeighbour(
|
||||
tree: PageTree,
|
||||
url: string
|
||||
): {
|
||||
previous?: FileNode
|
||||
next?: FileNode
|
||||
} {
|
||||
const list = flattenTree(tree.children)
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].url === url) {
|
||||
return {
|
||||
next: list[i + 1],
|
||||
previous: list[i - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 whether to 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
|
||||
}
|
||||
}
|
||||
export function createGetUrl(baseUrl: string): {
|
||||
(slugs: string[], locale?: string): string
|
||||
} {
|
||||
return (slugs, locale) => {
|
||||
const paths = [baseUrl, ...slugs]
|
||||
if (locale) paths.push(locale)
|
||||
|
||||
return joinPaths(paths, 'leading')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user