refactor(theme): migrate contributors and changelog to @vuepress/plugin-git built-in component (#536)

This commit is contained in:
pengzhanbo 2025-03-24 00:28:58 +08:00 committed by GitHub
parent 9dd5a21676
commit 6235833e7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 66 additions and 253 deletions

View File

@ -71,5 +71,5 @@ export const theme: Theme = plumeTheme({
}, },
}, },
plugins: { git: true }, // plugins: { git: true },
}) })

17
pnpm-lock.yaml generated
View File

@ -301,8 +301,8 @@ catalogs:
specifier: 2.0.0-rc.86 specifier: 2.0.0-rc.86
version: 2.0.0-rc.86 version: 2.0.0-rc.86
'@vuepress/plugin-git': '@vuepress/plugin-git':
specifier: 2.0.0-rc.79 specifier: 2.0.0-rc.87
version: 2.0.0-rc.79 version: 2.0.0-rc.87
'@vuepress/plugin-markdown-hint': '@vuepress/plugin-markdown-hint':
specifier: 2.0.0-rc.86 specifier: 2.0.0-rc.86
version: 2.0.0-rc.86 version: 2.0.0-rc.86
@ -705,7 +705,7 @@ importers:
version: 2.0.0-rc.86(@algolia/client-search@5.18.0)(search-insights@2.17.3)(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) version: 2.0.0-rc.86(@algolia/client-search@5.18.0)(search-insights@2.17.3)(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))
'@vuepress/plugin-git': '@vuepress/plugin-git':
specifier: catalog:vuepress specifier: catalog:vuepress
version: 2.0.0-rc.79(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) version: 2.0.0-rc.87(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))
'@vuepress/plugin-markdown-hint': '@vuepress/plugin-markdown-hint':
specifier: catalog:vuepress specifier: catalog:vuepress
version: 2.0.0-rc.86(markdown-it@14.1.0)(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) version: 2.0.0-rc.86(markdown-it@14.1.0)(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))
@ -2559,8 +2559,8 @@ packages:
peerDependencies: peerDependencies:
vuepress: 2.0.0-rc.20 vuepress: 2.0.0-rc.20
'@vuepress/plugin-git@2.0.0-rc.79': '@vuepress/plugin-git@2.0.0-rc.87':
resolution: {integrity: sha512-f3S3VieD0+K2V5feSbs85rzMXm4Pwk7ieP4UnAEDGfEXKTsPDJIuf5mLrbxL+9dwfopylO6A2rugtUqESQ2VKQ==} resolution: {integrity: sha512-kiZJyiLl2tkt3i/iuhc/g9FlnWKr1Yylj+iNf5gZYMwnO8rGnIdLFVw8a8mqqAc3587RYao6upvL/Kd75hKVfA==}
peerDependencies: peerDependencies:
vuepress: 2.0.0-rc.20 vuepress: 2.0.0-rc.20
@ -8735,10 +8735,15 @@ snapshots:
- search-insights - search-insights
- typescript - typescript
'@vuepress/plugin-git@2.0.0-rc.79(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))': '@vuepress/plugin-git@2.0.0-rc.87(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))':
dependencies: dependencies:
'@vuepress/helper': 2.0.0-rc.86(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))
'@vueuse/core': 13.0.0(vue@3.5.13(typescript@5.8.2))
execa: 9.5.2 execa: 9.5.2
vue: 3.5.13(typescript@5.8.2)
vuepress: 2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)) vuepress: 2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))
transitivePeerDependencies:
- typescript
'@vuepress/plugin-markdown-hint@2.0.0-rc.86(markdown-it@14.1.0)(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))': '@vuepress/plugin-markdown-hint@2.0.0-rc.86(markdown-it@14.1.0)(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.11)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.86.0)(sass@1.86.0)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))':
dependencies: dependencies:

View File

@ -118,7 +118,7 @@ catalogs:
'@vuepress/plugin-comment': 2.0.0-rc.86 '@vuepress/plugin-comment': 2.0.0-rc.86
'@vuepress/plugin-copy-code': 2.0.0-rc.86 '@vuepress/plugin-copy-code': 2.0.0-rc.86
'@vuepress/plugin-docsearch': 2.0.0-rc.86 '@vuepress/plugin-docsearch': 2.0.0-rc.86
'@vuepress/plugin-git': 2.0.0-rc.79 '@vuepress/plugin-git': 2.0.0-rc.87
'@vuepress/plugin-markdown-hint': 2.0.0-rc.86 '@vuepress/plugin-markdown-hint': 2.0.0-rc.86
'@vuepress/plugin-markdown-image': 2.0.0-rc.86 '@vuepress/plugin-markdown-image': 2.0.0-rc.86
'@vuepress/plugin-markdown-include': 2.0.0-rc.86 '@vuepress/plugin-markdown-include': 2.0.0-rc.86

View File

@ -1,8 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import VPDocAside from '@theme/VPDocAside.vue' import VPDocAside from '@theme/VPDocAside.vue'
import VPDocBreadcrumbs from '@theme/VPDocBreadcrumbs.vue' import VPDocBreadcrumbs from '@theme/VPDocBreadcrumbs.vue'
import VPDocChangelog from '@theme/VPDocChangelog.vue'
import VPDocContributor from '@theme/VPDocContributor.vue'
import VPDocCopyright from '@theme/VPDocCopyright.vue' import VPDocCopyright from '@theme/VPDocCopyright.vue'
import VPDocFooter from '@theme/VPDocFooter.vue' import VPDocFooter from '@theme/VPDocFooter.vue'
import VPDocMeta from '@theme/VPDocMeta.vue' import VPDocMeta from '@theme/VPDocMeta.vue'
@ -12,6 +10,7 @@ import { computed, nextTick, ref, resolveComponent, watch } from 'vue'
import { useRoute } from 'vuepress/client' import { useRoute } from 'vuepress/client'
import { import {
useBlogPageData, useBlogPageData,
useContributors,
useData, useData,
useEncrypt, useEncrypt,
useHeaders, useHeaders,
@ -25,6 +24,7 @@ const { hasSidebar, hasAside, leftAside } = useSidebar()
const { isBlogPost } = useBlogPageData() const { isBlogPost } = useBlogPageData()
const headers = useHeaders() const headers = useHeaders()
const { isPageDecrypted } = useEncrypt() const { isPageDecrypted } = useEncrypt()
const { mode: contributorsMode } = useContributors()
const hasComments = computed(() => { const hasComments = computed(() => {
return resolveComponent('CommentService') !== 'CommentService' && page.value.frontmatter.comments !== false && isPageDecrypted.value return resolveComponent('CommentService') !== 'CommentService' && page.value.frontmatter.comments !== false && isPageDecrypted.value
@ -129,8 +129,10 @@ watch(
:class="[pageName, enabledExternalLinkIcon && 'external-link-icon-enabled']" vp-content :class="[pageName, enabledExternalLinkIcon && 'external-link-icon-enabled']" vp-content
> >
<Content /> <Content />
<VPDocContributor />
<VPDocChangelog /> <DocGitContributors v-if="contributorsMode === 'block'" />
<DocGitChangelog />
<VPDocCopyright /> <VPDocCopyright />
</div> </div>
</main> </main>

View File

@ -1,175 +0,0 @@
<script setup lang="ts">
import VPDocHeader from '@theme/VPDocHeader.vue'
import VPLink from '@theme/VPLink.vue'
import { computed } from 'vue'
import { usePageLang } from 'vuepress/client'
import { useData, useLastUpdated, useThemeData } from '../composables/index.js'
const { theme, frontmatter, page } = useData()
const themeData = useThemeData()
const lang = usePageLang()
const { datetime, lastUpdatedText } = useLastUpdated()
const list = computed(() => {
const list = page.value.git?.changelog || []
const formatter = new Intl.DateTimeFormat(lang.value, { dateStyle: 'short' })
// TODO: plugin-git
return [...list].reverse().map(({ date, ...item }) => {
const datetime = formatter.format(date)
return { datetime, ...item }
})
})
const hasChangelog = computed(() =>
list.value.length && (frontmatter.value.changelog ?? !!themeData.value.changelog),
)
</script>
<template>
<div v-if="hasChangelog" class="vp-doc-changelog">
<VPDocHeader anchor="doc-changelog">
{{ theme.changelogText || 'Changelog' }}
</VPDocHeader>
<details class="hint-container details">
<summary class="changelog-header">
<span><span class="vpi-changelog" /><span>{{ lastUpdatedText }}:</span> <span>{{ datetime }}</span></span>
<span class="changelog-button"><span class="vpi-menu" /><span>{{ theme.changelogButtonText || 'View All Changelog' }}</span></span>
</summary>
<ul class="changelog-list">
<template v-for="item in list" :key="item.hash">
<li v-if="item.tag" class="changelog release-tag">
<div>
<VPLink :href="item.tagUrl" no-icon class="release-tag">
<code>{{ item.tag }}</code>
</VPLink>
<span class="datetime" data-allow-mismatch>{{ theme.changelogOnText }} {{ item.datetime }}</span>
</div>
</li>
<li v-else class="changelog commit">
<div>
<VPLink :href="item.commitUrl" no-icon class="hash">
<code>{{ item.hash.slice(0, 5) }}</code>
</VPLink>
<span class="divider">-</span>
</div>
<div>
<p class="message" v-html="item.message" />
<span class="datetime" data-allow-mismatch>{{ theme.changelogOnText }} {{ item.datetime }}</span>
</div>
</li>
</template>
</ul>
</details>
</div>
</template>
<style scoped>
.vp-doc-changelog .changelog-header {
display: block;
}
@media print {
.vp-doc-changelog {
display: none;
}
}
.vp-doc-changelog .changelog-header > span {
display: block;
}
@media (min-width: 440px) {
.vp-doc-changelog .changelog-header {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.vp-doc-changelog .changelog-header [class*="vpi-"] {
margin-right: 4px;
color: var(--vp-c-text-2);
transition: color var(--vp-t-color);
transform: translateY(-1px);
}
.vp-doc-changelog .changelog-header .changelog-button:hover,
.vp-doc-changelog .changelog-header .changelog-button:hover .vpi-menu {
color: var(--vp-c-brand-1);
transition: color var(--vp-t-color);
}
.vp-doc-changelog .changelog-list {
padding-left: 0;
list-style: none;
}
.vp-doc-changelog .changelog-list .changelog {
position: relative;
display: flex;
align-items: flex-start;
padding-left: 20px;
transition: color var(--vp-t-color);
}
.vp-doc-changelog .changelog-list .changelog::before {
position: absolute;
top: 4px;
left: 0;
display: inline-block;
width: 1.25em;
height: 1.25em;
color: var(--vp-c-text-3);
content: "";
background-color: currentcolor;
-webkit-mask: var(--icon) no-repeat;
mask: var(--icon) no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
transition: color var(--vp-t-color);
}
.vp-doc-changelog .changelog-list .changelog.commit::before {
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 12a3 3 0 1 0 6 0a3 3 0 1 0-6 0m3-9v6m0 6v6'/%3E%3C/svg%3E");
}
.vp-doc-changelog .changelog-list .changelog.release-tag::before {
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M6.5 7.5a1 1 0 1 0 2 0a1 1 0 1 0-2 0'/%3E%3Cpath d='M3 6v5.172a2 2 0 0 0 .586 1.414l7.71 7.71a2.41 2.41 0 0 0 3.408 0l5.592-5.592a2.41 2.41 0 0 0 0-3.408l-7.71-7.71A2 2 0 0 0 11.172 3H6a3 3 0 0 0-3 3'/%3E%3C/g%3E%3C/svg%3E");
}
.vp-doc-changelog .changelog-list .changelog .release-tag {
margin-right: 4px;
text-decoration: none;
}
.vp-doc-changelog .changelog-list .changelog .release-tag code {
font-size: 14px;
font-weight: 500;
}
.vp-doc-changelog .changelog-list .changelog .hash {
margin-right: 4px;
text-decoration: none;
}
.vp-doc-changelog .changelog-list .changelog .divider {
margin-right: 8px;
}
.vp-doc-changelog .changelog-list .changelog .message {
display: inline;
margin-right: 8px;
}
.vp-doc-changelog .changelog-list .changelog .message :deep(a::after) {
display: none;
}
.vp-doc-changelog .changelog-list .changelog .datetime {
font-size: 12px;
color: var(--vp-c-text-3);
transition: color var(--vp-t-color);
}
</style>

View File

@ -1,59 +0,0 @@
<script setup lang="ts">
import VPDocHeader from '@theme/VPDocHeader.vue'
import VPLink from '@theme/VPLink.vue'
import { computed } from 'vue'
import { useContributors, useData } from '../composables/index.js'
const { theme } = useData()
const { contributors, mode } = useContributors()
const hasContributors = computed(() => Boolean(contributors.value.length) && mode.value === 'block')
</script>
<template>
<div v-if="hasContributors" class="vp-doc-contributor">
<VPDocHeader anchor="doc-contributors">
{{ theme.contributorsText || 'Contributors' }}
</VPDocHeader>
<ul class="contributor-list">
<li v-for="contributor in contributors" :key="contributor.name + contributor.email" class="contributor">
<VPLink :href="contributor.url" no-icon>
<img v-if="contributor.avatar" :src="contributor.avatar" :alt="contributor.name">
<span>{{ contributor.name }}</span>
</VPLink>
</li>
</ul>
</div>
</template>
<style scoped>
.vp-doc-contributor .contributor-list {
display: flex;
flex-wrap: wrap;
gap: 24px 16px;
align-items: center;
justify-content: flex-start;
padding-left: 0;
margin-top: 32px;
list-style: none;
}
.vp-doc-contributor .contributor-list li {
margin: 0;
}
.contributor-list .contributor img {
width: 32px;
height: 32px;
border-radius: 50%;
}
.contributor-list .contributor :deep(.vp-link) {
display: flex;
gap: 4px;
align-items: center;
justify-content: center;
text-decoration: none;
}
</style>

View File

@ -1,5 +1,6 @@
import type { ComputedRef } from 'vue' import type { ComputedRef } from 'vue'
import type { GitContributor } from '../../shared/index.js' import type { GitContributor } from '../../shared/index.js'
import { useContributors as _useContributors } from '@vuepress/plugin-git/client'
import { computed } from 'vue' import { computed } from 'vue'
import { isPlainObject } from 'vuepress/shared' import { isPlainObject } from 'vuepress/shared'
import { useData } from '../composables/data.js' import { useData } from '../composables/data.js'
@ -8,8 +9,11 @@ import { useThemeData } from './theme-data.js'
export function useContributors(): { export function useContributors(): {
mode: ComputedRef<'inline' | 'block'> mode: ComputedRef<'inline' | 'block'>
contributors: ComputedRef<GitContributor[]> contributors: ComputedRef<GitContributor[]>
hasContributors: ComputedRef<boolean>
} { } {
const { page, frontmatter } = useData() const { frontmatter } = useData()
const list = _useContributors()
const theme = useThemeData() const theme = useThemeData()
const mode = computed(() => { const mode = computed(() => {
@ -25,8 +29,10 @@ export function useContributors(): {
if (config === false) if (config === false)
return [] return []
return (page.value.git?.contributors ?? []) return list.value
}) })
return { mode, contributors } const hasContributors = computed(() => contributors.value.length > 0)
return { mode, contributors, hasContributors }
} }

View File

@ -44,4 +44,18 @@ export function globalComponents(app: App) {
} }
return null return null
}) })
app.component('DocGitContributors', () => {
if (hasGlobalComponent('GitContributors')) {
return h(resolveComponent('GitContributors'))
}
return null
})
app.component('DocGitChangelog', () => {
if (hasGlobalComponent('GitChangelog')) {
return h(resolveComponent('GitChangelog'))
}
return null
})
} }

View File

@ -16,3 +16,19 @@
.vp-comment { .vp-comment {
margin-top: 80px; margin-top: 80px;
} }
/* ------------------ Plugin Git ------------------ */
.vp-doc #doc-contributors,
.vp-doc #doc-changelog {
border-top: 1px solid var(--vp-c-divider);
}
.vp-doc .vp-changelog-wrapper {
background-color: var(--vp-c-default-soft);
}
.vp-doc .vp-changelog-wrapper .vp-changelog-list {
padding-left: 0;
margin-block: 8px;
list-style: none;
}

View File

@ -18,7 +18,7 @@ export function extendsBundlerOptions(bundlerOptions: any, app: App): void {
addViteOptimizeDepsInclude( addViteOptimizeDepsInclude(
bundlerOptions, bundlerOptions,
app, app,
['@vueuse/core', 'bcrypt-ts/browser', '@vuepress/helper/client', '@iconify/vue', '@iconify/vue/offline'], ['@vueuse/core', 'bcrypt-ts/browser', '@vuepress/helper/client', '@iconify/vue', '@iconify/vue/offline', '@vuepress/plugin-git/client'],
) )
addViteOptimizeDepsExclude(bundlerOptions, app, '@theme') addViteOptimizeDepsExclude(bundlerOptions, app, '@theme')

View File

@ -114,9 +114,9 @@ export function setupPlugins(
...options.contributors === true ? {} : options.contributors, ...options.contributors === true ? {} : options.contributors,
} }
: false, : false,
changelog: options.changelog && (options.docsRepo || changelogOptions.repoUrl) changelog: options.changelog && options.docsRepo
? { repoUrl: options.docsRepo, ...changelogOptions } ? { repoUrl: options.docsRepo, ...changelogOptions }
: false, : options.changelog,
filter(page) { filter(page) {
if (page.frontmatter.home || excludes.includes(page.frontmatter.pageLayout as string)) if (page.frontmatter.home || excludes.includes(page.frontmatter.pageLayout as string))
return false return false

View File

@ -8,4 +8,8 @@ export type * from './pageData.js'
export type * from './plugins.js' export type * from './plugins.js'
export type * from './resolved/index.js' export type * from './resolved/index.js'
export type * from './utils.js' export type * from './utils.js'
export type { GitChangelog, GitContributor } from '@vuepress/plugin-git'
export type {
GitChangelogInfo as GitChangelog,
GitContributorInfo as GitContributor,
} from '@vuepress/plugin-git'