feat(theme): add theme deps version detection (#551)

* feat(theme): add theme deps version detection

* chore: tweak
This commit is contained in:
pengzhanbo 2025-04-07 07:38:52 +08:00 committed by GitHub
parent 560eef0a48
commit 487448d611
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 161 additions and 24 deletions

View File

@ -1,23 +1,3 @@
import type { ThemeOptions } from '../../shared/index.js'
import { detectDependencies } from './dependency.js'
import { detectMarkdown } from './markdown.js'
import { detectPlugins } from './plugins.js'
export * from './fields.js'
/**
*
*/
export function detectThemeOptions({
plugins = {},
configFile,
...themeOptions
}: ThemeOptions) {
detectDependencies(themeOptions, plugins)
// detect options
detectMarkdown(themeOptions)
detectPlugins(plugins)
return { configFile, plugins, themeOptions }
}
export * from './options.js'
export * from './versions.js'

View File

@ -0,0 +1,21 @@
import type { ThemeOptions } from '../../shared/index.js'
import { detectDependencies } from './dependency.js'
import { detectMarkdown } from './markdown.js'
import { detectPlugins } from './plugins.js'
/**
*
*/
export function detectThemeOptions({
plugins = {},
configFile,
...themeOptions
}: ThemeOptions) {
detectDependencies(themeOptions, plugins)
// detect options
detectMarkdown(themeOptions)
detectPlugins(plugins)
return { configFile, plugins, themeOptions }
}

View File

@ -0,0 +1,134 @@
import type { App } from 'vuepress'
import fs from 'node:fs'
import path from 'node:path'
import { colors } from 'vuepress/utils'
import { createTranslate, getPackage, getThemePackage, logger } from '../utils/index.js'
const t = createTranslate({
en: {
title: 'The following dependencies have version mismatches:',
footer: 'Please update the dependencies to the correct versions.',
},
zh: {
title: '以下依赖版本不匹配:',
footer: '请更新依赖至正确的版本。',
},
})
export function detectVersions(app: App) {
detectVuepressVersion()
detectThemeVersion(app)
}
interface DepVersion {
name: string
expected: string
current: string
}
/**
* vuepress
* vuepress
* `@vuepress/helper`
*/
function detectVuepressVersion() {
const themePackage = getThemePackage()
const userPackage = getPackage()
const vuepressDeps = Object.entries({
'vuepress-theme-plume': themePackage.version,
'@vuepress/bundler-vite': themePackage.peerDependencies?.vuepress,
'@vuepress/bundler-webpack': themePackage.peerDependencies?.vuepress,
...themePackage.dependencies,
...themePackage.peerDependencies,
}).reduce((deps, [name, version]) => {
if (name.includes('vuepress') && version && version !== 'workspace:*')
deps[name] = version as string
return deps
}, {} as Record<string, string>)
/**
*
* TODO: 检查 pnpm catalog
*/
const detect = (deps: Record<string, string>) => {
const results: DepVersion[] = []
for (const [name, version] of Object.entries(deps)) {
const resolved = resolveVersion(version)
if (resolved && vuepressDeps[name] && vuepressDeps[name] !== resolved)
results.push({ name, expected: vuepressDeps[name], current: version as string })
}
return results
}
const devResults = detect(userPackage.devDependencies)
const prodResults = detect(userPackage.dependencies)
if (devResults.length || prodResults.length) {
const output = (deps: DepVersion[]) => deps
.map(dep =>
` ${colors.green(dep.name)}: ${colors.gray(dep.current)} -> ${colors.cyan(dep.expected)}`)
.join(' \n')
logger.warn(`${t('title')}
${
devResults.length ? `\ndevDependencies:\n${output(devResults)}\n` : ''
}${
prodResults.length ? `\ndependencies:\n${output(prodResults)}\n` : ''
}
${t('footer')}
`)
}
}
/**
*
*
*/
function detectThemeVersion(app: App) {
if (app.env.isBuild)
return
try {
const versionCache = app.dir.cache('.theme-plume-version')
const themePackage = getThemePackage()
const current = themePackage.version
const updateCache = () => {
fs.mkdirSync(path.dirname(versionCache), { recursive: true })
fs.writeFileSync(versionCache, current, 'utf-8')
}
// 缓存文件不存在时,不做检查
if (!fs.existsSync(versionCache)) {
updateCache()
return
}
const cached = fs.readFileSync(versionCache, 'utf-8') || ''
if (cached === current)
return
/**
*
*
*/
fs.rmSync(app.dir.cache(), { recursive: true })
fs.rmSync(app.dir.temp(), { recursive: true })
updateCache()
}
catch {}
}
const RE_FLAG = /^[\^~<>=]+/
function resolveVersion(version: string) {
if (RE_FLAG.test(version))
return version.replace(RE_FLAG, '')
if (/^\d/.test(version))
return version
return ''
}

View File

@ -40,7 +40,7 @@ export function codePlugins(pluginOptions: ThemeBuiltinPlugins): PluginConfig {
notationWordHighlight: true,
highlightLines: true,
collapsedLines: false,
langs: uniq([...twoslash ? ['ts', 'js', 'vue', 'json'] : [], ...langs]),
langs: uniq([...twoslash ? ['ts', 'js', 'vue', 'json', 'bash', 'sh'] : [], ...langs]),
codeBlockTitle: (title, code) => {
const icon = getIcon(title)
return `<div class="code-block-title"><div class="code-block-title-bar"><span class="title">${icon ? `<VPIcon name="${icon}"/>` : ''}${title}</span></div>${code}</div>`

View File

@ -12,7 +12,7 @@ import {
setupProvideData,
templateBuildRenderer,
} from './config/index.js'
import { detectThemeOptions } from './detector/index.js'
import { detectThemeOptions, detectVersions } from './detector/index.js'
import { initConfigLoader, waitForConfigLoaded, watchConfigFile } from './loadConfig/index.js'
import { createPages, extendsPageData } from './pages/index.js'
import { setupPlugins } from './plugins/index.js'
@ -25,6 +25,8 @@ export function plumeTheme(options: ThemeOptions = {}): Theme {
setTranslateLang(app.options.lang)
perf.init(app.env.isDebug)
detectVersions(app)
const { configFile, plugins, themeOptions } = detectThemeOptions(options)
initConfigLoader(app, {