@sugarat/theme 0.1.38 → 0.1.40
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/README.md +2 -1
- package/node.d.ts +42 -8
- package/node.js +292 -206
- package/package.json +4 -3
- package/src/components/BlogArticleAnalyze.vue +2 -2
- package/src/components/BlogFriendLink.vue +2 -2
- package/src/components/BlogHomeOverview.vue +1 -1
- package/src/components/BlogHotArticle.vue +1 -1
- package/src/components/BlogItem.vue +1 -1
- package/src/components/BlogRecommendArticle.vue +19 -9
- package/src/components/BlogSearch.vue +1 -1
- package/src/components/BlogSidebar.vue +22 -6
- package/src/components/UserWorks.vue +1 -1
- package/src/composables/config/index.ts +34 -2
- package/src/node.ts +35 -367
- package/src/styles/index.scss +1 -1
- package/src/utils/{index.ts → client/index.ts} +1 -1
- package/src/utils/node/genFeed.ts +53 -0
- package/src/utils/node/index.ts +118 -0
- package/src/utils/node/mdPlugins.ts +95 -0
- package/src/utils/node/theme.ts +138 -0
- package/src/utils/node/vitePlugins.ts +98 -0
|
@@ -24,7 +24,7 @@ import { ElAvatar } from 'element-plus'
|
|
|
24
24
|
import { useDark } from '@vueuse/core'
|
|
25
25
|
import { computed } from 'vue'
|
|
26
26
|
import { useBlogConfig } from '../composables/config/blog'
|
|
27
|
-
import { getImageUrl } from '../utils'
|
|
27
|
+
import { getImageUrl } from '../utils/client'
|
|
28
28
|
|
|
29
29
|
const isDark = useDark({
|
|
30
30
|
storageKey: 'vitepress-theme-appearance'
|
|
@@ -114,4 +114,4 @@ const friendList = computed(() => {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
-
</style>
|
|
117
|
+
</style>
|
|
@@ -44,7 +44,7 @@ import { ref, computed } from 'vue'
|
|
|
44
44
|
import { ElButton, ElLink } from 'element-plus'
|
|
45
45
|
import { withBase } from 'vitepress'
|
|
46
46
|
import { useArticles, useBlogConfig } from '../composables/config/blog'
|
|
47
|
-
import { formatShowDate } from '../utils/
|
|
47
|
+
import { formatShowDate } from '../utils/client'
|
|
48
48
|
|
|
49
49
|
const { hotArticle } = useBlogConfig()
|
|
50
50
|
const title = computed(() => hotArticle?.title || '🔥 精选文章')
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
import { withBase } from 'vitepress'
|
|
43
43
|
import { computed } from 'vue'
|
|
44
44
|
import { useWindowSize } from '@vueuse/core'
|
|
45
|
-
import { formatShowDate } from '../utils/
|
|
45
|
+
import { formatShowDate } from '../utils/client'
|
|
46
46
|
|
|
47
47
|
const { width } = useWindowSize()
|
|
48
48
|
const inMobile = computed(() => width.value <= 500)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="
|
|
3
|
+
class="recommend"
|
|
4
|
+
:class="{ card: sidebarStyle === 'card' }"
|
|
4
5
|
v-if="_recommend !== false && (recommendList.length || emptyText)"
|
|
5
6
|
data-pagefind-ignore="all"
|
|
6
7
|
>
|
|
7
8
|
<!-- 头部 -->
|
|
8
9
|
<div class="card-header">
|
|
9
|
-
<span class="title">{{ title }}</span>
|
|
10
|
+
<span class="title" v-if="title">{{ title }}</span>
|
|
10
11
|
<el-button
|
|
11
12
|
v-if="showChangeBtn"
|
|
12
13
|
size="small"
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
<ol class="recommend-container" v-if="currentWikiData.length">
|
|
21
22
|
<li v-for="(v, idx) in currentWikiData" :key="v.route">
|
|
22
23
|
<!-- 序号 -->
|
|
23
|
-
<i class="num">{{ idx + 1 }}</i>
|
|
24
|
+
<i class="num">{{ startIdx + idx + 1 }}</i>
|
|
24
25
|
<!-- 简介 -->
|
|
25
26
|
<div class="des">
|
|
26
27
|
<!-- title -->
|
|
@@ -47,20 +48,27 @@
|
|
|
47
48
|
|
|
48
49
|
<script lang="ts" setup>
|
|
49
50
|
import { ref, computed } from 'vue'
|
|
50
|
-
import { ElButton, ElLink } from 'element-plus'
|
|
51
51
|
import { useRoute, withBase } from 'vitepress'
|
|
52
|
-
import {
|
|
52
|
+
import { ElButton, ElLink } from 'element-plus'
|
|
53
|
+
import { formatShowDate } from '../utils/client'
|
|
53
54
|
import { useArticles, useBlogConfig } from '../composables/config/blog'
|
|
54
55
|
|
|
55
56
|
const { recommend: _recommend } = useBlogConfig()
|
|
56
57
|
|
|
58
|
+
const sidebarStyle = computed(() =>
|
|
59
|
+
_recommend && _recommend?.style ? _recommend.style : 'sidebar'
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
const recommendPadding = computed(() =>
|
|
63
|
+
sidebarStyle.value === 'card' ? '10px' : '0px'
|
|
64
|
+
)
|
|
57
65
|
const recommend = computed(() =>
|
|
58
66
|
_recommend === false ? undefined : _recommend
|
|
59
67
|
)
|
|
60
|
-
const title = computed(() => recommend.value?.title
|
|
68
|
+
const title = computed(() => recommend.value?.title ?? '🔍 相关文章')
|
|
61
69
|
const pageSize = computed(() => recommend.value?.pageSize || 9)
|
|
62
70
|
const nextText = computed(() => recommend.value?.nextText || '换一组')
|
|
63
|
-
const emptyText = computed(() => recommend.value?.empty ?? '
|
|
71
|
+
const emptyText = computed(() => recommend.value?.empty ?? '暂无相关文章')
|
|
64
72
|
|
|
65
73
|
const docs = useArticles()
|
|
66
74
|
|
|
@@ -110,6 +118,8 @@ const changePage = () => {
|
|
|
110
118
|
currentPage.value % Math.ceil(recommendList.value.length / pageSize.value)
|
|
111
119
|
currentPage.value = newIdx + 1
|
|
112
120
|
}
|
|
121
|
+
// 当前页开始的序号
|
|
122
|
+
const startIdx = computed(() => (currentPage.value - 1) * pageSize.value)
|
|
113
123
|
|
|
114
124
|
const currentWikiData = computed(() => {
|
|
115
125
|
const startIdx = (currentPage.value - 1) * pageSize.value
|
|
@@ -143,7 +153,7 @@ const showChangeBtn = computed(() => {
|
|
|
143
153
|
|
|
144
154
|
.recommend {
|
|
145
155
|
flex-direction: column;
|
|
146
|
-
padding:
|
|
156
|
+
padding: v-bind(recommendPadding);
|
|
147
157
|
}
|
|
148
158
|
|
|
149
159
|
.recommend-container {
|
|
@@ -163,7 +173,7 @@ const showChangeBtn = computed(() => {
|
|
|
163
173
|
color: var(--description-font-color);
|
|
164
174
|
font-weight: 600;
|
|
165
175
|
margin: 6px 8px 10px 0;
|
|
166
|
-
width:
|
|
176
|
+
width: 22px;
|
|
167
177
|
height: 18px;
|
|
168
178
|
line-height: 18px;
|
|
169
179
|
text-align: center;
|
|
@@ -132,7 +132,7 @@ import { computed, nextTick, ref, watch, onBeforeMount, onMounted } from 'vue'
|
|
|
132
132
|
import { Command } from 'vue-command-palette'
|
|
133
133
|
import { useRoute, useRouter, withBase } from 'vitepress'
|
|
134
134
|
import { useMagicKeys, useWindowSize } from '@vueuse/core'
|
|
135
|
-
import { chineseSearchOptimize, formatDate } from '../utils'
|
|
135
|
+
import { chineseSearchOptimize, formatDate } from '../utils/client'
|
|
136
136
|
import { useArticles, useBlogConfig } from '../composables/config/blog'
|
|
137
137
|
import { Theme } from '../composables/config'
|
|
138
138
|
import LogoPagefind from './LogoPagefind.vue'
|
|
@@ -1,19 +1,35 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="sidebar" data-pagefind-ignore="all"><BlogRecommendArticle /></div>
|
|
3
|
-
</template>
|
|
4
|
-
|
|
5
1
|
<script lang="ts" setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { useBlogConfig } from '../composables/config/blog'
|
|
6
4
|
import BlogRecommendArticle from './BlogRecommendArticle.vue'
|
|
5
|
+
|
|
6
|
+
const { recommend: _recommend } = useBlogConfig()
|
|
7
|
+
|
|
8
|
+
const sidebarStyle = computed(() =>
|
|
9
|
+
_recommend && _recommend?.style ? _recommend.style : 'card'
|
|
10
|
+
)
|
|
11
|
+
const marginTop = computed(() =>
|
|
12
|
+
sidebarStyle.value === 'card' ? '40px' : '0px'
|
|
13
|
+
)
|
|
14
|
+
const marginTopMini = computed(() =>
|
|
15
|
+
sidebarStyle.value === 'card' ? '60px' : '0px'
|
|
16
|
+
)
|
|
7
17
|
</script>
|
|
8
18
|
|
|
19
|
+
<template>
|
|
20
|
+
<div v-if="_recommend !== false" class="sidebar" data-pagefind-ignore="all">
|
|
21
|
+
<BlogRecommendArticle />
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
9
25
|
<style lang="scss" scoped>
|
|
10
26
|
.sidebar {
|
|
11
|
-
margin-top:
|
|
27
|
+
margin-top: v-bind(marginTop);
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
@media screen and (min-width: 960px) and (max-width: 1120px) {
|
|
15
31
|
.sidebar {
|
|
16
|
-
margin-top:
|
|
32
|
+
margin-top: v-bind(marginTopMini);
|
|
17
33
|
}
|
|
18
34
|
}
|
|
19
35
|
</style>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ElButton } from 'element-plus'
|
|
2
2
|
import type { DefaultTheme } from 'vitepress'
|
|
3
|
+
import type { FeedOptions } from 'feed'
|
|
3
4
|
|
|
4
5
|
export namespace BlogPopover {
|
|
5
6
|
export interface Title {
|
|
@@ -106,11 +107,16 @@ export namespace Theme {
|
|
|
106
107
|
nextText?: string
|
|
107
108
|
/**
|
|
108
109
|
* 是否展示当前正在浏览的文章在左侧
|
|
109
|
-
* @default
|
|
110
|
+
* @default true
|
|
110
111
|
*/
|
|
111
112
|
showSelf?: boolean
|
|
112
113
|
filter?: (page: Theme.PageData) => boolean
|
|
113
114
|
empty?: string | boolean
|
|
115
|
+
/**
|
|
116
|
+
* 设置推荐文章的展示风格
|
|
117
|
+
* @default 'sidebar'
|
|
118
|
+
*/
|
|
119
|
+
style?: 'card' | 'sidebar'
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
export interface HomeBlog {
|
|
@@ -261,11 +267,37 @@ export namespace Theme {
|
|
|
261
267
|
works?: UserWorks
|
|
262
268
|
/**
|
|
263
269
|
* https://mermaid.js.org/config/setup/modules/mermaidAPI.html#mermaidapi-configuration-defaults for options
|
|
264
|
-
* @default
|
|
270
|
+
* @default true
|
|
265
271
|
*/
|
|
266
272
|
mermaid?: any
|
|
273
|
+
/**
|
|
274
|
+
* 设置解析 frontmatter 里 date 的时区
|
|
275
|
+
* @default 8 => 'UTC+8'
|
|
276
|
+
* */
|
|
277
|
+
timeZone?: number
|
|
278
|
+
/**
|
|
279
|
+
* 启用RSS配置
|
|
280
|
+
*/
|
|
281
|
+
RSS?: RSSOptions
|
|
267
282
|
}
|
|
268
283
|
|
|
284
|
+
export type RSSOptions = FeedOptions & {
|
|
285
|
+
baseUrl: string
|
|
286
|
+
/**
|
|
287
|
+
* 线上访问的RSS地址
|
|
288
|
+
*/
|
|
289
|
+
url: string
|
|
290
|
+
/**
|
|
291
|
+
* 输出的RSS文件名
|
|
292
|
+
* @default 'feed.rss'
|
|
293
|
+
*/
|
|
294
|
+
filename?: string
|
|
295
|
+
/**
|
|
296
|
+
* 是否展示RSS的图标
|
|
297
|
+
* @default true
|
|
298
|
+
*/
|
|
299
|
+
showIcon?: boolean
|
|
300
|
+
}
|
|
269
301
|
export interface Config extends DefaultTheme.Config {
|
|
270
302
|
blog?: BlogConfig
|
|
271
303
|
}
|
package/src/node.ts
CHANGED
|
@@ -1,388 +1,56 @@
|
|
|
1
1
|
/* eslint-disable global-require */
|
|
2
2
|
/* eslint-disable prefer-rest-params */
|
|
3
|
-
import
|
|
4
|
-
import matter from 'gray-matter'
|
|
5
|
-
import fs from 'fs'
|
|
6
|
-
import { execSync, spawn, spawnSync } from 'child_process'
|
|
7
|
-
import path from 'path'
|
|
8
|
-
import type { SiteConfig, UserConfig } from 'vitepress'
|
|
9
|
-
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
|
|
10
|
-
import { formatDate } from './utils/index'
|
|
3
|
+
import type { UserConfig } from 'vitepress'
|
|
11
4
|
import type { Theme } from './composables/config/index'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
import {
|
|
6
|
+
getMarkdownPlugins,
|
|
7
|
+
registerMdPlugins,
|
|
8
|
+
wrapperCfgWithMermaid,
|
|
9
|
+
supportRunExtendsPlugin
|
|
10
|
+
} from './utils/node/mdPlugins'
|
|
11
|
+
import { getArticles, patchVPThemeConfig } from './utils/node/theme'
|
|
12
|
+
import { getVitePlugins, registerVitePlugins } from './utils/node/vitePlugins'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 获取主题的配置
|
|
16
|
+
* @param cfg 主题配置
|
|
17
|
+
*/
|
|
15
18
|
export function getThemeConfig(cfg?: Partial<Theme.BlogConfig>) {
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const data = files
|
|
20
|
-
.map((v) => {
|
|
21
|
-
let route = v
|
|
22
|
-
// 处理文件后缀名
|
|
23
|
-
.replace('.md', '')
|
|
24
|
-
|
|
25
|
-
// 去除 srcDir 处理目录名
|
|
26
|
-
if (route.startsWith('./')) {
|
|
27
|
-
route = route.replace(
|
|
28
|
-
new RegExp(
|
|
29
|
-
`^\\.\\/${path
|
|
30
|
-
.join(srcDir, '/')
|
|
31
|
-
.replace(new RegExp(`\\${path.sep}`, 'g'), '/')}`
|
|
32
|
-
),
|
|
33
|
-
''
|
|
34
|
-
)
|
|
35
|
-
} else {
|
|
36
|
-
route = route.replace(
|
|
37
|
-
new RegExp(
|
|
38
|
-
`^${path
|
|
39
|
-
.join(srcDir, '/')
|
|
40
|
-
.replace(new RegExp(`\\${path.sep}`, 'g'), '/')}`
|
|
41
|
-
),
|
|
42
|
-
''
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const fileContent = fs.readFileSync(v, 'utf-8')
|
|
47
|
-
|
|
48
|
-
// TODO: 支持JSON
|
|
49
|
-
const meta: Partial<Theme.PageMeta> = {
|
|
50
|
-
...matter(fileContent).data
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!meta.title) {
|
|
54
|
-
meta.title = getDefaultTitle(fileContent)
|
|
55
|
-
}
|
|
56
|
-
if (!meta.date) {
|
|
57
|
-
// getGitTimestamp(v).then((v) => {
|
|
58
|
-
// meta.date = formatDate(v)
|
|
59
|
-
// })
|
|
60
|
-
meta.date = getFileBirthTime(v)
|
|
61
|
-
} else {
|
|
62
|
-
// TODO: 开放配置,设置时区
|
|
63
|
-
meta.date = formatDate(
|
|
64
|
-
new Date(`${new Date(meta.date).toUTCString()}+8`)
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// 处理tags和categories,兼容历史文章
|
|
69
|
-
meta.categories =
|
|
70
|
-
typeof meta.categories === 'string'
|
|
71
|
-
? [meta.categories]
|
|
72
|
-
: meta.categories
|
|
73
|
-
meta.tags = typeof meta.tags === 'string' ? [meta.tags] : meta.tags
|
|
74
|
-
meta.tag = [meta.tag || []]
|
|
75
|
-
.flat()
|
|
76
|
-
.concat([
|
|
77
|
-
...new Set([...(meta.categories || []), ...(meta.tags || [])])
|
|
78
|
-
])
|
|
79
|
-
|
|
80
|
-
// 获取摘要信息
|
|
81
|
-
const wordCount = 100
|
|
82
|
-
meta.description =
|
|
83
|
-
meta.description || getTextSummary(fileContent, wordCount)
|
|
84
|
-
|
|
85
|
-
// 获取封面图
|
|
86
|
-
meta.cover =
|
|
87
|
-
meta.cover ??
|
|
88
|
-
(fileContent.match(/[!]\[.*?\]\((https:\/\/.+)\)/)?.[1] || '')
|
|
89
|
-
|
|
90
|
-
// 是否发布 默认发布
|
|
91
|
-
if (meta.publish === false) {
|
|
92
|
-
meta.hidden = true
|
|
93
|
-
meta.recommend = false
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
route: `/${route}`,
|
|
98
|
-
meta
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
.filter((v) => v.meta.layout !== 'home')
|
|
102
|
-
|
|
103
|
-
const extraConfig: any = {}
|
|
19
|
+
// 文章数据
|
|
20
|
+
const pagesData = getArticles(cfg)
|
|
21
|
+
const extraVPConfig: any = {}
|
|
104
22
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
109
|
-
checkKeys.push('vite')
|
|
110
|
-
|
|
111
|
-
let resolveConfig: any
|
|
112
|
-
extraConfig.vite = {
|
|
113
|
-
plugins: [
|
|
114
|
-
{
|
|
115
|
-
name: '@sugarar/theme-plugin-pagefind',
|
|
116
|
-
enforce: 'pre',
|
|
117
|
-
configResolved(config: any) {
|
|
118
|
-
if (resolveConfig) {
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
resolveConfig = config
|
|
122
|
-
|
|
123
|
-
const vitepressConfig: SiteConfig = config.vitepress
|
|
124
|
-
if (!vitepressConfig) {
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 添加 自定义 vitepress 的钩子
|
|
129
|
-
const selfBuildEnd = vitepressConfig.buildEnd
|
|
130
|
-
vitepressConfig.buildEnd = (siteConfig: any) => {
|
|
131
|
-
// 调用自己的
|
|
132
|
-
selfBuildEnd?.(siteConfig)
|
|
133
|
-
// 调用pagefind
|
|
134
|
-
const ignore: string[] = [
|
|
135
|
-
// 侧边栏内容
|
|
136
|
-
'div.aside',
|
|
137
|
-
// 标题锚点
|
|
138
|
-
'a.header-anchor'
|
|
139
|
-
]
|
|
140
|
-
const { log } = console
|
|
141
|
-
log()
|
|
142
|
-
log('=== pagefind: https://pagefind.app/ ===')
|
|
143
|
-
let command = `npx pagefind --source ${path.join(
|
|
144
|
-
process.argv.slice(2)?.[1] || '.',
|
|
145
|
-
'.vitepress/dist'
|
|
146
|
-
)}`
|
|
147
|
-
|
|
148
|
-
if (ignore.length) {
|
|
149
|
-
command += ` --exclude-selectors "${ignore.join(', ')}"`
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
log(command)
|
|
153
|
-
log()
|
|
154
|
-
execSync(command, {
|
|
155
|
-
stdio: 'inherit'
|
|
156
|
-
})
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
// 添加检索的内容标识
|
|
160
|
-
transform(code: string, id: string) {
|
|
161
|
-
if (id.endsWith('theme-default/Layout.vue')) {
|
|
162
|
-
return code.replace(
|
|
163
|
-
'<VPContent>',
|
|
164
|
-
'<VPContent data-pagefind-body>'
|
|
165
|
-
)
|
|
166
|
-
}
|
|
167
|
-
return code
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
const markdownPlugin: any[] = []
|
|
174
|
-
// tabs支持
|
|
175
|
-
if (cfg?.tabs) {
|
|
176
|
-
markdownPlugin.push(tabsMarkdownPlugin)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// 流程图支持
|
|
180
|
-
if (cfg) {
|
|
181
|
-
cfg.mermaid = cfg?.mermaid ?? true
|
|
182
|
-
}
|
|
183
|
-
if (cfg?.mermaid !== false) {
|
|
184
|
-
const { MermaidMarkdown } = require('vitepress-plugin-mermaid')
|
|
185
|
-
markdownPlugin.push(MermaidMarkdown)
|
|
186
|
-
}
|
|
23
|
+
// 获取要加载的vite插件
|
|
24
|
+
const vitePlugins = getVitePlugins(cfg)
|
|
25
|
+
// 注册Vite插件
|
|
26
|
+
registerVitePlugins(extraVPConfig, vitePlugins)
|
|
187
27
|
|
|
28
|
+
// 获取要加载的markdown插件
|
|
29
|
+
const markdownPlugin = getMarkdownPlugins(cfg)
|
|
188
30
|
// 注册markdown插件
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
config(...rest: any[]) {
|
|
192
|
-
markdownPlugin.forEach((plugin) => {
|
|
193
|
-
plugin?.(...rest)
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
31
|
+
registerMdPlugins(extraVPConfig, markdownPlugin)
|
|
32
|
+
|
|
198
33
|
return {
|
|
199
34
|
themeConfig: {
|
|
200
35
|
blog: {
|
|
201
|
-
pagesData
|
|
36
|
+
pagesData,
|
|
202
37
|
...cfg
|
|
203
38
|
},
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
sidebar: [
|
|
207
|
-
{
|
|
208
|
-
text: '',
|
|
209
|
-
items: []
|
|
210
|
-
}
|
|
211
|
-
]
|
|
212
|
-
}
|
|
213
|
-
: undefined)
|
|
39
|
+
// 补充一些额外的配置用于继承
|
|
40
|
+
...patchVPThemeConfig(cfg)
|
|
214
41
|
},
|
|
215
|
-
...
|
|
42
|
+
...extraVPConfig
|
|
216
43
|
}
|
|
217
44
|
}
|
|
218
45
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.split('\n')
|
|
223
|
-
?.find((str) => {
|
|
224
|
-
return str.startsWith('# ')
|
|
225
|
-
})
|
|
226
|
-
?.slice(2)
|
|
227
|
-
.replace(/^\s+|\s+$/g, '') || ''
|
|
228
|
-
return title
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export function clearMatterContent(content: string) {
|
|
232
|
-
let first___: unknown
|
|
233
|
-
let second___: unknown
|
|
234
|
-
|
|
235
|
-
const lines = content.split('\n').reduce<string[]>((pre, line) => {
|
|
236
|
-
// 移除开头的空白行
|
|
237
|
-
if (!line.trim() && pre.length === 0) {
|
|
238
|
-
return pre
|
|
239
|
-
}
|
|
240
|
-
if (line.trim() === '---') {
|
|
241
|
-
if (first___ === undefined) {
|
|
242
|
-
first___ = pre.length
|
|
243
|
-
} else if (second___ === undefined) {
|
|
244
|
-
second___ = pre.length
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
pre.push(line)
|
|
248
|
-
return pre
|
|
249
|
-
}, [])
|
|
250
|
-
return (
|
|
251
|
-
lines
|
|
252
|
-
// 剔除---之间的内容
|
|
253
|
-
.slice((second___ as number) || 0)
|
|
254
|
-
.join('\n')
|
|
255
|
-
)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export function getFileBirthTime(url: string) {
|
|
259
|
-
let date = new Date()
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
// 参考 vitepress 中的 getGitTimestamp 实现
|
|
263
|
-
const infoStr = spawnSync('git', ['log', '-1', '--pretty="%ci"', url])
|
|
264
|
-
.stdout?.toString()
|
|
265
|
-
.replace(/["']/g, '')
|
|
266
|
-
.trim()
|
|
267
|
-
if (infoStr) {
|
|
268
|
-
date = new Date(infoStr)
|
|
269
|
-
}
|
|
270
|
-
} catch (error) {
|
|
271
|
-
return formatDate(date)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return formatDate(date)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export function getGitTimestamp(file: string) {
|
|
278
|
-
return new Promise((resolve, reject) => {
|
|
279
|
-
const child = spawn('git', ['log', '-1', '--pretty="%ci"', file])
|
|
280
|
-
let output = ''
|
|
281
|
-
child.stdout.on('data', (d) => {
|
|
282
|
-
output += String(d)
|
|
283
|
-
})
|
|
284
|
-
child.on('close', () => {
|
|
285
|
-
resolve(+new Date(output))
|
|
286
|
-
})
|
|
287
|
-
child.on('error', reject)
|
|
288
|
-
})
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function getTextSummary(text: string, count = 100) {
|
|
292
|
-
return (
|
|
293
|
-
clearMatterContent(text)
|
|
294
|
-
.match(/^# ([\s\S]+)/m)?.[1]
|
|
295
|
-
// 除去标题
|
|
296
|
-
?.replace(/#/g, '')
|
|
297
|
-
// 除去图片
|
|
298
|
-
?.replace(/!\[.*?\]\(.*?\)/g, '')
|
|
299
|
-
// 除去链接
|
|
300
|
-
?.replace(/\[(.*?)\]\(.*?\)/g, '$1')
|
|
301
|
-
// 除去加粗
|
|
302
|
-
?.replace(/\*\*(.*?)\*\*/g, '$1')
|
|
303
|
-
?.split('\n')
|
|
304
|
-
?.filter((v) => !!v)
|
|
305
|
-
?.slice(1)
|
|
306
|
-
?.join('\n')
|
|
307
|
-
?.replace(/>(.*)/, '')
|
|
308
|
-
?.slice(0, count)
|
|
309
|
-
)
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export function assignMermaid(config: any) {
|
|
313
|
-
if (!config.mermaid) return
|
|
314
|
-
|
|
315
|
-
if (!config.vite) config.vite = {}
|
|
316
|
-
if (!config.vite.plugins) config.vite.plugins = []
|
|
317
|
-
const { MermaidPlugin } = require('vitepress-plugin-mermaid')
|
|
318
|
-
config.vite.plugins.push(MermaidPlugin(config.mermaid))
|
|
319
|
-
if (!config.vite.resolve) config.vite.resolve = {}
|
|
320
|
-
if (!config.vite.resolve.alias) config.vite.resolve.alias = {}
|
|
321
|
-
|
|
322
|
-
config.vite.resolve.alias = [
|
|
323
|
-
...aliasObjectToArray({
|
|
324
|
-
...config.vite.resolve.alias,
|
|
325
|
-
'cytoscape/dist/cytoscape.umd.js': 'cytoscape/dist/cytoscape.esm.js',
|
|
326
|
-
mermaid: 'mermaid/dist/mermaid.esm.mjs'
|
|
327
|
-
}),
|
|
328
|
-
{ find: /^dayjs\/(.*).js/, replacement: 'dayjs/esm/$1' }
|
|
329
|
-
]
|
|
330
|
-
}
|
|
331
|
-
function aliasObjectToArray(obj: Record<string, string>) {
|
|
332
|
-
return Object.entries(obj).map(([find, replacement]) => ({
|
|
333
|
-
find,
|
|
334
|
-
replacement
|
|
335
|
-
}))
|
|
336
|
-
}
|
|
46
|
+
/**
|
|
47
|
+
* defineConfig Helper
|
|
48
|
+
*/
|
|
337
49
|
export function defineConfig(config: UserConfig<Theme.Config>): any {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (config.themeConfig?.themeConfig) {
|
|
341
|
-
config.extends = checkKeys.reduce((pre, key) => {
|
|
342
|
-
// @ts-ignore
|
|
343
|
-
pre[key] = config.themeConfig[key]
|
|
344
|
-
// @ts-ignore
|
|
345
|
-
delete config.themeConfig[key]
|
|
346
|
-
return pre
|
|
347
|
-
}, {})
|
|
348
|
-
|
|
349
|
-
// 打印warn信息
|
|
350
|
-
setTimeout(() => {
|
|
351
|
-
console.warn('==↓ 主题配置方式过期,请尽快参照文档更新 ↓==')
|
|
352
|
-
console.warn('https://theme.sugarat.top/config/global.html')
|
|
353
|
-
}, 1200)
|
|
354
|
-
}
|
|
355
|
-
// @ts-ignore
|
|
356
|
-
const extendThemeConfig = config.extends?.themeConfig
|
|
357
|
-
?.blog as Theme.BlogConfig
|
|
358
|
-
|
|
359
|
-
// 开关支持Mermaid
|
|
360
|
-
const resultConfig =
|
|
361
|
-
extendThemeConfig.mermaid === false
|
|
362
|
-
? config
|
|
363
|
-
: {
|
|
364
|
-
...config,
|
|
365
|
-
mermaid:
|
|
366
|
-
extendThemeConfig.mermaid === true ? {} : extendThemeConfig.mermaid
|
|
367
|
-
}
|
|
368
|
-
assignMermaid(resultConfig)
|
|
369
|
-
|
|
370
|
-
// 处理markdown插件
|
|
371
|
-
if (!resultConfig.markdown) resultConfig.markdown = {}
|
|
372
|
-
// @ts-ignore
|
|
373
|
-
if (config.extends?.markdown?.config) {
|
|
374
|
-
const markdownExtendsConfigOriginal =
|
|
375
|
-
// @ts-ignore
|
|
376
|
-
config.extends?.markdown?.config
|
|
377
|
-
const selfMarkdownConfig = resultConfig.markdown?.config
|
|
378
|
-
|
|
379
|
-
resultConfig.markdown.config = (...rest: any[]) => {
|
|
380
|
-
// @ts-ignore
|
|
381
|
-
selfMarkdownConfig?.(...rest)
|
|
382
|
-
markdownExtendsConfigOriginal?.(...rest)
|
|
383
|
-
}
|
|
384
|
-
}
|
|
50
|
+
const resultConfig = wrapperCfgWithMermaid(config)
|
|
51
|
+
supportRunExtendsPlugin(resultConfig)
|
|
385
52
|
return resultConfig
|
|
386
53
|
}
|
|
387
54
|
|
|
55
|
+
// 重新导包 tabsMarkdownPlugin 导出CJS格式支持
|
|
388
56
|
export { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
|
package/src/styles/index.scss
CHANGED