From 0f4795fca765f3418212f677909f57ca874707ec Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Sat, 30 Jul 2022 16:48:10 +0200 Subject: [PATCH] [nextra]: refactor `loaders.ts`, provide type for `PageOpts` in loader (#579) * chore(nextra/blog/docs): provide types for PageOpts in loader * yet more readable * move `shallow cloned` console messages outside of loader --- .changeset/strange-fishes-marry.md | 7 + .../__test__/__fixture__/pageMap.ts | 8 +- .../nextra-theme-blog/src/blog-context.tsx | 7 +- packages/nextra-theme-blog/src/constants.tsx | 7 + packages/nextra-theme-blog/src/index.tsx | 31 ++--- packages/nextra-theme-blog/src/types.ts | 28 ++-- .../nextra-theme-blog/src/utils/collect.ts | 3 +- packages/nextra-theme-docs/src/config.ts | 12 +- packages/nextra-theme-docs/src/head.tsx | 4 +- packages/nextra-theme-docs/src/index.tsx | 59 ++++---- packages/nextra-theme-docs/src/types.ts | 5 +- packages/nextra/src/compile.ts | 9 +- packages/nextra/src/content-dump.ts | 10 +- packages/nextra/src/loader.ts | 128 ++++++++---------- .../nextra/src/mdx-plugins/rehype-handler.js | 8 +- packages/nextra/src/mdx-plugins/remark.ts | 16 +-- packages/nextra/src/types.ts | 19 ++- 17 files changed, 170 insertions(+), 191 deletions(-) create mode 100644 .changeset/strange-fishes-marry.md create mode 100644 packages/nextra-theme-blog/src/constants.tsx diff --git a/.changeset/strange-fishes-marry.md b/.changeset/strange-fishes-marry.md new file mode 100644 index 0000000000..4dd3be64c4 --- /dev/null +++ b/.changeset/strange-fishes-marry.md @@ -0,0 +1,7 @@ +--- +'nextra': patch +'nextra-theme-blog': patch +'nextra-theme-docs': patch +--- + +chore(nextra/blog/docs): provide types for PageOpts in loader diff --git a/packages/nextra-theme-blog/__test__/__fixture__/pageMap.ts b/packages/nextra-theme-blog/__test__/__fixture__/pageMap.ts index 43ddddb959..52756d2096 100644 --- a/packages/nextra-theme-blog/__test__/__fixture__/pageMap.ts +++ b/packages/nextra-theme-blog/__test__/__fixture__/pageMap.ts @@ -1,5 +1,5 @@ -import { BlogPageOpt } from '../../src/types' -export const indexOpts: BlogPageOpt = { +import { BlogPageOpts } from '../../src/types' +export const indexOpts: BlogPageOpts = { filename: 'index.mdx', route: '/', meta: { @@ -102,7 +102,7 @@ export const indexOpts: BlogPageOpt = { ] } -export const postsOpts: BlogPageOpt = { +export const postsOpts: BlogPageOpts = { filename: 'index.md', route: '/posts', meta: { @@ -205,7 +205,7 @@ export const postsOpts: BlogPageOpt = { ] } -export const articleOpts: BlogPageOpt = { +export const articleOpts: BlogPageOpts = { filename: 'aaron-swartz-a-programmable-web.mdx', route: '/posts/aaron-swartz-a-programmable-web', meta: { diff --git a/packages/nextra-theme-blog/src/blog-context.tsx b/packages/nextra-theme-blog/src/blog-context.tsx index 4ef9cd5f4b..2909389f23 100644 --- a/packages/nextra-theme-blog/src/blog-context.tsx +++ b/packages/nextra-theme-blog/src/blog-context.tsx @@ -1,15 +1,14 @@ -import React, { useContext } from 'react' -import { createContext, PropsWithChildren } from 'react' +import React, { ReactElement, useContext, createContext, ReactNode } from 'react' import { LayoutProps } from './types' import { isValidDate } from './utils/date' const BlogContext = createContext(null) -export const BlogProvider: React.FC> = ({ +export const BlogProvider = ({ config, children, opts -}) => { +}: LayoutProps & { children: ReactNode }): ReactElement => { const { date } = opts.meta if (date && !isValidDate(date)) { diff --git a/packages/nextra-theme-blog/src/constants.tsx b/packages/nextra-theme-blog/src/constants.tsx new file mode 100644 index 0000000000..62dca81b5f --- /dev/null +++ b/packages/nextra-theme-blog/src/constants.tsx @@ -0,0 +1,7 @@ +import React from 'react' +import { NextraBlogTheme } from './types' + +export const DEFAULT_CONFIG: NextraBlogTheme = { + readMore: 'Read More →', + footer: CC BY-NC 4.0 2022 © Shu Ding. +} diff --git a/packages/nextra-theme-blog/src/index.tsx b/packages/nextra-theme-blog/src/index.tsx index 5b85d1e9cc..f1bdfaa7b2 100644 --- a/packages/nextra-theme-blog/src/index.tsx +++ b/packages/nextra-theme-blog/src/index.tsx @@ -1,11 +1,12 @@ -import React, { PropsWithChildren, ReactNode } from 'react' +import React, { ReactElement, ReactNode } from 'react' import { ThemeProvider } from 'next-themes' -import type { PageOpt } from 'nextra' +import type { PageOpts } from 'nextra' import type { LayoutProps, NextraBlogTheme } from './types' import { BlogProvider } from './blog-context' import { ArticleLayout } from './article-layout' import { PostsLayout } from './posts-layout' import { PageLayout } from './page-layout' +import { DEFAULT_CONFIG } from './constants' const layoutMap = { post: ArticleLayout, @@ -14,11 +15,11 @@ const layoutMap = { tag: PostsLayout } -const BlogLayout: React.FC> = ({ +const BlogLayout = ({ config, children, opts -}) => { +}: LayoutProps & { children: ReactNode }): ReactElement => { const type = opts.meta.type || 'post' const Layout = layoutMap[type] if (!Layout) { @@ -33,27 +34,17 @@ const BlogLayout: React.FC> = ({ ) } -const createLayout = (opts: PageOpt, _config: NextraBlogTheme) => { - const config: NextraBlogTheme = Object.assign( - { - readMore: 'Read More →', - footer: ( - CC BY-NC 4.0 2022 © Shu Ding. - ), - titleSuffix: null, - postFooter: null - }, - _config - ) - const Page = ({ children }: { children: ReactNode }) => children - const Layout = (page: ReactNode) => ( +const createLayout = (opts: PageOpts, config: NextraBlogTheme) => { + const extendedConfig = { ...DEFAULT_CONFIG, ...config } + + const Page = ({ children }: { children: ReactNode }): ReactNode => children + Page.getLayout = (page: ReactNode): ReactElement => ( - + {page} ) - Page.getLayout = Layout return Page } diff --git a/packages/nextra-theme-blog/src/types.ts b/packages/nextra-theme-blog/src/types.ts index d18b235217..4f2bb5edda 100644 --- a/packages/nextra-theme-blog/src/types.ts +++ b/packages/nextra-theme-blog/src/types.ts @@ -1,4 +1,4 @@ -import { PageOpt } from 'nextra' +import { PageOpts } from 'nextra' export interface NextraBlogTheme { readMore?: string @@ -23,18 +23,22 @@ export interface NextraBlogTheme { name: string }[] } -export interface BlogPageOpt extends PageOpt { - meta: { - title?: string - type?: 'post' | 'page' | 'posts' | 'tag' - tag?: string | string[] - back?: string - date?: string - description?: string - author?: string - } + +export interface BlogPageOpts extends PageOpts { + meta: Meta } + +type Meta = { + title?: string + type?: 'post' | 'page' | 'posts' | 'tag' + tag?: string | string[] + back?: string + date?: string + description?: string + author?: string +} + export interface LayoutProps { config: NextraBlogTheme - opts: BlogPageOpt + opts: BlogPageOpts } diff --git a/packages/nextra-theme-blog/src/utils/collect.ts b/packages/nextra-theme-blog/src/utils/collect.ts index 5e0e3b0aa6..75e2deb759 100644 --- a/packages/nextra-theme-blog/src/utils/collect.ts +++ b/packages/nextra-theme-blog/src/utils/collect.ts @@ -10,8 +10,7 @@ const isPost = (page: PageMapItem) => { if (page.children) return false if (page.name.startsWith('_')) return false return ( - !page.frontMatter || - !page.frontMatter.type || + !page.frontMatter?.type || page.frontMatter.type === 'post' ) } diff --git a/packages/nextra-theme-docs/src/config.ts b/packages/nextra-theme-docs/src/config.ts index b48e55cd12..da77db2f00 100644 --- a/packages/nextra-theme-docs/src/config.ts +++ b/packages/nextra-theme-docs/src/config.ts @@ -1,8 +1,10 @@ -import { PageOpt } from 'nextra' -import React from 'react' +import { PageOpts } from 'nextra' +import { createContext, useContext } from 'react' import { DocsThemeConfig } from './types' + interface Config extends DocsThemeConfig { - unstable_flexsearch?: PageOpt['unstable_flexsearch'] + unstable_flexsearch?: PageOpts['unstable_flexsearch'] } -export const ThemeConfigContext = React.createContext({}) -export const useConfig = () => React.useContext(ThemeConfigContext) + +export const ThemeConfigContext = createContext({}) +export const useConfig = () => useContext(ThemeConfigContext) diff --git a/packages/nextra-theme-docs/src/head.tsx b/packages/nextra-theme-docs/src/head.tsx index 5ad2794da7..4180f6f35a 100644 --- a/packages/nextra-theme-docs/src/head.tsx +++ b/packages/nextra-theme-docs/src/head.tsx @@ -4,12 +4,12 @@ import { useTheme } from 'next-themes' import renderComponent from './utils/render-component' import { useConfig } from './config' -import { Meta } from './types' +import { PageOpts } from 'nextra' interface HeadProps { title: string locale: string - meta: Meta + meta: PageOpts['meta'] } export default function Head({ title, locale, meta }: HeadProps) { diff --git a/packages/nextra-theme-docs/src/index.tsx b/packages/nextra-theme-docs/src/index.tsx index f8add02f52..01dd7330ab 100644 --- a/packages/nextra-theme-docs/src/index.tsx +++ b/packages/nextra-theme-docs/src/index.tsx @@ -1,5 +1,6 @@ import React, { - PropsWithChildren, + ReactElement, + ReactNode, useEffect, useMemo, useRef, @@ -9,7 +10,7 @@ import { useRouter } from 'next/router' import 'focus-visible' import { SkipNavContent } from '@reach/skip-nav' import { ThemeProvider } from 'next-themes' -import { Heading, PageMapItem, PageOpt } from 'nextra' +import { PageMapItem, PageOpts } from 'nextra' import cn from 'classnames' import Head from './head' import Navbar from './navbar' @@ -23,7 +24,7 @@ import defaultConfig from './misc/default.config' import { getFSRoute } from './utils/get-fs-route' import { MenuContext } from './utils/menu-context' import normalizePages from './utils/normalize-pages' -import { DocsThemeConfig, Meta, PageTheme } from './types' +import { DocsThemeConfig, PageTheme } from './types' import './polyfill' import Breadcrumb from './breadcrumb' import renderComponent from './utils/render-component' @@ -47,13 +48,13 @@ if (typeof window !== 'undefined') { } function useDirectoryInfo(pageMap: PageMapItem[]) { - const { locale, defaultLocale, asPath } = useRouter() + const { locale = 'en-US', defaultLocale, asPath } = useRouter() return useMemo(() => { const fsPath = getFSRoute(asPath, locale) return normalizePages({ list: pageMap, - locale: locale ? locale : 'en-US', + locale, defaultLocale, route: fsPath }) @@ -62,19 +63,20 @@ function useDirectoryInfo(pageMap: PageMapItem[]) { interface BodyProps { themeContext: PageTheme - breadcrumb?: React.ReactNode - toc?: React.ReactNode + breadcrumb?: ReactNode + toc?: ReactNode timestamp?: number - navLinks: React.ReactNode + navLinks: ReactNode + children: ReactNode } -const Body: React.FC> = ({ +const Body = ({ themeContext, breadcrumb, navLinks, timestamp, children -}) => { +}: BodyProps): ReactElement => { const config = useConfig() const { locale = 'en-US' } = useRouter() const date = timestamp ? new Date(timestamp) : null @@ -91,7 +93,7 @@ const Body: React.FC> = ({ }, []) return ( - + <> {themeContext.layout === 'full' ? (
@@ -157,19 +159,11 @@ const Body: React.FC> = ({
)} -
+ ) } -interface LayoutProps { - filename: string - pageMap: PageMapItem[] - meta: Meta - titleText: string | null - headings?: Heading[] - timestamp?: number -} -const Content: React.FC> = ({ +const Content = ({ filename, pageMap, meta, @@ -177,7 +171,7 @@ const Content: React.FC> = ({ headings, timestamp, children -}) => { +}: PageOpts & { children: ReactNode }): ReactElement => { const { route, locale = 'en-US' } = useRouter() const config = useConfig() @@ -207,8 +201,6 @@ const Content: React.FC> = ({ const hideSidebar = !themeContext.sidebar || themeContext.layout === 'raw' const hideToc = !themeContext.toc || themeContext.layout === 'raw' - - const headingArr = headings ?? [] return ( > = ({ ) ) : ( )} @@ -286,15 +278,16 @@ const Content: React.FC> = ({ ) } -interface DocsLayoutProps extends PageOpt { - meta: Meta -} -const createLayout = (opts: DocsLayoutProps, config: DocsThemeConfig) => { - const extendedConfig = Object.assign({}, defaultConfig, config, opts) - const nextThemes = extendedConfig.nextThemes || {} - const Page = ({ children }: { children: React.ReactChildren }) => children - Page.getLayout = (page: any) => ( +const createLayout = (opts: PageOpts, config: DocsThemeConfig) => { + const extendedConfig = { + ...defaultConfig, + ...config, + unstable_flexsearch: opts.unstable_flexsearch + } + const nextThemes = extendedConfig.nextThemes || {} + const Page = ({ children }: { children: ReactNode }): ReactNode => children + Page.getLayout = (page: ReactNode): ReactElement => ( diff --git a/packages/nextra/src/compile.ts b/packages/nextra/src/compile.ts index fa3c46108b..0978e6b7cd 100644 --- a/packages/nextra/src/compile.ts +++ b/packages/nextra/src/compile.ts @@ -4,8 +4,8 @@ import remarkGfm from 'remark-gfm' import rehypePrettyCode from 'rehype-pretty-code' import { rehypeMdxTitle } from 'rehype-mdx-title' import { remarkStaticImage } from './mdx-plugins/static-image' -import { remarkHeadings, HeadingMeta } from './mdx-plugins/remark' -import { LoaderOptions } from './types' +import { remarkHeadings } from './mdx-plugins/remark' +import { LoaderOptions, PageOpts } from './types' import structurize from './mdx-plugins/structurize' import { parseMeta, attachMeta } from './mdx-plugins/rehype-handler' import theme from './theme.json' @@ -75,7 +75,10 @@ export async function compileMdx( const result = await compiler.process(source) return { result: String(result), - ...(compiler.data('headingMeta') as HeadingMeta), + ...(compiler.data('headingMeta') as Pick< + PageOpts, + 'headings' | 'hasJsxInH1' + >), structurizedData } } catch (err) { diff --git a/packages/nextra/src/content-dump.ts b/packages/nextra/src/content-dump.ts index cbc76b8c3f..9fc53e7842 100644 --- a/packages/nextra/src/content-dump.ts +++ b/packages/nextra/src/content-dump.ts @@ -26,10 +26,10 @@ try { function initFromCache(filename: string) { if (!cached.has(filename)) { try { - const content = fs.readFileSync(path.join(assetDir, filename)).toString() + const content = fs.readFileSync(path.join(assetDir, filename), 'utf8') cached.set(filename, true) return JSON.parse(content) - } catch (err) { + } catch { cached.set(filename, false) } } @@ -40,20 +40,20 @@ export function addPage({ fileLocale, route, title, - data, + meta, structurizedData }: { fileLocale: string route: string title: string - data: any + meta: Record structurizedData: any }): void { const dataFilename = `nextra-data-${fileLocale}.json` asset[fileLocale] ||= initFromCache(dataFilename) asset[fileLocale][route] = { - title: title || data.title, + title: title || meta.title, data: structurizedData } diff --git a/packages/nextra/src/loader.ts b/packages/nextra/src/loader.ts index 3d00fd8e25..abe34bb2c3 100644 --- a/packages/nextra/src/loader.ts +++ b/packages/nextra/src/loader.ts @@ -1,4 +1,4 @@ -import type { LoaderOptions } from './types' +import type { LoaderOptions, PageOpts } from './types' import path from 'path' import grayMatter from 'gray-matter' @@ -18,13 +18,27 @@ const indexContentEmitted = new Set() const pagesDir = path.resolve(findPagesDir()) -let wasShallowWarningPrinted = false - const [repository, gitRoot] = (function () { try { const repo = Repository.discover(process.cwd()) + if (repo.isShallow()) { + if (process.env.VERCEL) { + console.warn( + '[nextra] The repository is shallow cloned, so the latest modified time will not be presented. Set the VERCEL_DEEP_CLONE=true environment variable to enable deep cloning.' + ) + } else if (process.env.GITHUB_ACTION) { + console.warn( + '[nextra] The repository is shallow cloned, so the latest modified time will not be presented. See https://github.com/actions/checkout#fetch-all-history-for-all-tags-and-branches to fetch all the history.' + ) + } else { + console.warn( + '[nextra] The repository is shallow cloned, so the latest modified time will not be presented.' + ) + } + } // repository.path() returns the `/path/to/repo/.git`, we need the parent directory of it const gitRoot = path.join(repo.path(), '..') + return [repo, gitRoot] } catch (e) { console.warn('[nextra] Init git repository failed', e) @@ -38,7 +52,6 @@ async function loader( ): Promise { context.cacheable(true) - const options = context.getOptions() let { theme, themeConfig, @@ -47,7 +60,12 @@ async function loader( unstable_staticImage, mdxOptions, pageMapCache - } = options + } = context.getOptions() + + // Check if there's a theme provided + if (!theme) { + throw new Error('No Nextra theme found!') + } const { resourcePath } = context if (resourcePath.includes('/pages/api/')) { @@ -57,14 +75,6 @@ async function loader( return '' } - const filename = path.basename(resourcePath) - const fileLocale = parseFileName(filename).locale - - // Check if there's a theme provided - if (!theme) { - throw new Error('No Nextra theme found!') - } - const { items: pageMapResult, fileMap } = IS_PRODUCTION ? pageMapCache.get()! : await collectFiles(pagesDir, '/') @@ -75,12 +85,8 @@ async function loader( context.addMissingDependency(resourcePath) } - const [pageMap, route, title] = getPageMap( - resourcePath, - pageMapResult, - fileMap, - defaultLocale - ) + const filename = path.basename(resourcePath) + const fileLocale = parseFileName(filename).locale for (const [filePath, { name, locale }] of Object.entries(fileMap)) { if (name === 'meta.json' && (!fileLocale || locale === fileLocale)) { @@ -92,18 +98,7 @@ async function loader( context.addContextDependency(pagesDir) // Extract frontMatter information if it exists - const { data, content } = grayMatter(source) - - let layout = theme - let layoutConfig = themeConfig || null - - // Relative path instead of a package name - if (theme.startsWith('.') || theme.startsWith('/')) { - layout = path.resolve(theme) - } - if (layoutConfig) { - layoutConfig = slash(path.resolve(layoutConfig)) - } + const { data: meta, content } = grayMatter(source) if (IS_PRODUCTION && indexContentEmitted.has(filename)) { unstable_flexsearch = false @@ -118,14 +113,20 @@ async function loader( }, resourcePath ) + const [pageMap, route, title] = getPageMap( + resourcePath, + pageMapResult, + fileMap, + defaultLocale + ) if (unstable_flexsearch) { - if (data.searchable !== false) { + if (meta.searchable !== false) { addPage({ fileLocale: fileLocale || DEFAULT_LOCALE, route, title, - data, + meta, structurizedData }) } @@ -133,24 +134,8 @@ async function loader( indexContentEmitted.add(filename) } - let timestamp: number | undefined + let timestamp: PageOpts['timestamp'] if (repository && gitRoot) { - if (repository.isShallow() && !wasShallowWarningPrinted) { - if (process.env.VERCEL) { - console.warn( - '[nextra] The repository is shallow cloned, so the latest modified time will not be presented. Set the VERCEL_DEEP_CLONE=true environment variable to enable deep cloning.' - ) - } else if (process.env.GITHUB_ACTION) { - console.warn( - '[nextra] The repository is shallow cloned, so the latest modified time will not be presented. See https://github.com/actions/checkout#fetch-all-history-for-all-tags-and-branches to fetch all the history.' - ) - } else { - console.warn( - '[nextra] The repository is shallow cloned, so the latest modified time will not be presented.' - ) - } - wasShallowWarningPrinted = true - } try { timestamp = await repository.getFileLatestModifiedDateAsync( path.relative(gitRoot, resourcePath) @@ -160,37 +145,37 @@ async function loader( } } - const layoutConfigImport = layoutConfig - ? `import __nextra_layoutConfig__ from '${layoutConfig}'` - : '' + // Relative path instead of a package name + const layout = + theme.startsWith('.') || theme.startsWith('/') ? path.resolve(theme) : theme + const layoutConfig = themeConfig ? slash(path.resolve(themeConfig)) : '' + const pageOpts: Omit = { + filename: slash(filename), + route: slash(route), + meta, + pageMap, + headings, + hasJsxInH1, + timestamp, + unstable_flexsearch + } return ` -import __nextra_withLayout__ from '${layout}' import { withSSG as __nextra_withSSG__ } from 'nextra/ssg' -${layoutConfigImport} +import __nextra_withLayout__ from '${layout}' +${layoutConfig && `import __nextra_layoutConfig__ from '${layoutConfig}'`} ${result.replace('export default MDXContent;', '')} -const __nextra_pageMap__ = ${JSON.stringify(pageMap)} +const __nextra_pageOpts__ = ${JSON.stringify(pageOpts)} globalThis.__nextra_internal__ = { - pageMap: __nextra_pageMap__, - route: ${JSON.stringify(route)} + pageMap: __nextra_pageOpts__.pageMap, + route: __nextra_pageOpts__.route } const NextraLayout = __nextra_withSSG__(__nextra_withLayout__({ - filename: "${slash(filename)}", - route: "${slash(route)}", - meta: ${JSON.stringify(data)}, - pageMap: __nextra_pageMap__, titleText: typeof titleText === 'string' ? titleText : undefined, - headings: ${JSON.stringify(headings)}, - hasJsxInH1: ${JSON.stringify(hasJsxInH1)}, - ${timestamp ? `timestamp: ${timestamp},\n` : ''} - ${ - unstable_flexsearch - ? `unstable_flexsearch: ${JSON.stringify(unstable_flexsearch)}` - : '' - } + ...__nextra_pageOpts__ }, ${layoutConfig ? '__nextra_layoutConfig__' : 'null'})) function NextraPage(props) { @@ -202,8 +187,7 @@ function NextraPage(props) { } NextraPage.getLayout = NextraLayout.getLayout -export default NextraPage -`.trimStart() +export default NextraPage`.trimStart() } export default function syncLoader( diff --git a/packages/nextra/src/mdx-plugins/rehype-handler.js b/packages/nextra/src/mdx-plugins/rehype-handler.js index c7c8c85663..0476c70327 100644 --- a/packages/nextra/src/mdx-plugins/rehype-handler.js +++ b/packages/nextra/src/mdx-plugins/rehype-handler.js @@ -1,5 +1,4 @@ import Slugger from 'github-slugger' - import { getFlattenedValue } from './remark' function visit(node, tagNames, handler) { @@ -7,9 +6,7 @@ function visit(node, tagNames, handler) { handler(node) return } - if (node.children) { - node.children.forEach(n => visit(n, tagNames, handler)) - } + node.children?.forEach(n => visit(n, tagNames, handler)) } export function parseMeta() { @@ -50,8 +47,7 @@ export function attachMeta() { } } else { // Attach slug - node.properties.id = - node.properties.id || slugger.slug(getFlattenedValue(node)) + node.properties.id ||= slugger.slug(getFlattenedValue(node)) } }) } diff --git a/packages/nextra/src/mdx-plugins/remark.ts b/packages/nextra/src/mdx-plugins/remark.ts index 8f4614fcc4..a62daf9b92 100644 --- a/packages/nextra/src/mdx-plugins/remark.ts +++ b/packages/nextra/src/mdx-plugins/remark.ts @@ -1,10 +1,6 @@ import { Processor } from '@mdx-js/mdx/lib/core' import { Root, Heading, Parent } from 'mdast' - -export interface HeadingMeta { - headings: Heading[] - hasJsxInH1: boolean -} +import { PageOpts } from '../types' function visit( node: any, @@ -14,9 +10,7 @@ function visit( if (tester(node)) { handler(node) } - if (node.children) { - node.children.forEach((n: any) => visit(n, tester, handler)) - } + node.children?.forEach((n: any) => visit(n, tester, handler)) } export function getFlattenedValue(node: Parent): string { @@ -32,7 +26,9 @@ export function getFlattenedValue(node: Parent): string { } export function remarkHeadings(this: Processor) { - const data = this.data() as any + const data = this.data() as { + headingMeta: Pick + } return (tree: Root, _file: any, done: () => void) => { visit( tree, @@ -50,7 +46,7 @@ export function remarkHeadings(this: Processor) { node.depth === 1 && Array.isArray(node.children) && node.children.some( - (child: any) => (child.type as string) === 'mdxJsxTextElement' + (child: { type: string }) => child.type === 'mdxJsxTextElement' ) const heading = { ...(node as Heading), diff --git a/packages/nextra/src/types.ts b/packages/nextra/src/types.ts index 3e56d930ad..9869b9a63a 100644 --- a/packages/nextra/src/types.ts +++ b/packages/nextra/src/types.ts @@ -2,6 +2,7 @@ import { NextConfig } from 'next' import { Heading as MDASTHeading } from 'mdast' import { ProcessorOptions } from '@mdx-js/mdx' import { Options as RehypePrettyCodeOptions } from 'rehype-pretty-code' +import { GrayMatterFile } from 'gray-matter' export abstract class NextraPluginCache { public cache: { items: PageMapItem[]; fileMap: Record } | null @@ -44,19 +45,16 @@ export type Heading = MDASTHeading & { value: string } -export interface PageOpt { +export type PageOpts = { filename: string route: string - meta: Record + meta: GrayMatterFile['data'] pageMap: PageMapItem[] - titleText: string | null - headings?: Heading[] - unstable_flexsearch?: - | boolean - | { - codeblocks: boolean - } + titleText?: string + headings: Heading[] hasJsxInH1?: boolean + timestamp?: number + unstable_flexsearch?: Flexsearch } export type PageMapResult = [ @@ -66,11 +64,12 @@ export type PageMapResult = [ ] type Theme = string +type Flexsearch = boolean | { codeblocks: boolean } export type NextraConfig = { theme: Theme themeConfig?: string - unstable_flexsearch?: boolean | { codeblocks: boolean } + unstable_flexsearch?: Flexsearch unstable_staticImage?: boolean mdxOptions?: Pick & { rehypePrettyCodeOptions?: Partial