/
config.ts
182 lines (165 loc) · 4.74 KB
/
config.ts
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import path from 'path'
import fs from 'fs-extra'
import chalk from 'chalk'
import globby from 'globby'
import {
AliasOptions,
UserConfig as ViteConfig,
mergeConfig as mergeViteConfig
} from 'vite'
import { Options as VuePluginOptions } from '@vitejs/plugin-vue'
import {
SiteData,
HeadConfig,
LocaleConfig,
createLangDictionary
} from './shared'
import { resolveAliases, APP_PATH, DEFAULT_THEME_PATH } from './alias'
import { MarkdownOptions } from './markdown/markdown'
export { resolveSiteDataByRoute } from './shared'
const debug = require('debug')('vitepress:config')
export interface UserConfig<ThemeConfig = any> {
extends?: RawConfigExports
lang?: string
base?: string
title?: string
description?: string
head?: HeadConfig[]
themeConfig?: ThemeConfig
locales?: Record<string, LocaleConfig>
markdown?: MarkdownOptions
/**
* Opitons to pass on to @vitejs/plugin-vue
*/
vue?: VuePluginOptions
/**
* Vite config
*/
vite?: ViteConfig
srcDir?: string
srcExclude?: string[]
/**
* Enable MPA / zero-JS mode
* @experimental
*/
mpa?: boolean
}
type RawConfigExports =
| UserConfig
| Promise<UserConfig>
| (() => UserConfig | Promise<UserConfig>)
export interface SiteConfig<ThemeConfig = any> {
root: string
srcDir: string
site: SiteData<ThemeConfig>
configPath: string
themeDir: string
outDir: string
tempDir: string
alias: AliasOptions
pages: string[]
markdown: MarkdownOptions | undefined
vue: VuePluginOptions | undefined
vite: ViteConfig | undefined
mpa: boolean
}
const resolve = (root: string, file: string) =>
path.resolve(root, `.vitepress`, file)
export async function resolveConfig(
root: string = process.cwd()
): Promise<SiteConfig> {
const userConfig = await resolveUserConfig(root)
const site = await resolveSiteData(root, userConfig)
const srcDir = path.resolve(root, userConfig.srcDir || '.')
// resolve theme path
const userThemeDir = resolve(root, 'theme')
const themeDir = (await fs.pathExists(userThemeDir))
? userThemeDir
: DEFAULT_THEME_PATH
const config: SiteConfig = {
root,
srcDir,
site,
themeDir,
pages: await globby(['**.md'], {
cwd: srcDir,
ignore: ['**/node_modules', ...(userConfig.srcExclude || [])]
}),
configPath: resolve(root, 'config.js'),
outDir: resolve(root, 'dist'),
tempDir: path.resolve(APP_PATH, 'temp'),
markdown: userConfig.markdown,
alias: resolveAliases(themeDir),
vue: userConfig.vue,
vite: userConfig.vite,
mpa: !!userConfig.mpa
}
return config
}
export async function resolveUserConfig(root: string): Promise<UserConfig> {
// load user config
const configPath = resolve(root, 'config.js')
const hasUserConfig = await fs.pathExists(configPath)
// always delete cache first before loading config
delete require.cache[configPath]
const userConfig: RawConfigExports = hasUserConfig ? require(configPath) : {}
if (hasUserConfig) {
debug(`loaded config at ${chalk.yellow(configPath)}`)
} else {
debug(`no config file found.`)
}
return resolveConfigExtends(userConfig)
}
async function resolveConfigExtends(
config: RawConfigExports
): Promise<UserConfig> {
const resolved = await (typeof config === 'function' ? config() : config)
if (resolved.extends) {
const base = await resolveConfigExtends(resolved.extends)
return mergeConfig(base, resolved)
}
return resolved
}
function mergeConfig(a: UserConfig, b: UserConfig, isRoot = true) {
const merged: Record<string, any> = { ...a }
for (const key in b) {
const value = b[key as keyof UserConfig]
if (value == null) {
continue
}
const existing = merged[key]
if (Array.isArray(existing) && Array.isArray(value)) {
merged[key] = [...existing, ...value]
continue
}
if (isObject(existing) && isObject(value)) {
if (isRoot && key === 'vite') {
merged[key] = mergeViteConfig(existing, value)
} else {
merged[key] = mergeConfig(existing, value, false)
}
continue
}
merged[key] = value
}
return merged
}
function isObject(value: unknown): value is Record<string, any> {
return Object.prototype.toString.call(value) === '[object Object]'
}
export async function resolveSiteData(
root: string,
userConfig?: UserConfig
): Promise<SiteData> {
userConfig = userConfig || (await resolveUserConfig(root))
return {
lang: userConfig.lang || 'en-US',
title: userConfig.title || 'VitePress',
description: userConfig.description || 'A VitePress site',
base: userConfig.base ? userConfig.base.replace(/([^/])$/, '$1/') : '/',
head: userConfig.head || [],
themeConfig: userConfig.themeConfig || {},
locales: userConfig.locales || {},
langs: createLangDictionary(userConfig)
}
}