diff --git a/plugins/plugin-blog-data/src/node/prepareBlogData.ts b/plugins/plugin-blog-data/src/node/prepareBlogData.ts index f3f821884..9ba25184d 100644 --- a/plugins/plugin-blog-data/src/node/prepareBlogData.ts +++ b/plugins/plugin-blog-data/src/node/prepareBlogData.ts @@ -1,3 +1,4 @@ +import { createHash } from 'node:crypto' import type { App, Page } from 'vuepress/core' import { colors, logger } from 'vuepress/utils' import type { BlogPostData, BlogPostDataItem } from '../shared/index.js' @@ -20,11 +21,8 @@ if (import.meta.hot) { const headingRe = /]*>.*?<\/h\1>/gi -function getTimestamp(time: Date): number { - return new Date(time).getTime() -} - const EXCERPT_SPLIT = '' +let contentHash: string | undefined export async function preparedBlogData(app: App, pageFilter: (id: string) => boolean, options: PluginOption): Promise { const start = performance.now() @@ -84,8 +82,20 @@ export const blogPostData = JSON.parse(${JSON.stringify( if (app.env.isDev) content += HMR_CODE - await app.writeTemp('internal/blogData.js', content) + const currentHash = hash(content) + if (!contentHash || contentHash !== currentHash) { + contentHash = currentHash + await app.writeTemp('internal/blogData.js', content) + } if (app.env.isDebug) logger.info(`\n[${colors.green('@vuepress-plume/plugin-blog-data')}] prepare blog data time spent: ${(performance.now() - start).toFixed(2)}ms`) } + +function getTimestamp(time: Date): number { + return new Date(time).getTime() +} + +function hash(content: string): string { + return createHash('md5').update(content).digest('hex') +} diff --git a/plugins/plugin-notes-data/src/node/prepareNotesData.ts b/plugins/plugin-notes-data/src/node/prepareNotesData.ts index 884709654..557c623e8 100644 --- a/plugins/plugin-notes-data/src/node/prepareNotesData.ts +++ b/plugins/plugin-notes-data/src/node/prepareNotesData.ts @@ -9,7 +9,7 @@ import type { NotesSidebar, NotesSidebarItem, } from '../shared/index.js' -import { ensureArray, normalizePath } from './utils.js' +import { ensureArray, hash, normalizePath } from './utils.js' const HMR_CODE = ` if (import.meta.webpackHot) { @@ -65,6 +65,7 @@ function resolvedNotesData(app: App, options: NotesDataOptions, result: NotesDat }) } +let contentHash: string | undefined export async function prepareNotesData(app: App, options: NotesDataOptions | NotesDataOptions[]) { const start = performance.now() const notesData: NotesData = {} @@ -78,7 +79,11 @@ export const notesData = ${JSON.stringify(notesData, null, 2)} if (app.env.isDev) content += HMR_CODE - await app.writeTemp('internal/notesData.js', content) + const currentHash = hash(content) + if (!contentHash || contentHash !== currentHash) { + contentHash = currentHash + await app.writeTemp('internal/notesData.js', content) + } if (app.env.isDebug) { logger.info( diff --git a/plugins/plugin-notes-data/src/node/utils.ts b/plugins/plugin-notes-data/src/node/utils.ts index edabc86d5..8e3da0425 100644 --- a/plugins/plugin-notes-data/src/node/utils.ts +++ b/plugins/plugin-notes-data/src/node/utils.ts @@ -1,3 +1,5 @@ +import { createHash } from 'node:crypto' + export function ensureArray(thing: T | T[] | null | undefined): T[] { if (Array.isArray(thing)) return thing @@ -13,3 +15,7 @@ export function normalizePath(str: string) { export function wait(time: number) { return new Promise(resolve => setTimeout(resolve, time)) } + +export function hash(content: string): string { + return createHash('md5').update(content).digest('hex') +} diff --git a/theme/src/node/prepare/index.ts b/theme/src/node/prepare/index.ts index aca88bf33..113c289f3 100644 --- a/theme/src/node/prepare/index.ts +++ b/theme/src/node/prepare/index.ts @@ -1,6 +1,6 @@ import type { App } from 'vuepress' import { watch } from 'chokidar' -import { prepareArticleTagColors } from './prepareArticleTagColor.js' +import { prepareArticleTagColors, updateArticleTagColor } from './prepareArticleTagColor.js' export async function setupPrepare(app: App): Promise { await prepareArticleTagColors(app) @@ -12,9 +12,9 @@ export function watchPrepare(app: App, watchers: any[]): void { ignoreInitial: true, }) - watcher.on('change', () => prepareArticleTagColors(app)) - watcher.on('add', () => prepareArticleTagColors(app)) - watcher.on('unlink', () => prepareArticleTagColors(app)) + watcher.on('change', () => updateArticleTagColor(app)) + watcher.on('add', () => updateArticleTagColor(app)) + watcher.on('unlink', () => updateArticleTagColor(app)) watchers.push(watcher) } diff --git a/theme/src/node/prepare/prepareArticleTagColor.ts b/theme/src/node/prepare/prepareArticleTagColor.ts index 66a5824b0..9e0f76836 100644 --- a/theme/src/node/prepare/prepareArticleTagColor.ts +++ b/theme/src/node/prepare/prepareArticleTagColor.ts @@ -1,6 +1,7 @@ import { toArray } from '@pengzhanbo/utils' import type { App } from 'vuepress' -import { nanoid } from '../utils.js' +import { fs } from 'vuepress/utils' +import { hash, nanoid } from '../utils.js' export type TagsColorsItem = readonly [ string, // normal color @@ -8,6 +9,9 @@ export type TagsColorsItem = readonly [ string, // background color ] +const TEMP_JS = 'internal/articleTagColors.js' +const TEMP_CSS = 'internal/articleTagColors.css' + export const PRESET: TagsColorsItem[] = [ ['#6aa1b7', '#5086a1', 'rgba(131, 208, 218, 0.314)'], ['#299764', '#18794e', 'rgba(16, 185, 129, 0.14)'], @@ -46,8 +50,45 @@ if (import.meta.hot) { // { index: className } const cache: Record = {} +const hashMap: { + js: string + css: string +} = { js: '', css: '' } export async function prepareArticleTagColors(app: App): Promise { + const [tempJS, tempCSS] = await Promise.all([ + readFile(app.dir.temp(TEMP_JS)), + readFile(app.dir.temp(TEMP_CSS)), + ]) + + if (tempJS) { + hashMap.js = hash(tempJS) + } + + if (tempCSS) { + hashMap.css = hash(tempCSS) + } + + await updateArticleTagColor(app) +} + +export async function updateArticleTagColor(app: App): Promise { + const { js, css } = genCode(app) + + const cssHash = hash(css) + if (!css || hashMap.css !== cssHash) { + hashMap.css = cssHash + await app.writeTemp(TEMP_CSS, css) + } + + const jsHash = hash(js) + if (hashMap.js !== jsHash) { + hashMap.js = jsHash + await app.writeTemp(TEMP_JS, js) + } +} + +export function genCode(app: App): { js: string, css: string } { const articleTagColors: Record = {} const tagList = new Set() @@ -70,16 +111,16 @@ export async function prepareArticleTagColors(app: App): Promise { } }) - let code = `\ + let js = `\ import './articleTagColors.css' export const articleTagColors = ${JSON.stringify(articleTagColors)} ` if (app.env.isDev) { - code += HMR_CODE + js += HMR_CODE } + const css = genCSS() - await app.writeTemp('internal/articleTagColors.css', genTagColorsStyle()) - await app.writeTemp('internal/articleTagColors.ts', code) + return { js, css } } function getTagCode(tag: string): number { @@ -91,7 +132,7 @@ function getTagCode(tag: string): number { return code % PRESET.length } -function genTagColorsStyle(): string { +function genCSS(): string { let css = '' for (const [code, className] of Object.entries(cache)) { @@ -109,3 +150,11 @@ function genTagColorsStyle(): string { return css } + +async function readFile(filepath: string): Promise { + try { + return await fs.readFile(filepath, 'utf-8') + } + catch {} + return '' +} diff --git a/theme/src/node/utils.ts b/theme/src/node/utils.ts index cbd049157..ddcd341f1 100644 --- a/theme/src/node/utils.ts +++ b/theme/src/node/utils.ts @@ -1,4 +1,5 @@ import process from 'node:process' +import { createHash } from 'node:crypto' import { customAlphabet } from 'nanoid' import { fs, getDirname, path } from 'vuepress/utils' import { Logger, ensureEndingSlash, ensureLeadingSlash } from '@vuepress/helper' @@ -12,6 +13,8 @@ export const templates = (url: string) => resolve('../templates', url) export const nanoid = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 8) +export const hash = (content: string) => createHash('md5').update(content).digest('hex') + export const logger = new Logger(THEME_NAME) export function getPackage() {