/
config.tsx
109 lines (100 loc) · 2.9 KB
/
config.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import React, {
createContext,
ReactElement,
ReactNode,
useContext,
useState
} from 'react'
import { PageOpts } from 'nextra'
import { ThemeProvider } from 'next-themes'
import { Context, DocsThemeConfig } from '../types'
import {
DEEP_OBJECT_KEYS,
DEFAULT_THEME,
LEGACY_CONFIG_OPTIONS
} from '../constants'
import { MenuProvider } from './menu'
type Config = DocsThemeConfig &
Pick<PageOpts, 'flexsearch' | 'newNextLinkBehavior' | 'title' | 'frontMatter'>
const ConfigContext = createContext<Config>({
title: '',
frontMatter: {},
...DEFAULT_THEME
})
export const useConfig = () => useContext(ConfigContext)
export const ConfigProvider = ({
children,
value
}: {
children: ReactNode
value: Context
}): ReactElement => {
const [menu, setMenu] = useState(false)
const { themeConfig, pageOpts } = value
const extendedConfig: Config = {
...DEFAULT_THEME,
...themeConfig,
flexsearch: pageOpts.flexsearch,
newNextLinkBehavior: pageOpts.newNextLinkBehavior,
title: pageOpts.title,
frontMatter: pageOpts.frontMatter,
...Object.fromEntries(
DEEP_OBJECT_KEYS.map(key =>
typeof themeConfig[key] === 'object'
? [
key,
// @ts-expect-error -- key has always object value
{ ...DEFAULT_THEME[key], ...themeConfig[key] }
]
: []
)
)
}
const { nextThemes } = extendedConfig
if (process.env.NODE_ENV === 'development') {
const notice = '[nextra-theme-docs] ⚠️ You are using a legacy theme config'
for (const [legacyOption, newPath] of Object.entries(
LEGACY_CONFIG_OPTIONS
)) {
if (legacyOption in themeConfig) {
const [obj, key] = newPath.split('.')
const renameTo = key ? `${obj}: { ${key}: ... }` : obj
console.warn(
`${notice} \`${legacyOption}\`. Rename it to \`${renameTo}\` for future compatibility.`
)
}
}
for (const key of ['search', 'footer'] as const) {
if (key in themeConfig) {
const option = themeConfig[key]
if (typeof option === 'boolean' || option == null) {
console.warn(
`${notice} \`${key}\`.`,
option
? 'Remove it'
: `Rename it to \`${key}: { component: null }\` for future compatibility.`
)
}
}
}
if (typeof themeConfig.banner === 'string') {
console.warn(
notice,
'`banner`. Rename it to `banner: { content: ... }` for future compatibility.'
)
}
}
return (
<ThemeProvider
attribute="class"
disableTransitionOnChange
defaultTheme={nextThemes.defaultTheme}
storageKey={nextThemes.storageKey}
forcedTheme={nextThemes.forcedTheme}
>
<ConfigContext.Provider value={extendedConfig}>
<MenuProvider value={{ menu, setMenu }}>{children}</MenuProvider>
</ConfigContext.Provider>
</ThemeProvider>
)
}