pull/1/head
Evan You 4 years ago
parent a512fc7925
commit 4414f8e7df

@ -1,4 +1,9 @@
import { createApp as createClientApp, createSSRApp, ref, readonly } from 'vue'
import {
createApp as createClientApp,
createSSRApp,
ref,
readonly
} from 'vue'
import { Content } from './components/Content'
import { createRouter, RouterSymbol } from './router'
import { useSiteData } from './composables/siteData'
@ -38,20 +43,18 @@ export function createApp() {
// the path conversion scheme.
// /foo/bar.html -> /js/foo_bar.md.js
// TODO handle base
pagePath = pagePath.slice(1).replace(/\//g, '_') + '.md.js'
pagePath = './' + pagePath.slice(1).replace(/\//g, '_') + '.md.js'
}
if (inBrowser) {
// in browser: native dynamic import
// js files are stored in a sub directory
return import('./js/' + pagePath).then(page => {
return import(pagePath).then((page) => {
pageDataRef.value = readonly(page.__pageData)
return page.default
})
} else {
// SSR, sync require
const page = require('./' + pagePath)
console.log('setting page data')
const page = require(pagePath)
pageDataRef.value = page.__pageData
return page.default
}
@ -85,5 +88,9 @@ export function createApp() {
}
if (inBrowser) {
createApp().app.mount('#app')
const { app, router } = createApp()
// wait unitl page component is fetched before mounting
router.go().then(() => {
app.mount('#app')
})
}

@ -10,7 +10,7 @@ import { reactive, inject, nextTick, markRaw } from 'vue'
*
* @typedef {{
* route: Route
* go: (href: string) => Promise<void>
* go: (href?: string) => Promise<void>
* }} Router
*/
@ -37,10 +37,11 @@ export function createRouter(loadComponent, fallbackComponent) {
const inBrowser = typeof window !== 'undefined'
/**
* @param {string} href
* @param {string} [href]
* @returns {Promise<void>}
*/
function go(href) {
href = href || (inBrowser ? location.href : '/')
if (inBrowser) {
// save scroll position before changing url
history.replaceState({ scrollPosition: window.scrollY }, document.title)
@ -159,10 +160,6 @@ export function createRouter(loadComponent, fallbackComponent) {
go
}
if (inBrowser) {
loadPage(location.href)
}
return router
}

@ -13,10 +13,13 @@ export async function build(buildOptions: BuildOptions = {}) {
const siteConfig = await resolveConfig(buildOptions.root)
try {
const result = await bundle(siteConfig, buildOptions)
console.log('rendering pages...')
for (const page of siteConfig.pages) {
await renderPage(siteConfig, page, result)
}
} finally {
await fs.rmdir(siteConfig.tempDir, { recursive: true })
console.log('done.')
}
}

@ -49,16 +49,16 @@ export async function bundle(
chunk.facadeModuleId &&
chunk.facadeModuleId.endsWith('.md')
) {
// foo/bar.md -> js/foo_bar.md.js
// foo/bar.md -> _assets/foo_bar.md.js
chunk.fileName = path.join(
'js/',
'_assets/',
slash(path.relative(root, chunk.facadeModuleId)).replace(
/\//g,
'_'
) + '.js'
)
} else {
chunk.fileName = path.join('js/', chunk.fileName)
chunk.fileName = path.join('_assets/', chunk.fileName)
}
}
}
@ -79,7 +79,7 @@ export async function bundle(
silent: true,
resolvers: [resolver],
srcRoots: [APP_PATH, config.themeDir],
cssFileName: 'css/style.css',
cssFileName: '_assets/style.css',
rollupPluginVueOptions,
rollupInputOptions: {
...rollupInputOptions,
@ -90,9 +90,10 @@ export async function bundle(
...rollupOutputOptions,
dir: config.outDir
},
debug: !!process.env.DEBUG
minify: !process.env.DEBUG
}
console.log('building client bundle...')
const clientResult = await build({
...sharedOptions,
rollupOutputOptions: {
@ -101,6 +102,7 @@ export async function bundle(
}
})
console.log('building server bundle...')
const serverResult = await build({
...sharedOptions,
rollupPluginVueOptions: {
@ -116,7 +118,9 @@ export async function bundle(
dir: config.tempDir,
format: 'cjs',
exports: 'named'
}
},
// server build doesn't need minification
minify: false
})
return [clientResult, serverResult]

@ -1,17 +1,73 @@
import path from 'path'
import { SiteConfig } from '../config'
import { promises as fs } from 'fs'
import { SiteConfig, HeadConfig } from '../config'
import { BuildResult } from 'vite'
import { renderToString } from '@vue/server-renderer'
const escape = require('escape-html')
export async function renderPage(
config: SiteConfig,
page: string, // foo.md
result: BuildResult[]
) {
const { createApp } = require(path.join(config.tempDir, 'js/index.js'))
const { createApp } = require(path.join(config.tempDir, '_assets/index.js'))
const { app, router } = createApp()
const routePath = `/${page.replace(/\.md$/, '')}`
router.go(routePath)
const html = await renderToString(app)
console.log(html)
const content = await renderToString(app)
const assetPath = `${config.site.base}_assets`
const pageJsPath = page.replace(/\//g, '_') + '.js'
const { __pageData } = require(path.join(
config.tempDir,
'_assets',
pageJsPath
))
const html = `
<html lang="en-US">
<head>
<title>${config.site.title}</title>
<meta name="description" content="${config.site.description}">
<link rel="stylesheet" href="${assetPath}/style.css">${renderHead(
config.site.head
)}${renderHead(__pageData.frontmatter.head)}
</head>
<body>
<div id="app">${content}</div>
<script type="module" src="${assetPath}/${pageJsPath}"></script>
<script type="module" src="${assetPath}/index.js"></script>
</body>
</html>`.trim()
const htmlFileName = path.join(config.outDir, page.replace(/\.md$/, '.html'))
await fs.mkdir(path.dirname(htmlFileName), { recursive: true })
await fs.writeFile(htmlFileName, html)
}
function renderHead(head: HeadConfig[]) {
if (!head || !head.length) {
return ''
}
return (
`\n ` +
head
.map(([tag, attrs = {}, innerHTML = '']) => {
const openTag = `<${tag}${renderAttrs(attrs)}>`
if (tag !== 'link' && tag !== 'meta') {
return `${openTag}${innerHTML}</${tag}>`
} else {
return openTag
}
})
.join('\n ')
)
}
function renderAttrs(attrs: Record<string, string>): string {
return Object.keys(attrs)
.map((key) => {
return ` ${key}="${escape(attrs[key])}"`
})
.join('')
}

@ -7,13 +7,15 @@ import { Resolver } from 'vite'
const debug = require('debug')('vitepress:config')
export type HeadConfig =
| [string, Record<string, string>]
| [string, Record<string, string>, string]
export interface UserConfig<ThemeConfig = any> {
base?: string
title?: string
description?: string
head?:
| [string, Record<string, string>]
| [string, Record<string, string>, string]
head?: HeadConfig[]
themeConfig?: ThemeConfig
// TODO locales support etc.
}
@ -22,6 +24,7 @@ export interface SiteData<ThemeConfig = any> {
title: string
description: string
base: string
head: HeadConfig[]
themeConfig: ThemeConfig
}
@ -90,6 +93,7 @@ export async function resolveSiteData(root: string): Promise<SiteData> {
title: userConfig.title || 'VitePress',
description: userConfig.description || 'A VitePress site',
base: userConfig.base || '/',
head: userConfig.head || [],
themeConfig: userConfig.themeConfig || {}
}
}

Loading…
Cancel
Save