feat(theme): add @vuepress/plugin-cache
This commit is contained in:
parent
688c96452e
commit
62ac0b3371
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
@ -5,13 +5,19 @@
|
|||||||
"name": "dev",
|
"name": "dev",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal",
|
"type": "node-terminal",
|
||||||
"command": "pnpm run dev"
|
"command": "pnpm dev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "build",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"command": "pnpm build"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "docs:dev",
|
"name": "docs:dev",
|
||||||
"type": "node-terminal",
|
"type": "node-terminal",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"command": "pnpm run docs:dev"
|
"command": "pnpm docs:dev"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "docs:build",
|
"name": "docs:build",
|
||||||
|
|||||||
@ -161,6 +161,26 @@ interface BlogOptions {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### cache
|
||||||
|
|
||||||
|
- 类型: `false | 'memory' | 'filesystem'`
|
||||||
|
- 默认值: `filesystem`
|
||||||
|
- 详情:
|
||||||
|
|
||||||
|
是否启用 编译缓存,或配置缓存方式
|
||||||
|
|
||||||
|
此配置项用于解决 VuePress 启动速度慢的问题,在首次启动服务时,对编译结果进行缓存,二次启动时
|
||||||
|
直接读取缓存,跳过编译,从而加快启动速度。
|
||||||
|
|
||||||
|
- `false`:禁用 缓存
|
||||||
|
- `'memory'`:使用内存缓存,此方式可获得更快的启动速度,但随着项目文件数量增加,内存占用会增加,
|
||||||
|
适合文章数量较少的项目使用
|
||||||
|
- `'filesystem'`:使用文件系统缓存,此方式可获得相对快且稳定的启动速度,更适合内容多的项目使用
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
该字段不支持在 [主题配置文件 `plume.config.js`](./配置说明.md#主题配置文件) 中进行配置。
|
||||||
|
:::
|
||||||
|
|
||||||
### locales
|
### locales
|
||||||
|
|
||||||
- 类型: `Record<string, PlumeThemeLocaleConfig>`
|
- 类型: `Record<string, PlumeThemeLocaleConfig>`
|
||||||
|
|||||||
@ -19,11 +19,10 @@ import {
|
|||||||
transformerRenderWhitespace,
|
transformerRenderWhitespace,
|
||||||
} from '@shikijs/transformers'
|
} from '@shikijs/transformers'
|
||||||
import type { HighlighterOptions, ThemeOptions } from './types.js'
|
import type { HighlighterOptions, ThemeOptions } from './types.js'
|
||||||
import { LRUCache, attrsToLines, resolveLanguage } from './utils/index.js'
|
import { attrsToLines, resolveLanguage } from './utils/index.js'
|
||||||
import { defaultHoverInfoProcessor, transformerTwoslash } from './twoslash/rendererTransformer.js'
|
import { defaultHoverInfoProcessor, transformerTwoslash } from './twoslash/rendererTransformer.js'
|
||||||
|
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
|
||||||
const cache = new LRUCache<string, string>(64)
|
|
||||||
|
|
||||||
const vueRE = /-vue$/
|
const vueRE = /-vue$/
|
||||||
const mustacheRE = /\{\{.*?\}\}/g
|
const mustacheRE = /\{\{.*?\}\}/g
|
||||||
@ -32,7 +31,6 @@ const decorationsRE = /^\/\/ @decorations:(.*)\n/
|
|||||||
export async function highlight(
|
export async function highlight(
|
||||||
theme: ThemeOptions,
|
theme: ThemeOptions,
|
||||||
options: HighlighterOptions,
|
options: HighlighterOptions,
|
||||||
isDev: boolean,
|
|
||||||
): Promise<(str: string, lang: string, attrs: string) => string> {
|
): Promise<(str: string, lang: string, attrs: string) => string> {
|
||||||
const {
|
const {
|
||||||
defaultHighlightLang: defaultLang = '',
|
defaultHighlightLang: defaultLang = '',
|
||||||
@ -95,14 +93,6 @@ export async function highlight(
|
|||||||
let lang = resolveLanguage(language) || defaultLang
|
let lang = resolveLanguage(language) || defaultLang
|
||||||
const vPre = vueRE.test(lang) ? '' : 'v-pre'
|
const vPre = vueRE.test(lang) ? '' : 'v-pre'
|
||||||
|
|
||||||
const key = str + language + attrs
|
|
||||||
|
|
||||||
if (isDev) {
|
|
||||||
const rendered = cache.get(key)
|
|
||||||
if (rendered)
|
|
||||||
return rendered
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lang) {
|
if (lang) {
|
||||||
const langLoaded = loadedLanguages.includes(lang as any)
|
const langLoaded = loadedLanguages.includes(lang as any)
|
||||||
if (!langLoaded && !isPlainLang(lang) && !isSpecialLang(lang)) {
|
if (!langLoaded && !isPlainLang(lang) && !isSpecialLang(lang)) {
|
||||||
@ -183,9 +173,6 @@ export async function highlight(
|
|||||||
|
|
||||||
const rendered = restoreMustache(highlighted)
|
const rendered = restoreMustache(highlighted)
|
||||||
|
|
||||||
if (isDev)
|
|
||||||
cache.set(key, rendered)
|
|
||||||
|
|
||||||
return rendered
|
return rendered
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export function shikiPlugin({
|
|||||||
extendsMarkdown: async (md, app) => {
|
extendsMarkdown: async (md, app) => {
|
||||||
const theme = options.theme ?? { light: 'github-light', dark: 'github-dark' }
|
const theme = options.theme ?? { light: 'github-light', dark: 'github-dark' }
|
||||||
|
|
||||||
md.options.highlight = await highlight(theme, options, app.env.isDev)
|
md.options.highlight = await highlight(theme, options)
|
||||||
|
|
||||||
md.use(highlightLinesPlugin)
|
md.use(highlightLinesPlugin)
|
||||||
md.use<PreWrapperOptions>(preWrapperPlugin, {
|
md.use<PreWrapperOptions>(preWrapperPlugin, {
|
||||||
|
|||||||
35
pnpm-lock.yaml
generated
35
pnpm-lock.yaml
generated
@ -259,6 +259,9 @@ importers:
|
|||||||
|
|
||||||
theme:
|
theme:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@iconify/vue':
|
||||||
|
specifier: ^4.1.2
|
||||||
|
version: 4.1.2(vue@3.4.33(typescript@5.5.3))
|
||||||
'@pengzhanbo/utils':
|
'@pengzhanbo/utils':
|
||||||
specifier: ^1.1.2
|
specifier: ^1.1.2
|
||||||
version: 1.1.2
|
version: 1.1.2
|
||||||
@ -283,6 +286,9 @@ importers:
|
|||||||
'@vuepress/plugin-active-header-links':
|
'@vuepress/plugin-active-header-links':
|
||||||
specifier: 2.0.0-rc.39
|
specifier: 2.0.0-rc.39
|
||||||
version: 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))
|
version: 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))
|
||||||
|
'@vuepress/plugin-cache':
|
||||||
|
specifier: 2.0.0-rc.39
|
||||||
|
version: 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))
|
||||||
'@vuepress/plugin-comment':
|
'@vuepress/plugin-comment':
|
||||||
specifier: 2.0.0-rc.39
|
specifier: 2.0.0-rc.39
|
||||||
version: 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))
|
version: 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))
|
||||||
@ -361,6 +367,10 @@ importers:
|
|||||||
vuepress-plugin-md-power:
|
vuepress-plugin-md-power:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../plugins/plugin-md-power
|
version: link:../plugins/plugin-md-power
|
||||||
|
devDependencies:
|
||||||
|
'@iconify/json':
|
||||||
|
specifier: ^2.2.229
|
||||||
|
version: 2.2.229
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@ -1934,6 +1944,11 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vuepress: 2.0.0-rc.14
|
vuepress: 2.0.0-rc.14
|
||||||
|
|
||||||
|
'@vuepress/plugin-cache@2.0.0-rc.39':
|
||||||
|
resolution: {integrity: sha512-PVsC797lGMuu8L7jtW9vv2hYM+d5qq5fbWwBJuSyRXEdpcwryhAjGWnz9F19dYe5KWLYG6EbCoANTQObmiyBag==}
|
||||||
|
peerDependencies:
|
||||||
|
vuepress: 2.0.0-rc.14
|
||||||
|
|
||||||
'@vuepress/plugin-comment@2.0.0-rc.39':
|
'@vuepress/plugin-comment@2.0.0-rc.39':
|
||||||
resolution: {integrity: sha512-/oCS+0wH/MtE4c1HUKlqH/tj70oXSz/tfR1hsHj8F8wiZ+IVJxexvtzMKk0vdRmYnH4nqeZh6dg5ggSJjrLEZQ==}
|
resolution: {integrity: sha512-/oCS+0wH/MtE4c1HUKlqH/tj70oXSz/tfR1hsHj8F8wiZ+IVJxexvtzMKk0vdRmYnH4nqeZh6dg5ggSJjrLEZQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -3981,14 +3996,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==}
|
resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
lru-cache@10.0.1:
|
|
||||||
resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==}
|
|
||||||
engines: {node: 14 || >=16.14}
|
|
||||||
|
|
||||||
lru-cache@10.0.2:
|
lru-cache@10.0.2:
|
||||||
resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==}
|
resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==}
|
||||||
engines: {node: 14 || >=16.14}
|
engines: {node: 14 || >=16.14}
|
||||||
|
|
||||||
|
lru-cache@10.4.3:
|
||||||
|
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||||
|
|
||||||
lru-cache@11.0.0:
|
lru-cache@11.0.0:
|
||||||
resolution: {integrity: sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==}
|
resolution: {integrity: sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==}
|
||||||
engines: {node: 20 || >=22}
|
engines: {node: 20 || >=22}
|
||||||
@ -7269,6 +7283,11 @@ snapshots:
|
|||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
|
'@vuepress/plugin-cache@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))':
|
||||||
|
dependencies:
|
||||||
|
lru-cache: 10.4.3
|
||||||
|
vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3))
|
||||||
|
|
||||||
'@vuepress/plugin-comment@2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))':
|
'@vuepress/plugin-comment@2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vuepress/helper': 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))
|
'@vuepress/helper': 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))
|
||||||
@ -9534,11 +9553,11 @@ snapshots:
|
|||||||
|
|
||||||
longest@2.0.1: {}
|
longest@2.0.1: {}
|
||||||
|
|
||||||
lru-cache@10.0.1: {}
|
|
||||||
|
|
||||||
lru-cache@10.0.2:
|
lru-cache@10.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.6.0
|
semver: 7.6.3
|
||||||
|
|
||||||
|
lru-cache@10.4.3: {}
|
||||||
|
|
||||||
lru-cache@11.0.0: {}
|
lru-cache@11.0.0: {}
|
||||||
|
|
||||||
@ -10331,7 +10350,7 @@ snapshots:
|
|||||||
|
|
||||||
path-scurry@1.10.1:
|
path-scurry@1.10.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
lru-cache: 10.0.1
|
lru-cache: 10.0.2
|
||||||
minipass: 5.0.0
|
minipass: 5.0.0
|
||||||
|
|
||||||
path-scurry@2.0.0:
|
path-scurry@2.0.0:
|
||||||
|
|||||||
@ -59,9 +59,16 @@
|
|||||||
"tsup:watch": "tsup --config tsup.config.ts --watch"
|
"tsup:watch": "tsup --config tsup.config.ts --watch"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"@iconify/json": "^2",
|
||||||
"vuepress": "2.0.0-rc.14"
|
"vuepress": "2.0.0-rc.14"
|
||||||
},
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@iconify/json": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@iconify/vue": "^4.1.2",
|
||||||
"@pengzhanbo/utils": "^1.1.2",
|
"@pengzhanbo/utils": "^1.1.2",
|
||||||
"@vuepress-plume/plugin-content-update": "workspace:*",
|
"@vuepress-plume/plugin-content-update": "workspace:*",
|
||||||
"@vuepress-plume/plugin-fonts": "workspace:*",
|
"@vuepress-plume/plugin-fonts": "workspace:*",
|
||||||
@ -70,6 +77,7 @@
|
|||||||
"@vuepress-plume/plugin-shikiji": "workspace:*",
|
"@vuepress-plume/plugin-shikiji": "workspace:*",
|
||||||
"@vuepress/helper": "2.0.0-rc.39",
|
"@vuepress/helper": "2.0.0-rc.39",
|
||||||
"@vuepress/plugin-active-header-links": "2.0.0-rc.39",
|
"@vuepress/plugin-active-header-links": "2.0.0-rc.39",
|
||||||
|
"@vuepress/plugin-cache": "2.0.0-rc.39",
|
||||||
"@vuepress/plugin-comment": "2.0.0-rc.39",
|
"@vuepress/plugin-comment": "2.0.0-rc.39",
|
||||||
"@vuepress/plugin-docsearch": "2.0.0-rc.39",
|
"@vuepress/plugin-docsearch": "2.0.0-rc.39",
|
||||||
"@vuepress/plugin-git": "2.0.0-rc.38",
|
"@vuepress/plugin-git": "2.0.0-rc.38",
|
||||||
@ -95,5 +103,8 @@
|
|||||||
"vue-router": "^4.4.0",
|
"vue-router": "^4.4.0",
|
||||||
"vuepress-plugin-md-enhance": "2.0.0-rc.52",
|
"vuepress-plugin-md-enhance": "2.0.0-rc.52",
|
||||||
"vuepress-plugin-md-power": "workspace:*"
|
"vuepress-plugin-md-power": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@iconify/json": "^2.2.229"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export function resolveThemeOptions({
|
|||||||
plugins,
|
plugins,
|
||||||
hostname,
|
hostname,
|
||||||
configFile,
|
configFile,
|
||||||
|
cache,
|
||||||
...localeOptions
|
...localeOptions
|
||||||
}: PlumeThemeOptions) {
|
}: PlumeThemeOptions) {
|
||||||
const pluginOptions = plugins ?? themePlugins ?? {}
|
const pluginOptions = plugins ?? themePlugins ?? {}
|
||||||
@ -17,6 +18,7 @@ export function resolveThemeOptions({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
cache,
|
||||||
configFile,
|
configFile,
|
||||||
pluginOptions,
|
pluginOptions,
|
||||||
hostname,
|
hostname,
|
||||||
|
|||||||
@ -1,134 +0,0 @@
|
|||||||
/**
|
|
||||||
* 针对主题使用了 shiki + twoslash, 以及各种各样的对 markdown 的扩展,
|
|
||||||
* 导致了 markdown render 的速度变得越来越慢,如果每次启动都全量编译,那么时间开销会非常夸张。
|
|
||||||
* 因此,对 markdown render 包装一层 缓存,通过 content hash 对比内容是否有更新,
|
|
||||||
* 没有更新的直接应用缓存从而跳过编译过程,加快启动速度。
|
|
||||||
*
|
|
||||||
* 此功能计划做成独立的插件,但还不确定是放在 vuepress/ecosystem 还是在 主题插件内,
|
|
||||||
* 也有可能到 vuepress/core 仓库中进行更深度的优化。
|
|
||||||
* 因此,先在本主题中进行 实验性验证。
|
|
||||||
*
|
|
||||||
* 使用此功能后,本主题原本的启动耗时,由每次 13s 左右 优化到 二次启动时 1.2s 左右。
|
|
||||||
* 基本只剩下 vuepress 本身的开销和 加载 shiki 所有语言带来 0.5s 左右的开销。
|
|
||||||
*/
|
|
||||||
import process from 'node:process'
|
|
||||||
import { fs, path } from 'vuepress/utils'
|
|
||||||
import type { App } from 'vuepress'
|
|
||||||
import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
|
|
||||||
import { hash } from './utils/index.js'
|
|
||||||
|
|
||||||
export interface CacheData {
|
|
||||||
content: string
|
|
||||||
env: MarkdownEnv
|
|
||||||
}
|
|
||||||
|
|
||||||
// { [filepath]: CacheDta }
|
|
||||||
export type Cache = Record<string, CacheData>
|
|
||||||
|
|
||||||
// { [filepath]: hash }
|
|
||||||
export type Metadata = Record<string, string>
|
|
||||||
|
|
||||||
const CACHE_DIR = 'markdown/rendered'
|
|
||||||
const META_FILE = '_metadata.json'
|
|
||||||
|
|
||||||
export async function extendsMarkdown(md: Markdown, app: App): Promise<void> {
|
|
||||||
if (app.env.isBuild && !fs.existsSync(app.dir.cache(CACHE_DIR))) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const basename = app.dir.cache(CACHE_DIR)
|
|
||||||
|
|
||||||
await fs.ensureDir(basename)
|
|
||||||
|
|
||||||
const speed = checkIOSpeed(basename)
|
|
||||||
|
|
||||||
const metaFilepath = `${basename}/${META_FILE}`
|
|
||||||
|
|
||||||
const metadata = (await readFile<Metadata>(metaFilepath)) || {}
|
|
||||||
|
|
||||||
let timer: ReturnType<typeof setTimeout> | null = null
|
|
||||||
const update = (filepath: string, data: CacheData): void => {
|
|
||||||
writeFile(`${basename}/${filepath}`, data)
|
|
||||||
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer)
|
|
||||||
}
|
|
||||||
timer = setTimeout(async () => writeFile(metaFilepath, metadata), 200)
|
|
||||||
}
|
|
||||||
const rawRender = md.render
|
|
||||||
md.render = (input, env: MarkdownEnv) => {
|
|
||||||
const filepath = env.filePathRelative
|
|
||||||
|
|
||||||
if (!filepath) {
|
|
||||||
return rawRender(input, env)
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = hash(input)
|
|
||||||
const filename = normalizeFilename(filepath)
|
|
||||||
|
|
||||||
if (metadata[filepath] === key) {
|
|
||||||
const cached = readFileSync<CacheData>(`${basename}/${filename}`)
|
|
||||||
if (cached) {
|
|
||||||
Object.assign(env, cached.env)
|
|
||||||
return cached.content
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
metadata[filepath] = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const start = performance.now()
|
|
||||||
const content = rawRender(input, env)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* High-frequency I/O is also a time-consuming operation,
|
|
||||||
* therefore, for render operations with low overhead, caching is not performed.
|
|
||||||
*/
|
|
||||||
if (performance.now() - start > speed) {
|
|
||||||
metadata[filepath] = key
|
|
||||||
update(filename, { content, env })
|
|
||||||
}
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeFilename(filename: string): string {
|
|
||||||
return hash(filename).slice(0, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function readFile<T = any>(filepath: string): Promise<T | null> {
|
|
||||||
try {
|
|
||||||
const content = await fs.readFile(filepath, 'utf-8')
|
|
||||||
return JSON.parse(content) as T
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function readFileSync<T = any>(filepath: string): T | null {
|
|
||||||
try {
|
|
||||||
const content = fs.readFileSync(filepath, 'utf-8')
|
|
||||||
return JSON.parse(content) as T
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeFile<T = any>(filepath: string, data: T): Promise<void> {
|
|
||||||
return await fs.writeFile(filepath, JSON.stringify(data), 'utf-8')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function checkIOSpeed(cwd = process.cwd()): number {
|
|
||||||
try {
|
|
||||||
const tmp = path.join(cwd, 'tmp')
|
|
||||||
fs.writeFileSync(tmp, '{}', 'utf-8')
|
|
||||||
const start = performance.now()
|
|
||||||
readFileSync(tmp)
|
|
||||||
const end = performance.now()
|
|
||||||
fs.unlinkSync(tmp)
|
|
||||||
return end - start
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return 0.15
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import type { App, PluginConfig } from 'vuepress/core'
|
import type { App, PluginConfig } from 'vuepress/core'
|
||||||
import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
|
import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
|
||||||
|
import { cachePlugin } from '@vuepress/plugin-cache'
|
||||||
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
|
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
|
||||||
import { gitPlugin } from '@vuepress/plugin-git'
|
import { gitPlugin } from '@vuepress/plugin-git'
|
||||||
import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
|
import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
|
||||||
@ -27,12 +28,14 @@ export interface SetupPluginOptions {
|
|||||||
app: App
|
app: App
|
||||||
pluginOptions: PlumeThemePluginOptions
|
pluginOptions: PlumeThemePluginOptions
|
||||||
hostname?: string
|
hostname?: string
|
||||||
|
cache?: false | 'memory' | 'filesystem'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPlugins({
|
export function getPlugins({
|
||||||
app,
|
app,
|
||||||
pluginOptions,
|
pluginOptions,
|
||||||
hostname,
|
hostname,
|
||||||
|
cache,
|
||||||
}: SetupPluginOptions): PluginConfig {
|
}: SetupPluginOptions): PluginConfig {
|
||||||
const isProd = !app.env.isDev
|
const isProd = !app.env.isDev
|
||||||
|
|
||||||
@ -156,5 +159,9 @@ export function getPlugins({
|
|||||||
plugins.push(seoPlugin({ hostname }))
|
plugins.push(seoPlugin({ hostname }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cache !== false) {
|
||||||
|
plugins.push(cachePlugin({ type: cache || 'filesystem' }))
|
||||||
|
}
|
||||||
|
|
||||||
return plugins
|
return plugins
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,6 @@ import {
|
|||||||
} from './autoFrontmatter/index.js'
|
} from './autoFrontmatter/index.js'
|
||||||
import { prepareData, watchPrepare } from './prepare/index.js'
|
import { prepareData, watchPrepare } from './prepare/index.js'
|
||||||
import { prepareThemeData } from './prepare/prepareThemeData.js'
|
import { prepareThemeData } from './prepare/prepareThemeData.js'
|
||||||
import { extendsMarkdown } from './extendsMarkdown.js'
|
|
||||||
|
|
||||||
export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
||||||
const {
|
const {
|
||||||
@ -34,6 +33,7 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
|||||||
pluginOptions,
|
pluginOptions,
|
||||||
hostname,
|
hostname,
|
||||||
configFile,
|
configFile,
|
||||||
|
cache,
|
||||||
} = resolveThemeOptions(options)
|
} = resolveThemeOptions(options)
|
||||||
|
|
||||||
return (app) => {
|
return (app) => {
|
||||||
@ -65,7 +65,7 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
|||||||
|
|
||||||
alias: resolveAlias(),
|
alias: resolveAlias(),
|
||||||
|
|
||||||
plugins: getPlugins({ app, pluginOptions, hostname }),
|
plugins: getPlugins({ app, pluginOptions, hostname, cache }),
|
||||||
|
|
||||||
onInitialized: async (app) => {
|
onInitialized: async (app) => {
|
||||||
const { localeOptions } = await waitForConfigLoaded()
|
const { localeOptions } = await waitForConfigLoaded()
|
||||||
@ -90,14 +90,14 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
|||||||
},
|
},
|
||||||
|
|
||||||
extendsPage: async (page) => {
|
extendsPage: async (page) => {
|
||||||
const { localeOptions } = await waitForConfigLoaded()
|
const { localeOptions, autoFrontmatter } = await waitForConfigLoaded()
|
||||||
await waitForAutoFrontmatter()
|
if ((autoFrontmatter ?? pluginOptions.frontmatter) !== false) {
|
||||||
|
await waitForAutoFrontmatter()
|
||||||
|
}
|
||||||
extendsPageData(page as Page<PlumeThemePageData>, localeOptions)
|
extendsPageData(page as Page<PlumeThemePageData>, localeOptions)
|
||||||
resolvePageHead(page, localeOptions)
|
resolvePageHead(page, localeOptions)
|
||||||
},
|
},
|
||||||
|
|
||||||
extendsMarkdown,
|
|
||||||
|
|
||||||
extendsBundlerOptions,
|
extendsBundlerOptions,
|
||||||
|
|
||||||
templateBuildRenderer,
|
templateBuildRenderer,
|
||||||
|
|||||||
@ -23,6 +23,13 @@ export interface PlumeThemeOptions extends PlumeThemeLocaleOptions {
|
|||||||
*/
|
*/
|
||||||
hostname?: string
|
hostname?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用编译缓存
|
||||||
|
*
|
||||||
|
* @default 'filesystem'
|
||||||
|
*/
|
||||||
|
cache?: false | 'memory' | 'filesystem'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密配置
|
* 加密配置
|
||||||
*/
|
*/
|
||||||
@ -33,6 +40,9 @@ export interface PlumeThemeOptions extends PlumeThemeLocaleOptions {
|
|||||||
*/
|
*/
|
||||||
configFile?: string
|
configFile?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动插入 frontmatter
|
||||||
|
*/
|
||||||
autoFrontmatter?: false | Omit<AutoFrontmatter, 'frontmatter'>
|
autoFrontmatter?: false | Omit<AutoFrontmatter, 'frontmatter'>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,13 +60,16 @@ export interface PlumeThemePluginOptions {
|
|||||||
* @deprecated
|
* @deprecated
|
||||||
* 请使用 [@vuepress/plugin-baidu-analytics](https://ecosystem.vuejs.press/zh/plugins/analytics/baidu-analytics.html) 代替
|
* 请使用 [@vuepress/plugin-baidu-analytics](https://ecosystem.vuejs.press/zh/plugins/analytics/baidu-analytics.html) 代替
|
||||||
*/
|
*/
|
||||||
baiduTongji?: never
|
baiduTongji?: false | { key: string }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated 使用 `autoFrontmatter` 代替
|
* @deprecated 使用 `autoFrontmatter` 代替
|
||||||
*/
|
*/
|
||||||
frontmatter?: Omit<AutoFrontmatter, 'frontmatter'>
|
frontmatter?: Omit<AutoFrontmatter, 'frontmatter'>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阅读时间、字数统计
|
||||||
|
*/
|
||||||
readingTime?: false | ReadingTimePluginOptions
|
readingTime?: false | ReadingTimePluginOptions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user