Skip to content

Commit

Permalink
Merge 215ea2e into bb739df
Browse files Browse the repository at this point in the history
  • Loading branch information
pengzhanbo committed Jun 24, 2024
2 parents bb739df + 215ea2e commit 5a78c02
Show file tree
Hide file tree
Showing 13 changed files with 263 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/.vuepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { footnote } from '@mdit/plugin-footnote'
import { viteBundler } from '@vuepress/bundler-vite'
import { webpackBundler } from '@vuepress/bundler-webpack'
import { getRealPath } from '@vuepress/helper'
import { cachePlugin } from '@vuepress/plugin-cache'
import { catalogPlugin } from '@vuepress/plugin-catalog'
import { commentPlugin } from '@vuepress/plugin-comment'
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
Expand Down Expand Up @@ -114,5 +115,6 @@ export default defineUserConfig({
notationWordHighlight: true,
})
: [],
cachePlugin(),
],
}) as UserConfig
3 changes: 2 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"scripts": {
"docs:build": "vuepress build . --clean-cache --clean-temp",
"docs:build-webpack": "DOCS_BUNDLER=webpack pnpm docs:build",
"docs:dev": "vuepress dev . --clean-cache --clean-temp",
"docs:dev": "vuepress dev .",
"docs:dev-webpack": "DOCS_BUNDLER=webpack pnpm docs:dev",
"docs:serve": "http-server -a localhost .vuepress/dist"
},
Expand All @@ -29,6 +29,7 @@
"@vuepress/plugin-register-components": "workspace:*",
"@vuepress/plugin-search": "workspace:*",
"@vuepress/plugin-shiki": "workspace:*",
"@vuepress/plugin-cache": "workspace:*",
"@vuepress/theme-default": "workspace:*",
"mathjax-full": "3.2.2",
"sass-loader": "^14.2.1",
Expand Down
4 changes: 4 additions & 0 deletions plugins/tools/plugin-cache/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
42 changes: 42 additions & 0 deletions plugins/tools/plugin-cache/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "@vuepress/plugin-cache",
"version": "2.0.0-rc.37",
"description": "VuePress plugin - cache",
"keywords": [
"vuepress-plugin",
"vuepress",
"plugin",
"cache"
],
"homepage": "https://ecosystem.vuejs.press/plugins/tools/cache.html",
"bugs": {
"url": "https://github.com/vuepress/ecosystem/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuepress/ecosystem.git",
"directory": "plugins/tools/plugin-cache"
},
"license": "MIT",
"author": "pengzhanbo",
"type": "module",
"exports": {
".": "./lib/node/index.js",
"./package.json": "./package.json"
},
"main": "./lib/node/index.js",
"types": "./lib/node/index.d.ts",
"files": [
"lib"
],
"scripts": {
"build": "tsc -b tsconfig.build.json",
"clean": "rimraf --glob ./lib ./*.tsbuildinfo"
},
"peerDependencies": {
"vuepress": "2.0.0-rc.14"
},
"publishConfig": {
"access": "public"
}
}
19 changes: 19 additions & 0 deletions plugins/tools/plugin-cache/src/node/cachePlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Plugin } from 'vuepress/core'
import { highlightCache } from './highlightCache.js'
import { renderCache } from './renderCache.js'

/**
* Cache markdown rendering, optimize compilation speed.
*
* This plugin is recommended to be placed after all other plugins to ensure maximum utilization of the cache.
*/
export const cachePlugin = (): Plugin => {
return {
name: '@vuepress/plugin-cache',

async extendsMarkdown(md, app) {
highlightCache(md, app)
await renderCache(md, app)
},
}
}
38 changes: 38 additions & 0 deletions plugins/tools/plugin-cache/src/node/highlightCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Code highlight is a relatively time-consuming operation,
* especially in `shiki` where enabling numerous transformers can significantly impact performance.
* This effect is particularly noticeable with tools like `twoslash`,
* which require type compilation and may lead to individual code blocks taking over 500ms to process.
*
* When there are multiple code blocks, focusing only on modifying parts of the code blocks while
* still compiling all code blocks entirely can lead to unnecessary overhead.
* Therefore, using the LRU cache algorithm can help by storing unchanged code blocks'
* highlighted results and only processing the parts that have been modified.
*/
import type { App } from 'vuepress'
import type { Markdown } from 'vuepress/markdown'
import { LRUCache } from './lru.js'
import { hash } from './utils.js'

const cache = new LRUCache<string, string>(64)

export const highlightCache = (md: Markdown, app: App): void => {
/**
* Cache is only needed in development mode to enhance the development experience.
*/
if (!app.env.isDev) return

const highlight = md.options.highlight

md.options.highlight = (...args) => {
const key = hash(args.join(''))
const cached = cache.get(key)

if (cached) return cached

const content = highlight?.(...args) ?? ''
cache.set(key, content)

return content
}
}
1 change: 1 addition & 0 deletions plugins/tools/plugin-cache/src/node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './cachePlugin.js'
37 changes: 37 additions & 0 deletions plugins/tools/plugin-cache/src/node/lru.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// adapted from https://stackoverflow.com/a/46432113/11613622

export class LRUCache<K, V> {
private max: number
private cache: Map<K, V>

constructor(max = 10) {
this.max = max
this.cache = new Map<K, V>()
}

get(key: K): V | undefined {
const item = this.cache.get(key)
if (item !== undefined) {
// refresh key
this.cache.delete(key)
this.cache.set(key, item)
}
return item
}

set(key: K, val: V): void {
// refresh key
if (this.cache.has(key)) this.cache.delete(key)
// evict oldest
else if (this.cache.size === this.max) this.cache.delete(this.first()!)
this.cache.set(key, val)
}

first(): K | undefined {
return this.cache.keys().next().value
}

clear(): void {
this.cache.clear()
}
}
81 changes: 81 additions & 0 deletions plugins/tools/plugin-cache/src/node/renderCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* When various features are added to markdown, the compilation speed of a single markdown file
* will become slower, especially when there are many pages in the project,
* causing the startup of the vuepress development service to become very slow and time-consuming.
* This plugin will cache the `markdown render` result during the initial compilation process.
* During subsequent compilations, if the content has not been modified,
* compilation will be skipped directly, thus speeding up the second startup of vuepress.
*/
import type { App } from 'vuepress'
import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
import { fs } from 'vuepress/utils'
import { hash, readFile, writeFile } from './utils.js'

export interface CacheDta {
content: string
env: MarkdownEnv
}

// { [filepath]: CacheDta }
export type Cache = Record<string, CacheDta>

// { [filepath]: hash }
export type Metadata = Record<string, string>

const CACHE_DIR = 'markdown/rendered'
const META_FILE = '_metadata.json'
const CACHE_FILE = '_cache.json'

export const renderCache = async (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)
const metaFilepath = `${basename}/${META_FILE}`
const cacheFilepath = `${basename}/${CACHE_FILE}`

await fs.ensureDir(basename)

const [metadata, cache] = await Promise.all([
readFile<Metadata>(metaFilepath),
readFile<Cache>(cacheFilepath),
])

let timer: ReturnType<typeof setTimeout> | null = null
const update = async (): Promise<void> => {
timer && clearTimeout(timer)
timer = setTimeout(
async () =>
await Promise.all([
writeFile(metaFilepath, metadata),
writeFile(cacheFilepath, cache),
]),
200,
)
}

const rawRender = md.render
md.render = (input, env: MarkdownEnv) => {
const filepath = env.filePathRelative

if (!filepath) {
return rawRender(input, env)
}

const key = hash(input)
if (metadata[filepath] === key && cache[filepath]) {
const cached = cache[filepath]
Object.assign(env, cached.env)
return cached.content
}

const content = rawRender(input, env)
metadata[filepath] = key
cache[filepath] = { content, env }

update()

return content
}
}
19 changes: 19 additions & 0 deletions plugins/tools/plugin-cache/src/node/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createHash } from 'node:crypto'
import { fs } from 'vuepress/utils'

export const hash = (data: string): string =>
createHash('md5').update(data).digest('hex')

export const readFile = async <T = any>(filepath: string): Promise<T> => {
try {
const content = await fs.readFile(filepath, 'utf-8')
return JSON.parse(content) as T
} catch {
return {} as T
}
}

export const writeFile = async <T = any>(
filepath: string,
data: T,
): Promise<void> => await fs.writeFile(filepath, JSON.stringify(data), 'utf-8')
8 changes: 8 additions & 0 deletions plugins/tools/plugin-cache/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../../tsconfig.build.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib"
},
"include": ["./src"]
}
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
{
"path": "./plugins/tools/plugin-register-components/tsconfig.build.json"
},
{ "path": "./plugins/tools/plugin-cache/tsconfig.build.json" },

// themes
{ "path": "./themes/theme-default/tsconfig.build.json" },
Expand Down

0 comments on commit 5a78c02

Please sign in to comment.