feat(theme): add fs cache for encrypt and icons (#248)
This commit is contained in:
parent
a6079d69ae
commit
2a355705ff
@ -3,7 +3,7 @@ import type { Page } from 'vuepress/core'
|
|||||||
import type { PlumeThemeEncrypt, PlumeThemePageData } from '../../shared/index.js'
|
import type { PlumeThemeEncrypt, PlumeThemePageData } from '../../shared/index.js'
|
||||||
import { isNumber, isString, random, toArray } from '@pengzhanbo/utils'
|
import { isNumber, isString, random, toArray } from '@pengzhanbo/utils'
|
||||||
import { genSaltSync, hashSync } from 'bcrypt-ts'
|
import { genSaltSync, hashSync } from 'bcrypt-ts'
|
||||||
import { hash, logger, resolveContent, writeTemp } from '../utils/index.js'
|
import { createFsCache, type FsCache, hash, logger, resolveContent, writeTemp } from '../utils/index.js'
|
||||||
|
|
||||||
export type EncryptConfig = readonly [
|
export type EncryptConfig = readonly [
|
||||||
boolean, // global
|
boolean, // global
|
||||||
@ -16,19 +16,30 @@ export type EncryptConfig = readonly [
|
|||||||
const isStringLike = (value: unknown): boolean => isString(value) || isNumber(value)
|
const isStringLike = (value: unknown): boolean => isString(value) || isNumber(value)
|
||||||
const separator = ':'
|
const separator = ':'
|
||||||
let contentHash = ''
|
let contentHash = ''
|
||||||
|
let fsCache: FsCache<[string, EncryptConfig]> | null = null
|
||||||
|
|
||||||
export async function prepareEncrypt(app: App, encrypt?: PlumeThemeEncrypt) {
|
export async function prepareEncrypt(app: App, encrypt?: PlumeThemeEncrypt) {
|
||||||
const start = performance.now()
|
const start = performance.now()
|
||||||
|
|
||||||
|
if (!fsCache && app.env.isDev) {
|
||||||
|
fsCache = createFsCache(app, 'encrypt')
|
||||||
|
await fsCache.read()
|
||||||
|
}
|
||||||
|
contentHash = fsCache?.data?.[0] ?? ''
|
||||||
|
let resolvedEncrypt = fsCache?.data?.[1]
|
||||||
const currentHash = encrypt ? hash(JSON.stringify(encrypt)) : ''
|
const currentHash = encrypt ? hash(JSON.stringify(encrypt)) : ''
|
||||||
|
|
||||||
if (!contentHash || contentHash !== currentHash) {
|
if (!contentHash || contentHash !== currentHash || !resolvedEncrypt) {
|
||||||
contentHash = currentHash
|
contentHash = currentHash
|
||||||
const content = resolveContent(app, {
|
resolvedEncrypt = resolveEncrypt(encrypt)
|
||||||
name: 'encrypt',
|
|
||||||
content: resolveEncrypt(encrypt),
|
|
||||||
})
|
|
||||||
await writeTemp(app, 'internal/encrypt.js', content)
|
|
||||||
}
|
}
|
||||||
|
await writeTemp(app, 'internal/encrypt.js', resolveContent(app, {
|
||||||
|
name: 'encrypt',
|
||||||
|
content: resolvedEncrypt,
|
||||||
|
}))
|
||||||
|
|
||||||
|
fsCache?.write([currentHash, resolvedEncrypt])
|
||||||
|
|
||||||
if (app.env.isDebug) {
|
if (app.env.isDebug) {
|
||||||
logger.info(`Generate encrypt: ${(performance.now() - start).toFixed(2)}ms`)
|
logger.info(`Generate encrypt: ${(performance.now() - start).toFixed(2)}ms`)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { isArray, uniq } from '@pengzhanbo/utils'
|
|||||||
import { entries, isLinkAbsolute, isLinkHttp, isPlainObject } from '@vuepress/helper'
|
import { entries, isLinkAbsolute, isLinkHttp, isPlainObject } from '@vuepress/helper'
|
||||||
import { isPackageExists } from 'local-pkg'
|
import { isPackageExists } from 'local-pkg'
|
||||||
import { fs } from 'vuepress/utils'
|
import { fs } from 'vuepress/utils'
|
||||||
import { interopDefault, logger, nanoid, resolveContent, writeTemp } from '../utils/index.js'
|
import { createFsCache, type FsCache, interopDefault, logger, nanoid, resolveContent, writeTemp } from '../utils/index.js'
|
||||||
|
|
||||||
interface IconData {
|
interface IconData {
|
||||||
className: string
|
className: string
|
||||||
@ -26,6 +26,7 @@ const CSS_FILENAME = 'internal/iconify.css'
|
|||||||
const isInstalled = isPackageExists('@iconify/json')
|
const isInstalled = isPackageExists('@iconify/json')
|
||||||
let locate!: ((name: string) => any)
|
let locate!: ((name: string) => any)
|
||||||
|
|
||||||
|
let fsCache: FsCache<IconDataMap> | null = null
|
||||||
// { iconName: { className, content } }
|
// { iconName: { className, content } }
|
||||||
const cache: IconDataMap = {}
|
const cache: IconDataMap = {}
|
||||||
|
|
||||||
@ -41,13 +42,21 @@ export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOpti
|
|||||||
await writeTemp(app, JS_FILENAME, resolveContent(app, { name: 'icons', content: '{}' }))
|
await writeTemp(app, JS_FILENAME, resolveContent(app, { name: 'icons', content: '{}' }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!fsCache && app.env.isDev) {
|
||||||
|
fsCache = createFsCache(app, 'iconify')
|
||||||
|
await fsCache.read()
|
||||||
|
}
|
||||||
|
|
||||||
const iconList: string[] = []
|
const iconList: string[] = []
|
||||||
app.pages.forEach(page => iconList.push(...getIconsWithPage(page)))
|
app.pages.forEach(page => iconList.push(...getIconsWithPage(page)))
|
||||||
iconList.push(...getIconWithThemeConfig(localeOptions))
|
iconList.push(...getIconWithThemeConfig(localeOptions))
|
||||||
|
|
||||||
const collectMap: CollectMap = {}
|
const collectMap: CollectMap = {}
|
||||||
uniq(iconList).filter(icon => !cache[icon]).forEach((iconName) => {
|
uniq(iconList).filter((icon) => {
|
||||||
|
if (fsCache?.data?.[icon] && !cache[icon])
|
||||||
|
cache[icon] = fsCache.data[icon]
|
||||||
|
return !cache[icon]
|
||||||
|
}).forEach((iconName) => {
|
||||||
const [collect, name] = iconName.split(':')
|
const [collect, name] = iconName.split(':')
|
||||||
if (!collectMap[collect])
|
if (!collectMap[collect])
|
||||||
collectMap[collect] = []
|
collectMap[collect] = []
|
||||||
@ -94,6 +103,7 @@ export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOpti
|
|||||||
})),
|
})),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
fsCache?.write(cache)
|
||||||
if (app.env.isDebug) {
|
if (app.env.isDebug) {
|
||||||
logger.info(`Generate icons total time: ${(performance.now() - start).toFixed(2)}ms`)
|
logger.info(`Generate icons total time: ${(performance.now() - start).toFixed(2)}ms`)
|
||||||
}
|
}
|
||||||
|
|||||||
65
theme/src/node/utils/createFsCache.ts
Normal file
65
theme/src/node/utils/createFsCache.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import type { App } from 'vuepress'
|
||||||
|
import fs from 'node:fs/promises'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { hash } from 'vuepress/utils'
|
||||||
|
|
||||||
|
interface CacheData<T = any> {
|
||||||
|
hash: string
|
||||||
|
data: T | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FsCache<T> {
|
||||||
|
hash: string
|
||||||
|
data: T | null
|
||||||
|
read: () => Promise<T | null>
|
||||||
|
write: (data: T) => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
const CACHE_BASE = 'markdown'
|
||||||
|
|
||||||
|
export function createFsCache<T = any>(app: App, name: string): FsCache<T> {
|
||||||
|
const filepath = app.dir.cache(`${CACHE_BASE}/${name}.json`)
|
||||||
|
const cache: CacheData<T> = { hash: '', data: null }
|
||||||
|
|
||||||
|
const read = async (): Promise<T | null> => {
|
||||||
|
if (!cache.data) {
|
||||||
|
try {
|
||||||
|
const content = await fs.readFile(filepath, 'utf-8')
|
||||||
|
if (content) {
|
||||||
|
const res = JSON.parse(content) as CacheData<T>
|
||||||
|
cache.data = res.data ?? null
|
||||||
|
cache.hash = hash(res.hash || '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
|
}
|
||||||
|
return cache.data
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer: NodeJS.Timeout | null = null
|
||||||
|
const write = async (data: T) => {
|
||||||
|
const currentHash = hash(data)
|
||||||
|
if (cache.hash && currentHash === cache.hash)
|
||||||
|
return
|
||||||
|
|
||||||
|
cache.data = data
|
||||||
|
cache.hash = currentHash
|
||||||
|
|
||||||
|
timer && clearTimeout(timer)
|
||||||
|
timer = setTimeout(async () => {
|
||||||
|
await fs.mkdir(path.dirname(filepath), { recursive: true })
|
||||||
|
await fs.writeFile(filepath, JSON.stringify(cache), 'utf-8')
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
get hash() {
|
||||||
|
return cache.hash
|
||||||
|
},
|
||||||
|
get data() {
|
||||||
|
return cache.data
|
||||||
|
},
|
||||||
|
read,
|
||||||
|
write,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ export const THEME_NAME = 'vuepress-theme-plume'
|
|||||||
|
|
||||||
export const logger = new Logger(THEME_NAME)
|
export const logger = new Logger(THEME_NAME)
|
||||||
|
|
||||||
|
export * from './createFsCache.js'
|
||||||
export * from './deleteAttrs.js'
|
export * from './deleteAttrs.js'
|
||||||
export * from './hash.js'
|
export * from './hash.js'
|
||||||
export * from './interopDefault.js'
|
export * from './interopDefault.js'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user