feat(theme): add theme deps version detection (#551)
* feat(theme): add theme deps version detection * chore: tweak
This commit is contained in:
parent
560eef0a48
commit
487448d611
@ -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'
|
||||
|
||||
21
theme/src/node/detector/options.ts
Normal file
21
theme/src/node/detector/options.ts
Normal 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 }
|
||||
}
|
||||
134
theme/src/node/detector/versions.ts
Normal file
134
theme/src/node/detector/versions.ts
Normal 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 ''
|
||||
}
|
||||
@ -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>`
|
||||
|
||||
@ -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, {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user