diff --git a/examples/basic/content/docs/features.mdx b/examples/basic/content/docs/features.mdx index 313e341..6c31410 100644 --- a/examples/basic/content/docs/features.mdx +++ b/examples/basic/content/docs/features.mdx @@ -2,6 +2,7 @@ title: Features description: Overview of Chronicle features order: 2 +draft: true --- # Features diff --git a/packages/chronicle/src/lib/source.ts b/packages/chronicle/src/lib/source.ts index 06a1b01..9819745 100644 --- a/packages/chronicle/src/lib/source.ts +++ b/packages/chronicle/src/lib/source.ts @@ -174,17 +174,31 @@ function sortTreeByOrder(tree: Root, pages: { url: string; data: unknown }[], me return { ...tree, children: sortNodes(tree.children, pageOrderMap, folderOrderMap) }; } +function filterDraftsFromTree(tree: Root, draftUrls: Set): Root { + function filterNodes(nodes: Node[]): Node[] { + return nodes + .filter(n => n.type !== NodeType.Page || !draftUrls.has(n.url)) + .map(n => n.type === NodeType.Folder + ? { ...n, children: filterNodes(n.children) } as Folder + : n + ); + } + return { ...tree, children: filterNodes(tree.children) }; +} + export async function getPageTree(): Promise { if (cachedTree) return cachedTree; const s = await getSource(); const metaFiles = buildFiles().filter(f => f.type === 'meta') as { path: string; data: Record }[]; - cachedTree = sortTreeByOrder(s.pageTree as Root, s.getPages(), metaFiles); + const sorted = sortTreeByOrder(s.pageTree as Root, s.getPages(), metaFiles); + const draftUrls = new Set(s.getPages().filter(p => isDraft(p)).map(p => p.url)); + cachedTree = draftUrls.size > 0 ? filterDraftsFromTree(sorted, draftUrls) : sorted; return cachedTree; } export async function getPages() { const s = await getSource(); - return s.getPages(); + return s.getPages().filter(p => !isDraft(p)); } export async function getPage(slugs?: string[]) { @@ -254,10 +268,15 @@ export function extractFrontmatter(page: { data: unknown }, fallbackTitle?: stri order: d.order as number | undefined, icon: d.icon as string | undefined, lastModified: d.lastModified as string | undefined, + draft: d.draft as boolean | undefined, _readingTime: d._readingTime as number | undefined, }; } +export function isDraft(page: { data: unknown }): boolean { + return (page.data as Record).draft === true; +} + export function getRelativePath(page: { data: unknown }): string { return ((page.data as Record)._relativePath as string) ?? ''; } diff --git a/packages/chronicle/src/server/api/page.ts b/packages/chronicle/src/server/api/page.ts index 32f792e..7c5ba9b 100644 --- a/packages/chronicle/src/server/api/page.ts +++ b/packages/chronicle/src/server/api/page.ts @@ -1,12 +1,12 @@ import { defineHandler, HTTPError } from 'nitro'; -import { getPage, getPageNav, extractFrontmatter, getRelativePath, getOriginalPath } from '@/lib/source'; +import { getPage, getPageNav, extractFrontmatter, getRelativePath, getOriginalPath, isDraft } from '@/lib/source'; export default defineHandler(async event => { const slugParam = event.url.searchParams.get('slug') ?? ''; const slug = slugParam ? slugParam.split(',').filter(Boolean) : []; const page = await getPage(slug); - if (!page) { + if (!page || isDraft(page)) { throw new HTTPError({ status: 404, message: 'Page not found' }); } diff --git a/packages/chronicle/src/server/entry-server.tsx b/packages/chronicle/src/server/entry-server.tsx index 2ead14b..d8220bb 100644 --- a/packages/chronicle/src/server/entry-server.tsx +++ b/packages/chronicle/src/server/entry-server.tsx @@ -9,7 +9,7 @@ import { getApiConfigsForVersion, loadConfig } from '@/lib/config'; import { loadApiSpecs } from '@/lib/openapi'; import { PageProvider } from '@/lib/page-context'; import { resolveRoute, RouteType } from '@/lib/route-resolver'; -import { getPageTree, getPage, getPageNav, loadPageModule, extractFrontmatter, getRelativePath, getOriginalPath } from '@/lib/source'; +import { getPageTree, getPage, getPageNav, loadPageModule, extractFrontmatter, getRelativePath, getOriginalPath, isDraft } from '@/lib/source'; import { getFirstApiUrl } from '@/lib/api-routes'; import { StatusCodes } from 'http-status-codes'; import { resolveDocsRedirect } from '@/lib/tree-utils'; @@ -44,10 +44,12 @@ export default { : []; const apiSpecs = apiConfigs.length ? await loadApiSpecs(apiConfigs) : []; - const [tree, page] = await Promise.all([ + const [tree, rawPage] = await Promise.all([ getPageTree(), route.type === RouteType.DocsPage ? getPage(route.slug) : Promise.resolve(null), ]); + const page = rawPage && isDraft(rawPage) ? null : rawPage; + // SSR redirects for index pages if (route.type === RouteType.ApiIndex) { const firstUrl = getFirstApiUrl(apiSpecs); diff --git a/packages/chronicle/src/types/content.ts b/packages/chronicle/src/types/content.ts index ae05a47..e268ba2 100644 --- a/packages/chronicle/src/types/content.ts +++ b/packages/chronicle/src/types/content.ts @@ -10,6 +10,7 @@ export interface Frontmatter { order?: number icon?: string lastModified?: string + draft?: boolean _readingTime?: number }