|
|
|
@ -12,6 +12,7 @@ import {
|
|
|
|
|
} from './shared'
|
|
|
|
|
import { resolveAliases, APP_PATH, DEFAULT_THEME_PATH } from './alias'
|
|
|
|
|
import { MarkdownOptions } from './markdown/markdown'
|
|
|
|
|
import { build } from 'esbuild'
|
|
|
|
|
|
|
|
|
|
export { resolveSiteDataByRoute } from './shared'
|
|
|
|
|
|
|
|
|
@ -70,7 +71,7 @@ const resolve = (root: string, file: string) =>
|
|
|
|
|
export async function resolveConfig(
|
|
|
|
|
root: string = process.cwd()
|
|
|
|
|
): Promise<SiteConfig> {
|
|
|
|
|
const userConfig = await resolveUserConfig(root)
|
|
|
|
|
const { configPath, userConfig } = await resolveUserConfig(root)
|
|
|
|
|
|
|
|
|
|
if (userConfig.vueOptions) {
|
|
|
|
|
console.warn(
|
|
|
|
@ -104,7 +105,7 @@ export async function resolveConfig(
|
|
|
|
|
cwd: srcDir,
|
|
|
|
|
ignore: ['**/node_modules', ...(userConfig.srcExclude || [])]
|
|
|
|
|
}),
|
|
|
|
|
configPath: resolve(root, 'config.js'),
|
|
|
|
|
configPath: configPath,
|
|
|
|
|
outDir: resolve(root, 'dist'),
|
|
|
|
|
tempDir: path.resolve(APP_PATH, 'temp'),
|
|
|
|
|
markdown: userConfig.markdown,
|
|
|
|
@ -116,29 +117,98 @@ export async function resolveConfig(
|
|
|
|
|
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)
|
|
|
|
|
/**
|
|
|
|
|
* load user config
|
|
|
|
|
* @param root
|
|
|
|
|
* @param configFile
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
export async function resolveUserConfig(
|
|
|
|
|
root: string,
|
|
|
|
|
configFile?: string
|
|
|
|
|
): Promise<{
|
|
|
|
|
configPath: string
|
|
|
|
|
userConfig: UserConfig
|
|
|
|
|
}> {
|
|
|
|
|
const start = Date.now()
|
|
|
|
|
|
|
|
|
|
let configPath: string | undefined
|
|
|
|
|
let isTS = false
|
|
|
|
|
|
|
|
|
|
if (configFile) {
|
|
|
|
|
configPath = resolve(root, configFile)
|
|
|
|
|
isTS = configFile.endsWith('.ts')
|
|
|
|
|
} else {
|
|
|
|
|
const jsConfigPath = resolve(root, 'config.js')
|
|
|
|
|
if (fs.existsSync(jsConfigPath)) {
|
|
|
|
|
configPath = jsConfigPath
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!configPath) {
|
|
|
|
|
const tsConfigPath = resolve(root, 'config.ts')
|
|
|
|
|
if (fs.existsSync(tsConfigPath)) {
|
|
|
|
|
configPath = tsConfigPath
|
|
|
|
|
isTS = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!configPath) {
|
|
|
|
|
debug(`no config file found.`)
|
|
|
|
|
return {
|
|
|
|
|
configPath: '',
|
|
|
|
|
userConfig: {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
let userConfig: UserConfig | (() => UserConfig) | undefined
|
|
|
|
|
|
|
|
|
|
if (!userConfig && !isTS) {
|
|
|
|
|
try {
|
|
|
|
|
// always delete cache first before loading config
|
|
|
|
|
delete require.cache[configPath]
|
|
|
|
|
const userConfig: UserConfig | (() => UserConfig) = hasUserConfig
|
|
|
|
|
? require(configPath)
|
|
|
|
|
: {}
|
|
|
|
|
if (hasUserConfig) {
|
|
|
|
|
userConfig = configPath ? require(configPath) : {}
|
|
|
|
|
debug(`loaded config at ${chalk.yellow(configPath)}`)
|
|
|
|
|
} else {
|
|
|
|
|
debug(`no config file found.`)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
const ignored = new RegExp(
|
|
|
|
|
[
|
|
|
|
|
`Cannot use import statement`,
|
|
|
|
|
`Must use import to load ES Module`,
|
|
|
|
|
`Unexpected token`,
|
|
|
|
|
`Unexpected identifier`
|
|
|
|
|
].join('|')
|
|
|
|
|
)
|
|
|
|
|
if (!ignored.test(e.message)) {
|
|
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return typeof userConfig === 'function' ? userConfig() : userConfig
|
|
|
|
|
if (!userConfig) {
|
|
|
|
|
const bundled = await bundleConfigFile(configPath)
|
|
|
|
|
userConfig = await loadConfigFromBundledFile(configPath, bundled.code)
|
|
|
|
|
debug(`bundled config file loaded in ${Date.now() - start}ms`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const config = typeof userConfig === 'function' ? userConfig() : userConfig
|
|
|
|
|
return {
|
|
|
|
|
configPath,
|
|
|
|
|
userConfig: config
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(chalk.red(`failed to load config from ${configPath}`), {
|
|
|
|
|
error: e
|
|
|
|
|
})
|
|
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function resolveSiteData(
|
|
|
|
|
root: string,
|
|
|
|
|
userConfig?: UserConfig
|
|
|
|
|
): Promise<SiteData> {
|
|
|
|
|
userConfig = userConfig || (await resolveUserConfig(root))
|
|
|
|
|
userConfig = userConfig || (await (await resolveUserConfig(root)).userConfig)
|
|
|
|
|
return {
|
|
|
|
|
lang: userConfig.lang || 'en-US',
|
|
|
|
|
title: userConfig.title || 'VitePress',
|
|
|
|
@ -151,3 +221,86 @@ export async function resolveSiteData(
|
|
|
|
|
customData: userConfig.customData || {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface NodeModuleWithCompile extends NodeModule {
|
|
|
|
|
_compile(code: string, filename: string): any
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadConfigFromBundledFile(
|
|
|
|
|
fileName: string,
|
|
|
|
|
bundledCode: string
|
|
|
|
|
): Promise<UserConfig> {
|
|
|
|
|
const extension = path.extname(fileName)
|
|
|
|
|
const defaultLoader = require.extensions[extension]!
|
|
|
|
|
require.extensions[extension] = (module: NodeModule, filename: string) => {
|
|
|
|
|
if (filename === fileName) {
|
|
|
|
|
;(module as NodeModuleWithCompile)._compile(bundledCode, filename)
|
|
|
|
|
} else {
|
|
|
|
|
defaultLoader(module, filename)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// clear cache in case of server restart
|
|
|
|
|
delete require.cache[require.resolve(fileName)]
|
|
|
|
|
const raw = require(fileName)
|
|
|
|
|
const config = raw.__esModule ? raw.default : raw
|
|
|
|
|
require.extensions[extension] = defaultLoader
|
|
|
|
|
return config
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function bundleConfigFile(
|
|
|
|
|
fileName: string,
|
|
|
|
|
mjs = false
|
|
|
|
|
): Promise<{ code: string; dependencies: string[] }> {
|
|
|
|
|
const result = await build({
|
|
|
|
|
absWorkingDir: process.cwd(),
|
|
|
|
|
entryPoints: [fileName],
|
|
|
|
|
outfile: 'out.js',
|
|
|
|
|
write: false,
|
|
|
|
|
platform: 'node',
|
|
|
|
|
bundle: true,
|
|
|
|
|
format: mjs ? 'esm' : 'cjs',
|
|
|
|
|
sourcemap: 'inline',
|
|
|
|
|
metafile: true,
|
|
|
|
|
plugins: [
|
|
|
|
|
{
|
|
|
|
|
name: 'externalize-deps',
|
|
|
|
|
setup(build) {
|
|
|
|
|
build.onResolve({ filter: /.*/ }, (args) => {
|
|
|
|
|
const id = args.path
|
|
|
|
|
if (id[0] !== '.' && !path.isAbsolute(id)) {
|
|
|
|
|
return {
|
|
|
|
|
external: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'replace-import-meta',
|
|
|
|
|
setup(build) {
|
|
|
|
|
build.onLoad({ filter: /\.[jt]s$/ }, async (args) => {
|
|
|
|
|
const contents = await fs.promises.readFile(args.path, 'utf8')
|
|
|
|
|
return {
|
|
|
|
|
loader: args.path.endsWith('.ts') ? 'ts' : 'js',
|
|
|
|
|
contents: contents
|
|
|
|
|
.replace(
|
|
|
|
|
/\bimport\.meta\.url\b/g,
|
|
|
|
|
JSON.stringify(`file://${args.path}`)
|
|
|
|
|
)
|
|
|
|
|
.replace(
|
|
|
|
|
/\b__dirname\b/g,
|
|
|
|
|
JSON.stringify(path.dirname(args.path))
|
|
|
|
|
)
|
|
|
|
|
.replace(/\b__filename\b/g, JSON.stringify(args.path))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
const { text } = result.outputFiles[0]
|
|
|
|
|
return {
|
|
|
|
|
code: text,
|
|
|
|
|
dependencies: result.metafile ? Object.keys(result.metafile.inputs) : []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|