Merge branch 'vuejs:main' into main

pull/474/head
Daniel Kreiseder 4 years ago committed by GitHub
commit 8a8bcdfacb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +1,24 @@
## [0.20.9](https://github.com/vuejs/vitepress/compare/v0.20.8...v0.20.9) (2021-12-15)
### Features
- shouldPreload hook ([e721d60](https://github.com/vuejs/vitepress/commit/e721d605851be4e27f4948d96d5c3bab6d23ead2))
- support array of patterns in data loaders ([f5308d7](https://github.com/vuejs/vitepress/commit/f5308d746f3089ef6818b0139fe249827a47628b))
## [0.20.8](https://github.com/vuejs/vitepress/compare/v0.20.7...v0.20.8) (2021-12-14)
## [0.20.7](https://github.com/vuejs/vitepress/compare/v0.20.6...v0.20.7) (2021-12-14)
### Features
- **types:** re-export vite client type ([4caa7b2](https://github.com/vuejs/vitepress/commit/4caa7b231753ddedb83365a37b8c259ae461bd37))
## [0.20.6](https://github.com/vuejs/vitepress/compare/v0.20.4...v0.20.6) (2021-12-14)
### Features
- support static data loaders ([26fe81c](https://github.com/vuejs/vitepress/commit/26fe81c88618d7df5d623d041ac3df96e7d7ee7b))
## [0.20.5](https://github.com/vuejs/vitepress/compare/v0.20.4...v0.20.5) (2021-12-12) ## [0.20.5](https://github.com/vuejs/vitepress/compare/v0.20.4...v0.20.5) (2021-12-12)
- Bump vue & vite versions - Bump vue & vite versions

4
client.d.ts vendored

@ -0,0 +1,4 @@
// re-export vite client types
// with strict installers like pnpm, user won't be able to reference vite/client
// in project root
/// <reference types="vite/client" />

@ -1,6 +1,6 @@
{ {
"name": "vitepress", "name": "vitepress",
"version": "0.20.5", "version": "0.20.9",
"description": "Vite & Vue powered static site generator", "description": "Vite & Vue powered static site generator",
"main": "dist/node/index.js", "main": "dist/node/index.js",
"typings": "types/index.d.ts", "typings": "types/index.d.ts",
@ -10,7 +10,8 @@
"files": [ "files": [
"bin", "bin",
"dist", "dist",
"types" "types",
"client.d.ts"
], ],
"scripts": { "scripts": {
"dev": "run-s dev-shared dev-start", "dev": "run-s dev-shared dev-start",
@ -90,6 +91,7 @@
"@types/koa-static": "^4.0.1", "@types/koa-static": "^4.0.1",
"@types/lru-cache": "^5.1.0", "@types/lru-cache": "^5.1.0",
"@types/markdown-it": "^12.0.1", "@types/markdown-it": "^12.0.1",
"@types/micromatch": "^4.0.2",
"@types/node": "^15.6.1", "@types/node": "^15.6.1",
"@types/polka": "^0.5.3", "@types/polka": "^0.5.3",
"chalk": "^4.1.1", "chalk": "^4.1.1",
@ -102,6 +104,7 @@
"esbuild": "^0.13.4", "esbuild": "^0.13.4",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"execa": "^5.0.0", "execa": "^5.0.0",
"fast-glob": "^3.2.7",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"globby": "^11.0.3", "globby": "^11.0.3",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
@ -114,6 +117,7 @@
"markdown-it-container": "^3.0.0", "markdown-it-container": "^3.0.0",
"markdown-it-emoji": "^2.0.0", "markdown-it-emoji": "^2.0.0",
"markdown-it-table-of-contents": "^0.5.2", "markdown-it-table-of-contents": "^0.5.2",
"micromatch": "^4.0.4",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"ora": "^5.4.0", "ora": "^5.4.0",

@ -19,6 +19,7 @@ importers:
'@types/koa-static': ^4.0.1 '@types/koa-static': ^4.0.1
'@types/lru-cache': ^5.1.0 '@types/lru-cache': ^5.1.0
'@types/markdown-it': ^12.0.1 '@types/markdown-it': ^12.0.1
'@types/micromatch': ^4.0.2
'@types/node': ^15.6.1 '@types/node': ^15.6.1
'@types/polka': ^0.5.3 '@types/polka': ^0.5.3
'@vitejs/plugin-vue': ^2.0.0 '@vitejs/plugin-vue': ^2.0.0
@ -32,6 +33,7 @@ importers:
esbuild: ^0.13.4 esbuild: ^0.13.4
escape-html: ^1.0.3 escape-html: ^1.0.3
execa: ^5.0.0 execa: ^5.0.0
fast-glob: ^3.2.7
fs-extra: ^10.0.0 fs-extra: ^10.0.0
globby: ^11.0.3 globby: ^11.0.3
gray-matter: ^4.0.3 gray-matter: ^4.0.3
@ -44,6 +46,7 @@ importers:
markdown-it-container: ^3.0.0 markdown-it-container: ^3.0.0
markdown-it-emoji: ^2.0.0 markdown-it-emoji: ^2.0.0
markdown-it-table-of-contents: ^0.5.2 markdown-it-table-of-contents: ^0.5.2
micromatch: ^4.0.4
minimist: ^1.2.5 minimist: ^1.2.5
npm-run-all: ^4.1.5 npm-run-all: ^4.1.5
ora: ^5.4.0 ora: ^5.4.0
@ -81,6 +84,7 @@ importers:
'@types/koa-static': 4.0.2 '@types/koa-static': 4.0.2
'@types/lru-cache': 5.1.1 '@types/lru-cache': 5.1.1
'@types/markdown-it': 12.2.1 '@types/markdown-it': 12.2.1
'@types/micromatch': 4.0.2
'@types/node': 15.14.9 '@types/node': 15.14.9
'@types/polka': 0.5.3 '@types/polka': 0.5.3
chalk: 4.1.2 chalk: 4.1.2
@ -93,6 +97,7 @@ importers:
esbuild: 0.13.4 esbuild: 0.13.4
escape-html: 1.0.3 escape-html: 1.0.3
execa: 5.1.1 execa: 5.1.1
fast-glob: 3.2.7
fs-extra: 10.0.0 fs-extra: 10.0.0
globby: 11.0.4 globby: 11.0.4
gray-matter: 4.0.3 gray-matter: 4.0.3
@ -105,6 +110,7 @@ importers:
markdown-it-container: 3.0.0 markdown-it-container: 3.0.0
markdown-it-emoji: 2.0.0 markdown-it-emoji: 2.0.0
markdown-it-table-of-contents: 0.5.2 markdown-it-table-of-contents: 0.5.2
micromatch: 4.0.4
minimist: 1.2.5 minimist: 1.2.5
npm-run-all: 4.1.5 npm-run-all: 4.1.5
ora: 5.4.1 ora: 5.4.1
@ -1076,6 +1082,10 @@ packages:
'@types/node': 15.14.9 '@types/node': 15.14.9
dev: true dev: true
/@types/braces/3.0.1:
resolution: {integrity: sha512-+euflG6ygo4bn0JHtn4pYqcXwRtLvElQ7/nnjDu7iYG56H0+OhCd7d6Ug0IE3WcFpZozBKW2+80FUbv5QGk5AQ==}
dev: true
/@types/compression/1.7.2: /@types/compression/1.7.2:
resolution: {integrity: sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg==} resolution: {integrity: sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg==}
dependencies: dependencies:
@ -1231,6 +1241,12 @@ packages:
resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==} resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==}
dev: true dev: true
/@types/micromatch/4.0.2:
resolution: {integrity: sha512-oqXqVb0ci19GtH0vOA/U2TmHTcRY9kuZl4mqUxe0QmJAlIW13kzhuK5pi1i9+ngav8FjpSb9FVS/GE00GLX1VA==}
dependencies:
'@types/braces': 3.0.1
dev: true
/@types/mime/1.3.2: /@types/mime/1.3.2:
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
dev: true dev: true

@ -39,27 +39,43 @@ export async function renderPage(
)) ))
const pageData = JSON.parse(__pageData) const pageData = JSON.parse(__pageData)
const preloadLinks = ( let preloadLinks = config.mpa
config.mpa ? appChunk
? appChunk ? [appChunk.fileName]
? [appChunk.fileName] : []
: [] : result && appChunk
: result && appChunk ? [
? [ ...new Set([
// resolve imports for index.js + page.md.js and inject script tags for // resolve imports for index.js + page.md.js and inject script tags for
// them as well so we fetch everything as early as possible without having // them as well so we fetch everything as early as possible without having
// to wait for entry chunks to parse // to wait for entry chunks to parse
...resolvePageImports(config, page, result, appChunk), ...resolvePageImports(config, page, result, appChunk),
pageClientJsFileName, pageClientJsFileName,
appChunk.fileName appChunk.fileName
] ])
: [] ]
) : []
let prefetchLinks: string[] = []
const { shouldPreload } = config
if (shouldPreload) {
prefetchLinks = preloadLinks.filter((link) => !shouldPreload(link, page))
preloadLinks = preloadLinks.filter((link) => shouldPreload(link, page))
}
const preloadLinksString = preloadLinks
.map((file) => { .map((file) => {
return `<link rel="modulepreload" href="${siteData.base}${file}">` return `<link rel="modulepreload" href="${siteData.base}${file}">`
}) })
.join('\n ') .join('\n ')
const prefetchLinkString = prefetchLinks
.map((file) => {
return `<link rel="prefetch" href="${siteData.base}${file}">`
})
.join('\n ')
const stylesheetLink = cssChunk const stylesheetLink = cssChunk
? `<link rel="stylesheet" href="${siteData.base}${cssChunk.fileName}">` ? `<link rel="stylesheet" href="${siteData.base}${cssChunk.fileName}">`
: '' : ''
@ -103,7 +119,8 @@ export async function renderPage(
pageData.description || siteData.description pageData.description || siteData.description
}"> }">
${stylesheetLink} ${stylesheetLink}
${preloadLinks} ${preloadLinksString}
${prefetchLinkString}
${renderHead(head)} ${renderHead(head)}
</head> </head>
<body> <body>
@ -130,24 +147,22 @@ function resolvePageImports(
config: SiteConfig, config: SiteConfig,
page: string, page: string,
result: RollupOutput, result: RollupOutput,
indexChunk: OutputChunk appChunk: OutputChunk
) { ) {
// find the page's js chunk and inject script tags for its imports so that // find the page's js chunk and inject script tags for its imports so that
// they are start fetching as early as possible // they start fetching as early as possible
const srcPath = normalizePath( const srcPath = normalizePath(
fs.realpathSync(path.resolve(config.srcDir, page)) fs.realpathSync(path.resolve(config.srcDir, page))
) )
const pageChunk = result.output.find( const pageChunk = result.output.find(
(chunk) => chunk.type === 'chunk' && chunk.facadeModuleId === srcPath (chunk) => chunk.type === 'chunk' && chunk.facadeModuleId === srcPath
) as OutputChunk ) as OutputChunk
return Array.from( return [
new Set([ ...appChunk.imports,
...indexChunk.imports, ...appChunk.dynamicImports,
...indexChunk.dynamicImports, ...pageChunk.imports,
...pageChunk.imports, ...pageChunk.dynamicImports
...pageChunk.dynamicImports ]
])
)
} }
function renderHead(head: HeadConfig[]) { function renderHead(head: HeadConfig[]) {

@ -47,6 +47,7 @@ export interface UserConfig<ThemeConfig = any> {
srcDir?: string srcDir?: string
srcExclude?: string[] srcExclude?: string[]
shouldPreload?: (link: string, page: string) => boolean
/** /**
* Enable MPA / zero-JS mode * Enable MPA / zero-JS mode
@ -60,7 +61,11 @@ export type RawConfigExports =
| Promise<UserConfig> | Promise<UserConfig>
| (() => UserConfig | Promise<UserConfig>) | (() => UserConfig | Promise<UserConfig>)
export interface SiteConfig<ThemeConfig = any> { export interface SiteConfig<ThemeConfig = any>
extends Pick<
UserConfig,
'markdown' | 'vue' | 'vite' | 'shouldPreload' | 'mpa'
> {
root: string root: string
srcDir: string srcDir: string
site: SiteData<ThemeConfig> site: SiteData<ThemeConfig>
@ -70,10 +75,6 @@ export interface SiteConfig<ThemeConfig = any> {
tempDir: string tempDir: string
alias: AliasOptions alias: AliasOptions
pages: string[] pages: string[]
markdown: MarkdownOptions | undefined
vue: VuePluginOptions | undefined
vite: ViteConfig | undefined
mpa: boolean
} }
const resolve = (root: string, file: string) => const resolve = (root: string, file: string) =>
@ -127,6 +128,7 @@ export async function resolveConfig(
alias: resolveAliases(themeDir), alias: resolveAliases(themeDir),
vue: userConfig.vue, vue: userConfig.vue,
vite: userConfig.vite, vite: userConfig.vite,
shouldPreload: userConfig.shouldPreload,
mpa: !!userConfig.mpa mpa: !!userConfig.mpa
} }

@ -8,6 +8,7 @@ import {
import { DIST_CLIENT_PATH, APP_PATH, SITE_DATA_REQUEST_PATH } from './alias' import { DIST_CLIENT_PATH, APP_PATH, SITE_DATA_REQUEST_PATH } from './alias'
import { slash } from './utils/slash' import { slash } from './utils/slash'
import { OutputAsset, OutputChunk } from 'rollup' import { OutputAsset, OutputChunk } from 'rollup'
import { staticDataPlugin } from './staticDataPlugin'
const hashRE = /\.(\w+)\.js$/ const hashRE = /\.(\w+)\.js$/
const staticInjectMarkerRE = const staticInjectMarkerRE =
@ -273,5 +274,10 @@ export function createVitePressPlugin(
} }
} }
return [vitePressPlugin, vuePlugin, ...(userViteConfig?.plugins || [])] return [
vitePressPlugin,
vuePlugin,
...(userViteConfig?.plugins || []),
staticDataPlugin
]
} }

@ -0,0 +1,126 @@
// TODO figure out why it causes full page reload
import { Plugin, ViteDevServer, loadConfigFromFile, normalizePath } from 'vite'
import { dirname, relative } from 'path'
import { isMatch } from 'micromatch'
const loaderMatch = /\.data\.(j|t)s$/
let server: ViteDevServer
interface LoaderModule {
watch: string[] | string | undefined
load: () => any
}
interface CachedLoaderModule {
base: string
pattern: string[] | undefined
loader: () => any
}
const idToLoaderModulesMap: Record<string, CachedLoaderModule | undefined> =
Object.create(null)
// During build, the load hook will be called on the same file twice
// once for client and once for server build. Not only is this wasteful, it
// also leads to a race condition in loadConfigFromFile() that results in an
// fs unlink error. So we reuse the same Promise during build to avoid double
// loading.
let idToPendingPromiseMap: Record<string, Promise<string> | undefined> =
Object.create(null)
let isBuild = false
export const staticDataPlugin: Plugin = {
name: 'vitepress:data',
configResolved(config) {
isBuild = config.command === 'build'
},
configureServer(_server) {
server = _server
},
async load(id) {
if (loaderMatch.test(id)) {
let _resolve: ((res: any) => void) | undefined
if (isBuild) {
if (idToPendingPromiseMap[id]) {
return idToPendingPromiseMap[id]
}
idToPendingPromiseMap[id] = new Promise((r) => {
_resolve = r
})
}
const base = dirname(id)
let pattern: string[] | undefined
let loader: () => any
const existing = idToLoaderModulesMap[id]
if (existing) {
;({ pattern, loader } = existing)
} else {
// use vite's load config util as a away to load Node.js file with
// TS & native ESM support
const loaderModule = (await loadConfigFromFile({} as any, id))
?.config as LoaderModule
pattern =
typeof loaderModule.watch === 'string'
? [loaderModule.watch]
: loaderModule.watch
if (pattern) {
pattern = pattern.map((p) => {
return p.startsWith('./') ? p.slice(2) : p
})
}
loader = loaderModule.load
}
// load the data
const data = await loader()
// record loader module for HMR
if (server) {
idToLoaderModulesMap[id] = { base, pattern, loader }
}
const result = `export const data = JSON.parse(${JSON.stringify(
JSON.stringify(data)
)})`
if (_resolve) _resolve(result)
return result
}
},
transform(_code, id) {
if (server && loaderMatch.test(id)) {
// register this module as a glob importer
const { base, pattern } = idToLoaderModulesMap[id]!
;(server as any)._globImporters[id] = {
module: server.moduleGraph.getModuleById(id),
importGlobs: pattern?.map((pattern) => ({ base, pattern }))
}
}
return null
},
handleHotUpdate(ctx) {
for (const id in idToLoaderModulesMap) {
const { base, pattern } = idToLoaderModulesMap[id]!
const isLoaderFile = normalizePath(ctx.file) === id
if (isLoaderFile) {
// invalidate loader file
delete idToLoaderModulesMap[id]
}
if (
isLoaderFile ||
(pattern && isMatch(relative(base, ctx.file), pattern))
) {
ctx.modules.push(server.moduleGraph.getModuleById(id)!)
}
}
}
}
Loading…
Cancel
Save