@movk/nuxt-docs 1.4.2 → 1.5.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.
- package/app/app.config.ts +18 -4
- package/app/components/content/HeaderToggleButton.vue +82 -0
- package/app/components/header/Header.vue +8 -0
- package/app/mdc.config.ts +12 -0
- package/app/types/index.d.ts +7 -3
- package/app/utils/shiki-transformer-icon-highlight.ts +90 -0
- package/content.config.ts +22 -2
- package/modules/config.ts +13 -1
- package/modules/css.ts +17 -1
- package/nuxt.config.ts +24 -7
- package/package.json +6 -5
- package/server/api/github/commits.get.ts +8 -0
- package/server/api/github/last-commit.get.ts +12 -2
package/app/app.config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExtendedButtonProps } from './types'
|
|
2
2
|
|
|
3
3
|
export default defineAppConfig({
|
|
4
4
|
toaster: {
|
|
@@ -44,17 +44,31 @@ export default defineAppConfig({
|
|
|
44
44
|
to: '/',
|
|
45
45
|
search: true,
|
|
46
46
|
colorMode: true,
|
|
47
|
-
links: [] as
|
|
47
|
+
links: [] as ExtendedButtonProps[]
|
|
48
48
|
},
|
|
49
49
|
footer: {
|
|
50
50
|
credits: `Copyright © 2024 - ${new Date().getFullYear()}`,
|
|
51
|
-
socials: [] as
|
|
51
|
+
socials: [] as ExtendedButtonProps[]
|
|
52
52
|
},
|
|
53
53
|
toc: {
|
|
54
54
|
title: '页面导航',
|
|
55
55
|
bottom: {
|
|
56
56
|
title: '社区',
|
|
57
|
-
links: [] as
|
|
57
|
+
links: [] as ExtendedButtonProps[]
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
github: {
|
|
61
|
+
rootDir: '',
|
|
62
|
+
dateFormat: {
|
|
63
|
+
locale: 'zh-CN',
|
|
64
|
+
options: {
|
|
65
|
+
year: 'numeric',
|
|
66
|
+
month: 'numeric',
|
|
67
|
+
day: 'numeric',
|
|
68
|
+
hour: '2-digit',
|
|
69
|
+
minute: '2-digit',
|
|
70
|
+
timeZone: 'Asia/Shanghai'
|
|
71
|
+
}
|
|
58
72
|
}
|
|
59
73
|
}
|
|
60
74
|
})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { motion } from 'motion-v'
|
|
3
|
+
import type { VariantType } from 'motion-v'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
open: boolean
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
const variants: { [k: string]: VariantType | ((custom: unknown) => VariantType) } = {
|
|
10
|
+
normal: {
|
|
11
|
+
rotate: 0,
|
|
12
|
+
y: 0,
|
|
13
|
+
opacity: 1
|
|
14
|
+
},
|
|
15
|
+
close: (custom: unknown) => {
|
|
16
|
+
const c = custom as number
|
|
17
|
+
return {
|
|
18
|
+
rotate: c === 1 ? 45 : c === 3 ? -45 : 0,
|
|
19
|
+
y: c === 1 ? 6 : c === 3 ? -6 : 0,
|
|
20
|
+
opacity: c === 2 ? 0 : 1,
|
|
21
|
+
transition: {
|
|
22
|
+
type: 'spring',
|
|
23
|
+
stiffness: 260,
|
|
24
|
+
damping: 20
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const state = computed(() => props.open ? 'close' : 'normal')
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<UButton
|
|
35
|
+
size="sm"
|
|
36
|
+
variant="ghost"
|
|
37
|
+
color="neutral"
|
|
38
|
+
square
|
|
39
|
+
>
|
|
40
|
+
<svg
|
|
41
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
42
|
+
class="size-5"
|
|
43
|
+
viewBox="0 0 24 24"
|
|
44
|
+
fill="none"
|
|
45
|
+
stroke="currentColor"
|
|
46
|
+
stroke-width="2"
|
|
47
|
+
stroke-linecap="round"
|
|
48
|
+
stroke-linejoin="round"
|
|
49
|
+
>
|
|
50
|
+
<motion.line
|
|
51
|
+
x1="4"
|
|
52
|
+
y1="6"
|
|
53
|
+
x2="20"
|
|
54
|
+
y2="6"
|
|
55
|
+
:variants="variants"
|
|
56
|
+
:animate="state"
|
|
57
|
+
:custom="1"
|
|
58
|
+
class="outline-none"
|
|
59
|
+
/>
|
|
60
|
+
<motion.line
|
|
61
|
+
x1="4"
|
|
62
|
+
y1="12"
|
|
63
|
+
x2="20"
|
|
64
|
+
y2="12"
|
|
65
|
+
:variants="variants"
|
|
66
|
+
:animate="state"
|
|
67
|
+
:custom="2"
|
|
68
|
+
class="outline-none"
|
|
69
|
+
/>
|
|
70
|
+
<motion.line
|
|
71
|
+
x1="4"
|
|
72
|
+
y1="18"
|
|
73
|
+
x2="20"
|
|
74
|
+
y2="18"
|
|
75
|
+
:variants="variants"
|
|
76
|
+
:animate="state"
|
|
77
|
+
:custom="3"
|
|
78
|
+
class="outline-none"
|
|
79
|
+
/>
|
|
80
|
+
</svg>
|
|
81
|
+
</UButton>
|
|
82
|
+
</template>
|
|
@@ -49,6 +49,14 @@ const links = computed<ButtonProps[]>(() => github && github.url
|
|
|
49
49
|
</template>
|
|
50
50
|
</template>
|
|
51
51
|
|
|
52
|
+
<template #toggle="{ open, toggle, ui }">
|
|
53
|
+
<HeaderToggleButton
|
|
54
|
+
:open="open"
|
|
55
|
+
:class="ui.toggle({ toggleSide: 'right' })"
|
|
56
|
+
@click="toggle"
|
|
57
|
+
/>
|
|
58
|
+
</template>
|
|
59
|
+
|
|
52
60
|
<template #body>
|
|
53
61
|
<HeaderBody />
|
|
54
62
|
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from '@nuxtjs/mdc/config'
|
|
2
|
+
import { transformerColorHighlight } from 'shiki-transformer-color-highlight'
|
|
3
|
+
import { transformerIconHighlight } from './utils/shiki-transformer-icon-highlight'
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
shiki: {
|
|
7
|
+
transformers: [
|
|
8
|
+
transformerColorHighlight(),
|
|
9
|
+
transformerIconHighlight()
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
})
|
package/app/types/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { ButtonProps } from '@nuxt/ui'
|
|
2
2
|
|
|
3
|
+
export interface ExtendedButtonProps extends ButtonProps {
|
|
4
|
+
label?: string
|
|
5
|
+
}
|
|
6
|
+
|
|
3
7
|
declare module 'nuxt/schema' {
|
|
4
8
|
interface AppConfig {
|
|
5
9
|
vercelAnalytics: {
|
|
@@ -17,17 +21,17 @@ declare module 'nuxt/schema' {
|
|
|
17
21
|
to: string
|
|
18
22
|
search: boolean
|
|
19
23
|
colorMode: boolean
|
|
20
|
-
links:
|
|
24
|
+
links: ExtendedButtonProps[]
|
|
21
25
|
}
|
|
22
26
|
footer: {
|
|
23
27
|
credits: string
|
|
24
|
-
socials:
|
|
28
|
+
socials: ExtendedButtonProps[]
|
|
25
29
|
}
|
|
26
30
|
toc: {
|
|
27
31
|
title: string
|
|
28
32
|
bottom: {
|
|
29
33
|
title: string
|
|
30
|
-
links:
|
|
34
|
+
links: ExtendedButtonProps[]
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
github: {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** copy from https://github.com/nuxt/ui/blob/v4/docs/app/utils/shiki-transformer-icon-highlight.ts */
|
|
2
|
+
import type { ShikiTransformer } from 'shiki'
|
|
3
|
+
|
|
4
|
+
export interface TransformerIconHighlightOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Custom function to render the icon HTML
|
|
7
|
+
* @default Uses Iconify API with mask mode
|
|
8
|
+
*/
|
|
9
|
+
htmlIcon?: (icon: string) => string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Common icon collections to validate against (sorted by length descending for proper matching)
|
|
13
|
+
const ICON_COLLECTIONS = [
|
|
14
|
+
'simple-icons',
|
|
15
|
+
'vscode-icons',
|
|
16
|
+
'tabler',
|
|
17
|
+
'lucide',
|
|
18
|
+
'logos',
|
|
19
|
+
'ph'
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
function parseIconName(text: string): { collection: string, name: string, format: 'i' | 'colon' } | null {
|
|
23
|
+
// Strip quotes if present (single, double, or backticks)
|
|
24
|
+
let cleanText = text
|
|
25
|
+
if (/^['"`].*['"`]$/.test(text)) {
|
|
26
|
+
cleanText = text.slice(1, -1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Try i-{collection}-{name} format
|
|
30
|
+
if (cleanText.startsWith('i-')) {
|
|
31
|
+
const rest = cleanText.slice(2)
|
|
32
|
+
for (const collection of ICON_COLLECTIONS) {
|
|
33
|
+
if (rest.startsWith(`${collection}-`)) {
|
|
34
|
+
const name = rest.slice(collection.length + 1)
|
|
35
|
+
if (name && /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.test(name)) {
|
|
36
|
+
return { collection, name, format: 'i' }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Try {collection}:{name} format
|
|
43
|
+
const colonIndex = cleanText.indexOf(':')
|
|
44
|
+
if (colonIndex > 0) {
|
|
45
|
+
const collection = cleanText.slice(0, colonIndex)
|
|
46
|
+
const name = cleanText.slice(colonIndex + 1)
|
|
47
|
+
if (ICON_COLLECTIONS.includes(collection) && name && /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.test(name)) {
|
|
48
|
+
return { collection, name, format: 'colon' }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function transformerIconHighlight(options: TransformerIconHighlightOptions = {}): ShikiTransformer {
|
|
56
|
+
const { htmlIcon } = options
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
name: 'shiki-transformer-icon-highlight',
|
|
60
|
+
span(hast, _line, _col, _lineElement, token) {
|
|
61
|
+
const text = token.content
|
|
62
|
+
|
|
63
|
+
// Try to parse as an icon
|
|
64
|
+
const parsed = parseIconName(text)
|
|
65
|
+
if (!parsed) return
|
|
66
|
+
|
|
67
|
+
const iconIdentifier = `${parsed.collection}:${parsed.name}`
|
|
68
|
+
// Add color=black for mask-image to work properly (mask uses luminance)
|
|
69
|
+
const iconUrl = `https://api.iconify.design/${iconIdentifier}.svg?color=%23000`
|
|
70
|
+
|
|
71
|
+
// Create the icon element as a proper HAST element
|
|
72
|
+
const iconElement = htmlIcon
|
|
73
|
+
? { type: 'raw' as const, value: htmlIcon(iconIdentifier) }
|
|
74
|
+
: {
|
|
75
|
+
type: 'element' as const,
|
|
76
|
+
tagName: 'i',
|
|
77
|
+
properties: {
|
|
78
|
+
class: 'shiki-icon-highlight',
|
|
79
|
+
style: `--shiki-icon-url: url('${iconUrl}')`
|
|
80
|
+
},
|
|
81
|
+
children: []
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Prepend the icon to the span content
|
|
85
|
+
if (hast.children) {
|
|
86
|
+
hast.children.unshift(iconElement)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
package/content.config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineCollection, defineContentConfig,
|
|
1
|
+
import { defineCollection, defineContentConfig, z } from '@nuxt/content'
|
|
2
2
|
import { useNuxt } from '@nuxt/kit'
|
|
3
3
|
import { asSeoCollection } from '@nuxtjs/seo/content'
|
|
4
4
|
import { joinURL } from 'ufo'
|
|
@@ -6,6 +6,26 @@ import { joinURL } from 'ufo'
|
|
|
6
6
|
const { options } = useNuxt()
|
|
7
7
|
const cwd = joinURL(options.rootDir, 'content')
|
|
8
8
|
|
|
9
|
+
const Avatar = z.object({
|
|
10
|
+
src: z.string(),
|
|
11
|
+
alt: z.string().optional()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const Button = z.object({
|
|
15
|
+
label: z.string(),
|
|
16
|
+
icon: z.string().optional(),
|
|
17
|
+
avatar: Avatar.optional(),
|
|
18
|
+
leadingIcon: z.string().optional(),
|
|
19
|
+
trailingIcon: z.string().optional(),
|
|
20
|
+
to: z.string().optional(),
|
|
21
|
+
target: z.enum(['_blank', '_self']).optional(),
|
|
22
|
+
color: z.enum(['primary', 'neutral', 'success', 'warning', 'error', 'info']).optional(),
|
|
23
|
+
size: z.enum(['xs', 'sm', 'md', 'lg', 'xl']).optional(),
|
|
24
|
+
variant: z.enum(['solid', 'outline', 'subtle', 'soft', 'ghost', 'link']).optional(),
|
|
25
|
+
id: z.string().optional(),
|
|
26
|
+
class: z.string().optional()
|
|
27
|
+
})
|
|
28
|
+
|
|
9
29
|
export default defineContentConfig({
|
|
10
30
|
collections: {
|
|
11
31
|
landing: defineCollection(asSeoCollection({
|
|
@@ -22,7 +42,7 @@ export default defineContentConfig({
|
|
|
22
42
|
include: 'docs/**/*'
|
|
23
43
|
}],
|
|
24
44
|
schema: z.object({
|
|
25
|
-
links: z.array(
|
|
45
|
+
links: z.array(Button),
|
|
26
46
|
category: z.string().optional(),
|
|
27
47
|
navigation: z.object({
|
|
28
48
|
title: z.string().optional()
|
package/modules/config.ts
CHANGED
|
@@ -70,7 +70,19 @@ export default defineNuxtModule({
|
|
|
70
70
|
'nuxt-og-image',
|
|
71
71
|
'@nuxtjs/plausible',
|
|
72
72
|
'@nuxt/ui',
|
|
73
|
-
|
|
73
|
+
(component: { filePath: string }) => {
|
|
74
|
+
const allowedComponents = [
|
|
75
|
+
'CommitChangelog.vue',
|
|
76
|
+
'ComponentEmits.vue',
|
|
77
|
+
'ComponentExample.vue',
|
|
78
|
+
'ComponentProps.vue',
|
|
79
|
+
'ComponentSlots.vue',
|
|
80
|
+
'PageLastCommit.vue',
|
|
81
|
+
'Motion.vue'
|
|
82
|
+
]
|
|
83
|
+
return component.filePath.startsWith(componentsPath)
|
|
84
|
+
&& !allowedComponents.some(name => component.filePath.endsWith(`/content/${name}`))
|
|
85
|
+
}
|
|
74
86
|
],
|
|
75
87
|
metaFields: {
|
|
76
88
|
type: false,
|
package/modules/css.ts
CHANGED
|
@@ -21,7 +21,23 @@ export default defineNuxtModule({
|
|
|
21
21
|
|
|
22
22
|
@source "${contentDir.replace(/\\/g, '/')}/**/*";
|
|
23
23
|
@source "${layerDir.replace(/\\/g, '/')}/**/*";
|
|
24
|
-
@source "../../app.config.ts"
|
|
24
|
+
@source "../../app.config.ts";
|
|
25
|
+
|
|
26
|
+
/* Shiki icon highlight transformer styles */
|
|
27
|
+
.shiki-icon-highlight {
|
|
28
|
+
display: inline-block;
|
|
29
|
+
width: 1.25em;
|
|
30
|
+
height: 1.25em;
|
|
31
|
+
vertical-align: -0.25em;
|
|
32
|
+
margin-right: 0.125em;
|
|
33
|
+
background-color: var(--ui-text-highlighted);
|
|
34
|
+
-webkit-mask-repeat: no-repeat;
|
|
35
|
+
mask-repeat: no-repeat;
|
|
36
|
+
-webkit-mask-size: 100% 100%;
|
|
37
|
+
mask-size: 100% 100%;
|
|
38
|
+
-webkit-mask-image: var(--shiki-icon-url);
|
|
39
|
+
mask-image: var(--shiki-icon-url);
|
|
40
|
+
}`
|
|
25
41
|
}
|
|
26
42
|
})
|
|
27
43
|
|
package/nuxt.config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createResolver } from '@nuxt/kit'
|
|
2
|
+
import { defineNuxtConfig } from 'nuxt/config'
|
|
2
3
|
import pkg from './package.json'
|
|
3
4
|
|
|
4
5
|
const { resolve } = createResolver(import.meta.url)
|
|
@@ -18,6 +19,13 @@ export default defineNuxtConfig({
|
|
|
18
19
|
'motion-v/nuxt',
|
|
19
20
|
'nuxt-llms'
|
|
20
21
|
],
|
|
22
|
+
app: {
|
|
23
|
+
rootAttrs: {
|
|
24
|
+
'data-vaul-drawer-wrapper': '',
|
|
25
|
+
'class': 'bg-default'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
// @ts-ignore - content 配置的类型定义在运行时才能正确解析
|
|
21
29
|
content: {
|
|
22
30
|
build: {
|
|
23
31
|
markdown: {
|
|
@@ -32,6 +40,11 @@ export default defineNuxtConfig({
|
|
|
32
40
|
noApiRoute: false
|
|
33
41
|
}
|
|
34
42
|
},
|
|
43
|
+
ui: {
|
|
44
|
+
experimental: {
|
|
45
|
+
componentDetection: true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
35
48
|
runtimeConfig: {
|
|
36
49
|
public: {
|
|
37
50
|
version: pkg.version
|
|
@@ -45,7 +58,13 @@ export default defineNuxtConfig({
|
|
|
45
58
|
: {}
|
|
46
59
|
},
|
|
47
60
|
experimental: {
|
|
48
|
-
typescriptPlugin: true
|
|
61
|
+
typescriptPlugin: true,
|
|
62
|
+
asyncContext: true,
|
|
63
|
+
defaults: {
|
|
64
|
+
nuxtLink: {
|
|
65
|
+
externalRelAttribute: 'noopener'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
49
68
|
},
|
|
50
69
|
compatibilityDate: 'latest',
|
|
51
70
|
nitro: {
|
|
@@ -65,12 +84,6 @@ export default defineNuxtConfig({
|
|
|
65
84
|
'tailwind-variants',
|
|
66
85
|
'tailwindcss/colors'
|
|
67
86
|
]
|
|
68
|
-
},
|
|
69
|
-
resolve: {
|
|
70
|
-
alias: {
|
|
71
|
-
extend: 'extend/index.js',
|
|
72
|
-
debug: 'debug/src/browser.js'
|
|
73
|
-
}
|
|
74
87
|
}
|
|
75
88
|
},
|
|
76
89
|
fonts: {
|
|
@@ -87,6 +100,10 @@ export default defineNuxtConfig({
|
|
|
87
100
|
icon: {
|
|
88
101
|
provider: 'iconify'
|
|
89
102
|
},
|
|
103
|
+
image: {
|
|
104
|
+
format: ['webp', 'jpeg', 'jpg', 'png', 'svg'],
|
|
105
|
+
provider: 'ipx'
|
|
106
|
+
},
|
|
90
107
|
linkChecker: {
|
|
91
108
|
report: {
|
|
92
109
|
publish: true,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@movk/nuxt-docs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.1",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "An elegant documentation theme for Nuxt, powered by Nuxt UI and Nuxt Content.",
|
|
7
7
|
"author": "YiXuan <mhaibaraai@gmail.com>",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"README.md"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@iconify-json/lucide": "^1.2.
|
|
30
|
+
"@iconify-json/lucide": "^1.2.82",
|
|
31
31
|
"@iconify-json/simple-icons": "^1.2.63",
|
|
32
32
|
"@iconify-json/vscode-icons": "^1.2.37",
|
|
33
|
-
"@movk/core": "^1.0.
|
|
34
|
-
"@nuxt/content": "^3.
|
|
33
|
+
"@movk/core": "^1.0.3",
|
|
34
|
+
"@nuxt/content": "^3.10.0",
|
|
35
35
|
"@nuxt/image": "^2.0.0",
|
|
36
36
|
"@nuxt/kit": "^4.2.2",
|
|
37
37
|
"@nuxt/ui": "^4.3.0",
|
|
@@ -45,13 +45,14 @@
|
|
|
45
45
|
"exsolve": "^1.0.8",
|
|
46
46
|
"git-url-parse": "^16.1.0",
|
|
47
47
|
"motion-v": "^1.7.4",
|
|
48
|
-
"nuxt-component-meta": "^0.
|
|
48
|
+
"nuxt-component-meta": "^0.16.0",
|
|
49
49
|
"nuxt-llms": "^0.1.3",
|
|
50
50
|
"ohash": "^2.0.11",
|
|
51
51
|
"pathe": "^2.0.3",
|
|
52
52
|
"pkg-types": "^2.3.0",
|
|
53
53
|
"prettier": "^3.7.4",
|
|
54
54
|
"scule": "^1.3.0",
|
|
55
|
+
"shiki-transformer-color-highlight": "^1.0.0",
|
|
55
56
|
"tailwindcss": "^4.1.18",
|
|
56
57
|
"ufo": "^1.6.1"
|
|
57
58
|
}
|
|
@@ -16,6 +16,14 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const { github } = useAppConfig()
|
|
19
|
+
|
|
20
|
+
if (!github || typeof github === 'boolean') {
|
|
21
|
+
throw createError({
|
|
22
|
+
statusCode: 500,
|
|
23
|
+
statusMessage: 'GitHub configuration is not available'
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
const octokit = new Octokit({ auth: process.env.NUXT_GITHUB_TOKEN })
|
|
20
28
|
|
|
21
29
|
const allCommits = await Promise.all(
|
|
@@ -14,6 +14,14 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const { github } = useAppConfig()
|
|
17
|
+
|
|
18
|
+
if (!github || typeof github === 'boolean') {
|
|
19
|
+
throw createError({
|
|
20
|
+
statusCode: 500,
|
|
21
|
+
statusMessage: 'GitHub configuration is not available'
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
const octokit = new Octokit({ auth: process.env.NUXT_GITHUB_TOKEN })
|
|
18
26
|
|
|
19
27
|
try {
|
|
@@ -30,6 +38,9 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
const commit = commits[0]
|
|
41
|
+
if (!commit) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
33
44
|
|
|
34
45
|
// 获取提交者信息,处理 web-flow 场景(PR squash merge)
|
|
35
46
|
let authorName = commit.commit.author?.name ?? ''
|
|
@@ -63,14 +74,13 @@ export default defineCachedEventHandler(async (event) => {
|
|
|
63
74
|
const date = commit.commit.author?.date ?? ''
|
|
64
75
|
const dateFormat = github.dateFormat ?? {}
|
|
65
76
|
const locale = dateFormat.locale ?? 'zh-CN'
|
|
66
|
-
const timeZone = dateFormat.timeZone ?? 'Asia/Shanghai'
|
|
67
77
|
const formatOptions: Intl.DateTimeFormatOptions = dateFormat.options ?? {
|
|
68
78
|
year: 'numeric',
|
|
69
79
|
month: 'numeric',
|
|
70
80
|
day: 'numeric',
|
|
71
81
|
hour: '2-digit',
|
|
72
82
|
minute: '2-digit',
|
|
73
|
-
timeZone
|
|
83
|
+
timeZone: 'Asia/Shanghai'
|
|
74
84
|
}
|
|
75
85
|
const dateFormatted = date
|
|
76
86
|
? new Date(date).toLocaleDateString(locale, formatOptions)
|