resolve config

pull/1/head
Evan You 5 years ago
parent 78722824cb
commit 13924c9734

@ -13,9 +13,13 @@ if (!command || command === 'dev') {
if (root) {
argv.root = root
}
require('../dist').createServer(argv).listen(port, () => {
require('../dist').createServer(argv).then(server => {
server.listen(port, () => {
console.log(`listening at http://localhost:${port}`)
})
}).catch(err => {
console.error(`failed to start server. error: `, err)
})
} else if (command === 'build') {
require('../dist').build(argv).catch(err => {
console.error(`build error: `, err)

@ -1,7 +1,7 @@
import { createApp, h } from 'vue'
import { Layout } from '/@theme/index.js'
import { Layout } from '/@theme/index'
import { Content } from './Content'
import { useRouter } from './router.js'
import { useRouter } from './router'
const App = {
setup() {

@ -0,0 +1,15 @@
{
"compilerOptions": {
"lib": ["ESNext", "DOM"],
"moduleResolution": "node",
"checkJs": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"noImplicitAny": true,
"paths": {
"/@app/*": ["lib/app/*"],
"/@theme/*": ["lib/theme-default/*"]
}
},
"include": ["."]
}

@ -13,7 +13,7 @@
"dist"
],
"scripts": {
"dev": "tsc -w -p ."
"dev": "tsc -w -p src"
},
"keywords": [
"vite",

@ -1,2 +1,2 @@
export * from './server/server'
export * from './server'
export * from './build/build'

@ -0,0 +1,78 @@
import path from 'path'
import { promises as fs } from 'fs'
import { createResolver } from './utils/pathResolver'
import { Resolver } from 'vite'
const debug = require('debug')('vitepress:config')
export interface UserConfig<ThemeConfig = Record<string, any>> {
base?: string
title?: string
description?: string
head?:
| [string, Record<string, string>]
| [string, Record<string, string>, string]
themeConfig?: ThemeConfig
// TODO locales support etc.
}
export interface SiteData<ThemeConfig = Record<string, any>> {
title: string
description: string
base: string
themeConfig: ThemeConfig
pages: PageData[]
}
export interface PageData {
path: string
}
export interface ResolvedConfig<ThemeConfig = Record<string, any>> {
site: SiteData<ThemeConfig>
root: string // project root on file system
themePath: string
resolver: Resolver
}
export async function resolveConfig(root: string): Promise<ResolvedConfig> {
// 1. load user config
const configPath = path.join(root, '.vitepress/config.js')
let hasUserConfig = false
try {
await fs.stat(configPath)
hasUserConfig = true
debug(`loading user config at ${configPath}`)
} catch (e) {}
const userConfig: UserConfig = hasUserConfig ? require(configPath) : {}
// 2. TODO scan pages data
// 3. resolve site data
const site: SiteData = {
title: userConfig.title || 'VitePress',
description: userConfig.description || 'A VitePress site',
base: userConfig.base || '/',
themeConfig: userConfig.themeConfig || {},
pages: []
}
// 4. resolve theme path
const userThemePath = path.join(root, '.vitepress/theme')
let themePath: string
try {
await fs.stat(userThemePath)
themePath = userThemePath
} catch (e) {
themePath = path.join(__dirname, '../lib/theme-default')
}
const config: ResolvedConfig = {
root,
site,
themePath,
resolver: createResolver(themePath)
}
return config
}

@ -0,0 +1,77 @@
import path from 'path'
import {
createServer as createViteServer,
cachedRead,
Plugin,
ServerConfig
} from 'vite'
import { resolveConfig, ResolvedConfig } from './resolveConfig'
import { createMarkdownToVueRenderFn } from './markdown/markdownToVue'
import { APP_PATH } from './utils/pathResolver'
const debug = require('debug')('vitepress:serve')
const debugHmr = require('debug')('vitepress:hmr')
function createVitePressPlugin({
themePath,
resolver: vitepressResolver
}: ResolvedConfig): Plugin {
return ({ app, root, watcher, resolver }) => {
const markdownToVue = createMarkdownToVueRenderFn(root)
// watch theme files if it's outside of project root
if (path.relative(root, themePath).startsWith('..')) {
debugHmr(`watching theme dir outside of project root: ${themePath}`)
watcher.add(themePath)
}
// hot reload .md files as .vue files
watcher.on('change', async (file) => {
if (file.endsWith('.md')) {
debugHmr(`reloading ${file}`)
const content = await cachedRead(null, file)
watcher.handleVueReload(file, Date.now(), markdownToVue(content, file))
}
})
// inject Koa middleware
app.use(async (ctx, next) => {
// handle .md -> vue transforms
if (ctx.path.endsWith('.md')) {
const file = resolver.publicToFile(ctx.path)
await cachedRead(ctx, file)
// let vite know this is supposed to be treated as vue file
ctx.vue = true
ctx.body = markdownToVue(ctx.body, file)
debug(ctx.url, ctx.status)
return next()
}
// detect and serve vitepress @app / @theme files
const file = vitepressResolver.publicToFile(ctx.path, root)
if (file) {
await cachedRead(ctx, file)
debug(ctx.url, ctx.status)
await next()
return
}
await next()
// serve our index.html after vite history fallback
if (ctx.url === '/index.html') {
await cachedRead(ctx, path.join(APP_PATH, 'index-dev.html'))
}
})
}
}
export async function createServer(options: ServerConfig = {}) {
const config = await resolveConfig(options.root || process.cwd())
return createViteServer({
...options,
plugins: [createVitePressPlugin(config)],
resolvers: [config.resolver]
})
}

@ -1,26 +0,0 @@
import path from 'path'
import { Resolver } from "vite"
// built ts files are placed into /dist
export const APP_PATH = path.join(__dirname, '../../lib/app')
// TODO detect user configured theme
export const THEME_PATH = path.join(__dirname, '../../lib/theme-default')
export const VitePressResolver: Resolver = {
publicToFile(publicPath) {
if (publicPath.startsWith('/@app')) {
return path.join(APP_PATH, publicPath.replace(/^\/@app\/?/, ''))
}
if (publicPath.startsWith('/@theme')) {
return path.join(THEME_PATH, publicPath.replace(/^\/@theme\/?/, ''))
}
},
fileToPublic(filePath) {
if (filePath.startsWith(APP_PATH)) {
return `/@app/${path.relative(APP_PATH, filePath)}`
}
if (filePath.startsWith(THEME_PATH)) {
return `/@theme/${path.relative(THEME_PATH, filePath)}`
}
}
}

@ -1,70 +0,0 @@
import path from 'path'
import {
createServer as createViteServer,
cachedRead,
Plugin,
ServerConfig
} from 'vite'
import { createMarkdownToVueRenderFn } from '../markdown/markdownToVue'
import { VitePressResolver, THEME_PATH, APP_PATH } from './resolver'
const debug = require('debug')('vitepress:serve')
const debugHmr = require('debug')('vitepress:hmr')
const VitePressPlugin: Plugin = ({ app, root, watcher, resolver }) => {
const markdownToVue = createMarkdownToVueRenderFn(root)
// watch theme files if it's outside of project root
if (path.relative(root, THEME_PATH).startsWith('..')) {
debugHmr(`watching theme dir outside of project root: ${THEME_PATH}`)
watcher.add(THEME_PATH)
}
// hot reload .md files as .vue files
watcher.on('change', async (file) => {
if (file.endsWith('.md')) {
debugHmr(`reloading ${file}`)
const content = await cachedRead(null, file)
watcher.handleVueReload(file, Date.now(), markdownToVue(content, file))
}
})
// inject Koa middleware
app.use(async (ctx, next) => {
// handle .md -> vue transforms
if (ctx.path.endsWith('.md')) {
const file = resolver.publicToFile(ctx.path)
await cachedRead(ctx, file)
// let vite know this is supposed to be treated as vue file
ctx.vue = true
ctx.body = markdownToVue(ctx.body, file)
debug(ctx.url)
return next()
}
// detect and serve vitepress @app / @theme files
const file = VitePressResolver.publicToFile(ctx.path, root)
if (file) {
ctx.type = path.extname(file)
await cachedRead(ctx, file)
debug(ctx.url)
return next()
}
await next()
// serve our index.html after vite history fallback
if (ctx.url === '/index.html') {
await cachedRead(ctx, path.join(APP_PATH, 'index-dev.html'))
}
})
}
export function createServer(options: ServerConfig = {}) {
return createViteServer({
...options,
plugins: [VitePressPlugin],
resolvers: [VitePressResolver]
})
}

@ -1,9 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "./dist",
"outDir": "../dist",
"module": "commonjs",
"lib": ["ESNext", "DOM"],
"lib": ["ESNext"],
"sourceMap": false,
"target": "esnext",
"moduleResolution": "node",
@ -16,11 +16,7 @@
"strictNullChecks": true,
"noImplicitAny": true,
"removeComments": false,
"preserveSymlinks": true,
"paths": {
"/@app/*": ["lib/app/*"],
"/@theme/*": ["lib/theme-default/*"]
}
"preserveSymlinks": true
},
"include": ["src", "lib"]
"include": ["."]
}

@ -0,0 +1,30 @@
import path from 'path'
import { Resolver } from "vite"
// built ts files are placed into /dist
export const APP_PATH = path.join(__dirname, '../../lib/app')
// this is a path resolver that is passed to vite
// so that we can resolve custom requests that start with /@app or /@theme
// we also need to map file paths back to their public served paths so that
// vite HMR can send the correct update notifications to the client.
export function createResolver(themePath: string): Resolver {
return {
publicToFile(publicPath) {
if (publicPath.startsWith('/@app')) {
return path.join(APP_PATH, publicPath.replace(/^\/@app\/?/, ''))
}
if (publicPath.startsWith('/@theme')) {
return path.join(themePath, publicPath.replace(/^\/@theme\/?/, ''))
}
},
fileToPublic(filePath) {
if (filePath.startsWith(APP_PATH)) {
return `/@app/${path.relative(APP_PATH, filePath)}`
}
if (filePath.startsWith(themePath)) {
return `/@theme/${path.relative(themePath, filePath)}`
}
}
}
}
Loading…
Cancel
Save