Skip to content

Commit

Permalink
wip: dev works with vite 2
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Dec 28, 2020
1 parent f05fe8b commit bf8fd3a
Show file tree
Hide file tree
Showing 12 changed files with 581 additions and 583 deletions.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Now, with Vite and Vue 3, it is time to rethink what a "Vue-powered static site

## Improvements Over VuePress

There're couple of things that are improved from VuePress.
There're couple of things that are improved from VuePress....

### It Uses Vue 3

Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"release": "node scripts/release.js",
"docs": "run-p dev docs-dev",
"docs-dev": "node ./bin/vitepress dev docs",
"debug": "node --inspect-brk ./bin/vitepress dev docs",
"docs-build": "yarn build && node ./bin/vitepress build docs",
"docs-serve": "yarn docs-build && node ./bin/vitepress serve --root docs"
},
Expand Down Expand Up @@ -64,8 +65,8 @@
"dependencies": {
"@docsearch/css": "^1.0.0-alpha.28",
"@docsearch/js": "^1.0.0-alpha.28",
"@vue/compiler-sfc": "^3.0.3",
"@vue/server-renderer": "^3.0.3",
"@vue/compiler-sfc": "^3.0.4",
"@vue/server-renderer": "^3.0.4",
"chalk": "^4.1.0",
"debug": "^4.1.1",
"diacritics": "^1.3.0",
Expand All @@ -83,8 +84,8 @@
"ora": "^5.1.0",
"prismjs": "^1.20.0",
"slash": "^3.0.0",
"vite": "^1.0.0-rc.13",
"vue": "^3.0.3"
"vite": "^2.0.0-alpha.1",
"vue": "^3.0.4"
},
"devDependencies": {
"@types/fs-extra": "^9.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/client/app/composables/siteData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function parse(data: string): SiteData {

// hmr
if (import.meta.hot) {
import.meta.hot!.acceptDeps('/@siteData', (m) => {
import.meta.hot!.accept('/@siteData', (m) => {
siteDataRef.value = parse(m.default)
})
}
2 changes: 1 addition & 1 deletion src/node/build/build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'fs-extra'
import { bundle, okMark, failMark } from './bundle'
import { BuildConfig as ViteBuildOptions } from 'vite'
import { BuildOptions as ViteBuildOptions } from 'vite'
import { resolveConfig } from '../config'
import { renderPage } from './render'
import { OutputChunk, OutputAsset } from 'rollup'
Expand Down
11 changes: 2 additions & 9 deletions src/node/build/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ import { BuildOptions } from './build'
import { resolveUserConfig, SiteConfig } from '../config'
import { Plugin, OutputAsset, OutputChunk } from 'rollup'
import { createMarkdownToVueRenderFn } from '../markdownToVue'
import {
build,
ssrBuild,
BuildConfig as ViteBuildOptions,
BuildResult
} from 'vite'
import { build, BuildOptions as ViteBuildOptions } from 'vite'
import ora from 'ora'

export const okMark = '\x1b[32m✓\x1b[0m'
Expand Down Expand Up @@ -60,9 +55,7 @@ export async function bundle(
// compile md into vue src
if (id.endsWith('.md')) {
const content = await fs.readFile(id, 'utf-8')
// TODO use git timestamp
const lastUpdated = (await fs.stat(id)).mtimeMs
const { vueSrc } = markdownToVue(content, id, lastUpdated)
const { vueSrc } = markdownToVue(content, id)
return vueSrc
}
},
Expand Down
9 changes: 5 additions & 4 deletions src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ if (root) {
}

if (!command || command === 'dev') {
const port = argv.port || 3000
createServer(argv)
createServer(root, argv)
.then((server) => {
server.listen(port, () => {
console.log(`listening at http://localhost:${port}`)
return server.listen().then(() => {
console.log(
`listening at http://localhost:${server.config.server.port}`
)
})
})
.catch((err) => {
Expand Down
10 changes: 5 additions & 5 deletions src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import path from 'path'
import fs from 'fs-extra'
import chalk from 'chalk'
import globby from 'globby'
import { createResolver, APP_PATH, DEFAULT_THEME_PATH } from './resolver'
import { Resolver } from 'vite'
import { APP_PATH, createAlias, DEFAULT_THEME_PATH } from './resolver'
import { SiteData, HeadConfig, LocaleConfig } from '../../types/shared'
import { MarkdownOptions } from './markdown/markdown'
import { AliasOptions } from 'vite'
export { resolveSiteDataByRoute } from './shared/config'

const debug = require('debug')('vitepress:config')
Expand All @@ -30,7 +30,7 @@ export interface SiteConfig<ThemeConfig = any> {
themeDir: string
outDir: string
tempDir: string
resolver: Resolver
aliases: AliasOptions
pages: string[]
markdown?: MarkdownOptions
}
Expand Down Expand Up @@ -58,8 +58,8 @@ export async function resolveConfig(
configPath: resolve(root, 'config.js'),
outDir: resolve(root, 'dist'),
tempDir: path.resolve(APP_PATH, 'temp'),
resolver: createResolver(themeDir, userConfig),
markdown: userConfig.markdown
markdown: userConfig.markdown,
aliases: createAlias(themeDir, userConfig)
}

return config
Expand Down
62 changes: 44 additions & 18 deletions src/node/markdownToVue.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import LRUCache from 'lru-cache'
import { createMarkdownRenderer, MarkdownOptions } from './markdown/markdown'
import { deeplyParseHeader } from './utils/parseHeader'
import { PageData, HeadConfig } from '../../types/shared'
import slash from 'slash'

const debug = require('debug')('vitepress:md')
const cache = new LRUCache<string, MarkdownCompileResult>({ max: 1024 })
const cache = new LRUCache<string, MarkdownCompileCachedResult>({ max: 1024 })

interface MarkdownCompileCachedResult extends MarkdownCompileResult {
tagsWithPageData: string
tagsWithoutPageData: string
}

interface MarkdownCompileResult {
vueSrc: string
Expand All @@ -22,44 +29,63 @@ export function createMarkdownToVueRenderFn(
return (
src: string,
file: string,
lastUpdated: number,
injectData = true
) => {
file = path.relative(root, file)
): MarkdownCompileResult => {
const relativePath = slash(path.relative(root, file))

const cached = cache.get(src)
if (cached) {
debug(`[cache hit] ${file}`)
return cached
debug(`[cache hit] ${relativePath}`)
return pickResult(cached, injectData)
}

const start = Date.now()

const { content, data: frontmatter } = matter(src)
const { html, data } = md.render(content)

// TODO validate data.links?
const vueSrc = `\n<template><div>${html}</div></template>`

// inject page data
// TODO validate data.links?
const pageData: PageData = {
title: inferTitle(frontmatter, content),
description: inferDescription(frontmatter),
frontmatter,
headers: data.headers,
relativePath: file.replace(/\\/g, '/'),
lastUpdated
relativePath,
// TODO use git timestamp?
lastUpdated: fs.statSync(file).mtimeMs
}

const additionalBlocks = injectData
? injectPageData(data.hoistedTags || [], pageData)
: data.hoistedTags || []
const tagsWithPageData = genPageDataCode(
data.hoistedTags || [],
pageData
).join('\n')

const vueSrc =
additionalBlocks.join('\n') + `\n<template><div>${html}</div></template>`
const tagsWithoutPageData = (data.hoistedTags || []).join('\n')

debug(`[render] ${file} in ${Date.now() - start}ms.`)

const result = { vueSrc, pageData }
const result = {
vueSrc,
pageData,
tagsWithPageData,
tagsWithoutPageData
}
cache.set(src, result)
return result
return pickResult(result, injectData)
}
}

function pickResult(
res: MarkdownCompileCachedResult,
injectData: boolean
): MarkdownCompileResult {
return {
vueSrc:
res.vueSrc +
(injectData ? res.tagsWithPageData : res.tagsWithoutPageData),
pageData: res.pageData
}
}

Expand All @@ -68,7 +94,7 @@ const scriptSetupRE = /<\s*script[^>]*\bsetup\b[^>]*/
const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/
const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)as(\s*)default/

function injectPageData(tags: string[], data: PageData) {
function genPageDataCode(tags: string[], data: PageData) {
const code = `\nexport const __pageData = ${JSON.stringify(
JSON.stringify(data)
)}`
Expand Down
107 changes: 107 additions & 0 deletions src/node/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import path from 'path'
import { Plugin } from 'vite'
import { SiteConfig, resolveSiteData } from './config'
import { createMarkdownToVueRenderFn } from './markdownToVue'
import { APP_PATH, SITE_DATA_REQUEST_PATH } from './resolver'
import createVuePlugin from '@vitejs/plugin-vue'
import slash from 'slash'

export function createVitePressPlugin(
root: string,
{ configPath, aliases, markdown, site: initialSiteData }: SiteConfig
): Plugin[] {
const markdownToVue = createMarkdownToVueRenderFn(root, markdown)

const vuePlugin = createVuePlugin({
include: [/\.vue$/, /\.md$/]
})

let siteData = initialSiteData
let stringifiedData = JSON.stringify(JSON.stringify(initialSiteData))

const vitePressPlugin: Plugin = {
name: 'vitepress',

config() {
return {
alias: aliases,
transformInclude: /\.md$/
}
},

resolveId(id) {
if (id === SITE_DATA_REQUEST_PATH) {
return SITE_DATA_REQUEST_PATH
}
},

load(id) {
if (id === SITE_DATA_REQUEST_PATH) {
return `export default ${stringifiedData}`
}
},

transform(code, id) {
if (id.endsWith('.md')) {
// transform .md files into vueSrc so plugin-vue can handle it
return markdownToVue(code, id).vueSrc
}
},

configureServer(server) {
// serve our index.html after vite history fallback
const indexPath = `/@fs/${path.join(APP_PATH, 'index.html')}`
return () => {
server.app.use((req, _, next) => {
if (req.url!.endsWith('.html')) {
req.url = indexPath
}
next()
})
}
},

async handleHotUpdate(file, mods, read, server) {
if (file === configPath) {
const newData = await resolveSiteData(root)
stringifiedData = JSON.stringify(JSON.stringify(newData))
if (newData.base !== siteData.base) {
console.warn(
`[vitepress]: config.base has changed. Please restart the dev server.`
)
}
siteData = newData
return
}

// hot reload .md files as .vue files
if (file.endsWith('.md')) {
const content = await read()
const { pageData, vueSrc } = markdownToVue(
content.toString(),
file,
// do not inject pageData on HMR
// it leads to plugin-vue to think <script> has changed and reloads
// the component instead of re-rendering.
// pageData needs separate HMR logic anyway (see below)
false
)

// notify the client to update page data
server.ws.send({
type: 'custom',
event: 'vitepress:pageData',
data: {
path: `/${slash(path.relative(root, file))}`,
pageData
}
})

// reload the content component
return vuePlugin.handleHotUpdate!(file, mods, () => vueSrc, server)
}
}
}

return [vitePressPlugin, vuePlugin]
}
32 changes: 10 additions & 22 deletions src/node/resolver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path from 'path'
import { Resolver } from 'vite'
import { AliasOptions } from 'vite'
import { UserConfig } from './config'

export const APP_PATH = path.join(__dirname, '../client/app')
Expand All @@ -20,29 +20,17 @@ export const SITE_DATA_REQUEST_PATH = '/' + SITE_DATA_ID
// 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(
export function createAlias(
themeDir: string,
userConfig: UserConfig
): Resolver {
): AliasOptions {
return {
alias: {
...userConfig.alias,
'/@app/': APP_PATH,
'/@theme/': themeDir,
'/@default-theme/': DEFAULT_THEME_PATH,
'/@shared/': SHARED_PATH,
vitepress: '/@app/exports.js',
[SITE_DATA_ID]: SITE_DATA_REQUEST_PATH
},
requestToFile(publicPath) {
if (publicPath === SITE_DATA_REQUEST_PATH) {
return SITE_DATA_REQUEST_PATH
}
},
fileToRequest(filePath) {
if (filePath === SITE_DATA_REQUEST_PATH) {
return SITE_DATA_REQUEST_PATH
}
}
...userConfig.alias,
'/@app': APP_PATH,
'/@theme': themeDir,
'/@default-theme': DEFAULT_THEME_PATH,
'/@shared': SHARED_PATH,
vitepress: `${APP_PATH}/exports.js`,
[SITE_DATA_ID]: SITE_DATA_REQUEST_PATH
}
}
Loading

0 comments on commit bf8fd3a

Please sign in to comment.