@movk/nuxt-docs 1.13.0 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { camelCase, kebabCase, upperFirst } from '
|
|
2
|
+
import { camelCase, kebabCase, upperFirst } from 'scule'
|
|
3
3
|
|
|
4
4
|
interface Commit {
|
|
5
5
|
sha: string
|
|
6
|
+
date: string
|
|
6
7
|
message: string
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
interface Release {
|
|
11
|
+
tag_name: string
|
|
12
|
+
published_at: string
|
|
13
|
+
html_url: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ReleaseGroup {
|
|
17
|
+
tag: string
|
|
18
|
+
url?: string
|
|
19
|
+
icon?: string
|
|
20
|
+
title: string
|
|
21
|
+
commits: Commit[]
|
|
22
|
+
published_at?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
const props = defineProps<{
|
|
10
26
|
/**
|
|
11
27
|
* 仓库中的文件路径
|
|
@@ -45,7 +61,6 @@ const SHA_SHORT_LENGTH = 5
|
|
|
45
61
|
const { github } = useAppConfig()
|
|
46
62
|
const route = useRoute()
|
|
47
63
|
|
|
48
|
-
// 计算文件路径相关的值
|
|
49
64
|
const routeName = computed(() => route.path.split('/').pop() ?? '')
|
|
50
65
|
const githubUrl = computed(() => (github && typeof github === 'object' ? github.url : ''))
|
|
51
66
|
|
|
@@ -55,7 +70,6 @@ const filePath = computed(() => {
|
|
|
55
70
|
const fileExtension = props.suffix ?? (github && typeof github === 'object' ? github.suffix : 'vue')
|
|
56
71
|
const fileName = props.name ?? routeName.value
|
|
57
72
|
|
|
58
|
-
// 根据 casing 参数转换文件名
|
|
59
73
|
const transformedName = (() => {
|
|
60
74
|
const casing = props.casing ?? (github && typeof github === 'object' ? github.casing : undefined) ?? 'auto'
|
|
61
75
|
|
|
@@ -82,38 +96,101 @@ const { data: commits } = await useLazyFetch<Commit[]>('/api/github/commits', {
|
|
|
82
96
|
query: { path: [filePath.value], author: props.author }
|
|
83
97
|
})
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
const { data: releases } = await useLazyFetch<Release[]>('/api/github/releases.json')
|
|
100
|
+
|
|
101
|
+
const groupedByRelease = computed<ReleaseGroup[]>(() => {
|
|
87
102
|
if (!commits.value?.length) return []
|
|
88
103
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
const sortedReleases = (releases.value ?? [])
|
|
105
|
+
.filter(r => r.published_at)
|
|
106
|
+
.sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime())
|
|
107
|
+
|
|
108
|
+
const releasesOldestFirst = [...sortedReleases].reverse()
|
|
109
|
+
const groups: ReleaseGroup[] = []
|
|
110
|
+
const unreleased: Commit[] = []
|
|
111
|
+
|
|
112
|
+
for (const commit of commits.value) {
|
|
113
|
+
const commitDate = new Date(commit.date).getTime()
|
|
114
|
+
const release = releasesOldestFirst.find(r => new Date(r.published_at).getTime() >= commitDate)
|
|
115
|
+
|
|
116
|
+
if (release) {
|
|
117
|
+
const majorTag = release.tag_name.replace(/-(alpha|beta|rc)\.\d+$/, '')
|
|
118
|
+
let group = groups.find(g => g.tag === majorTag)
|
|
119
|
+
if (!group) {
|
|
120
|
+
group = { tag: majorTag, title: majorTag, icon: 'i-lucide-tag', published_at: release.published_at, url: release.html_url, commits: [] }
|
|
121
|
+
groups.push(group)
|
|
122
|
+
}
|
|
123
|
+
if (new Date(release.published_at) > new Date(group.published_at!)) {
|
|
124
|
+
group.published_at = release.published_at
|
|
125
|
+
group.url = release.html_url
|
|
126
|
+
}
|
|
127
|
+
group.commits.push(commit)
|
|
128
|
+
} else {
|
|
129
|
+
unreleased.push(commit)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
92
132
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
133
|
+
const result: ReleaseGroup[] = []
|
|
134
|
+
if (unreleased.length) {
|
|
135
|
+
result.push({ tag: 'unreleased', title: 'Soon', icon: 'i-lucide-tag', commits: unreleased })
|
|
136
|
+
}
|
|
97
137
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
138
|
+
const uniqueTags = [...new Set(sortedReleases.map(r => r.tag_name.replace(/-(alpha|beta|rc)\.\d+$/, '')))]
|
|
139
|
+
groups.sort((a, b) => uniqueTags.indexOf(a.tag) - uniqueTags.indexOf(b.tag))
|
|
140
|
+
result.push(...groups)
|
|
141
|
+
|
|
142
|
+
return result
|
|
103
143
|
})
|
|
144
|
+
|
|
145
|
+
function normalizeCommitMessage(commit: Commit) {
|
|
146
|
+
const prefix = `[\`${commit.sha.slice(0, SHA_SHORT_LENGTH)}\`](${githubUrl.value}/commit/${commit.sha})`
|
|
147
|
+
const content = commit.message
|
|
148
|
+
.replace(/#(\d+)/g, `<a href='${githubUrl.value}/issues/$1'>#$1</a>`)
|
|
149
|
+
.replace(/`(.*?)`/g, '<code class="text-xs">$1</code>')
|
|
150
|
+
|
|
151
|
+
return `${prefix} — ${content}`
|
|
152
|
+
}
|
|
104
153
|
</script>
|
|
105
154
|
|
|
106
155
|
<template>
|
|
107
|
-
<div v-if="!
|
|
156
|
+
<div v-if="!commits?.length">
|
|
108
157
|
No recent changes
|
|
109
158
|
</div>
|
|
110
159
|
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
160
|
+
<UTimeline
|
|
161
|
+
v-else
|
|
162
|
+
:items="groupedByRelease"
|
|
163
|
+
size="xs"
|
|
164
|
+
:ui="{ root: '', wrapper: 'mt-0 pb-0', title: 'mb-1.5 flex items-center justify-between' }"
|
|
165
|
+
>
|
|
166
|
+
<template #title="{ item }">
|
|
167
|
+
<UBadge
|
|
168
|
+
v-if="item.tag === 'unreleased'"
|
|
169
|
+
color="neutral"
|
|
170
|
+
variant="subtle"
|
|
171
|
+
:label="item.title"
|
|
172
|
+
class="w-12.5 justify-center"
|
|
173
|
+
/>
|
|
174
|
+
<NuxtLink
|
|
175
|
+
v-else
|
|
176
|
+
:to="item.url"
|
|
177
|
+
target="_blank"
|
|
178
|
+
class="hover:underline"
|
|
179
|
+
>
|
|
180
|
+
<UBadge variant="subtle" :label="item.tag" />
|
|
181
|
+
</NuxtLink>
|
|
182
|
+
|
|
183
|
+
<time v-if="item.published_at" :datetime="item.published_at" class="text-xs text-dimmed font-normal">
|
|
184
|
+
{{ useTimeAgo(new Date(item.published_at)) }}
|
|
185
|
+
</time>
|
|
186
|
+
</template>
|
|
187
|
+
|
|
188
|
+
<template #description="{ item }">
|
|
189
|
+
<ul class="flex flex-col gap-1.5">
|
|
190
|
+
<li v-for="commit of item.commits" :key="commit.sha">
|
|
191
|
+
<MDC :value="normalizeCommitMessage(commit)" class="text-sm [&_code]:text-xs" unwrap="p" />
|
|
192
|
+
</li>
|
|
193
|
+
</ul>
|
|
194
|
+
</template>
|
|
195
|
+
</UTimeline>
|
|
119
196
|
</template>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@movk/nuxt-docs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.13.
|
|
4
|
+
"version": "1.13.1",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Modern Nuxt 4 documentation theme with auto-generated component docs, AI chat assistant, MCP server, and complete developer experience optimization.",
|
|
7
7
|
"author": "YiXuan <mhaibaraai@gmail.com>",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Octokit } from '@octokit/rest'
|
|
2
|
+
|
|
3
|
+
export default defineCachedEventHandler(async () => {
|
|
4
|
+
if (!process.env.NUXT_GITHUB_TOKEN) {
|
|
5
|
+
return []
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { github } = useAppConfig()
|
|
9
|
+
|
|
10
|
+
if (!github || typeof github === 'boolean') {
|
|
11
|
+
throw createError({
|
|
12
|
+
status: 500,
|
|
13
|
+
statusText: 'GitHub configuration is not available'
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const octokit = new Octokit({ auth: process.env.NUXT_GITHUB_TOKEN })
|
|
18
|
+
|
|
19
|
+
const { data: releases } = await octokit.rest.repos.listReleases({
|
|
20
|
+
owner: github.owner,
|
|
21
|
+
repo: github.name
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
return releases
|
|
25
|
+
}, {
|
|
26
|
+
maxAge: 60 * 60,
|
|
27
|
+
getKey: () => 'releases'
|
|
28
|
+
})
|