Merge pull request #160 from pengzhanbo/perf-ui
fix(cli): incorrect exec context
This commit is contained in:
commit
2376379772
@ -1,3 +1,5 @@
|
||||
import process from 'node:process'
|
||||
import path from 'node:path'
|
||||
import { intro, outro, spinner } from '@clack/prompts'
|
||||
import { execaCommand } from 'execa'
|
||||
import colors from 'picocolors'
|
||||
@ -19,16 +21,17 @@ export async function run(mode: Mode, root?: string) {
|
||||
|
||||
await generate(mode, data)
|
||||
|
||||
const cwd = path.join(process.cwd(), data.root)
|
||||
if (data.git) {
|
||||
progress.message(t('spinner.git'))
|
||||
await execaCommand('git init')
|
||||
await execaCommand('git init', { cwd })
|
||||
}
|
||||
|
||||
const pm = data.packageManager
|
||||
|
||||
if (data.install) {
|
||||
progress.message(t('spinner.install'))
|
||||
await execaCommand(pm === 'yarn' ? 'yarn' : `${pm} install`)
|
||||
await execaCommand(pm === 'yarn' ? 'yarn' : `${pm} install`, { cwd })
|
||||
}
|
||||
|
||||
const cdCommand = mode === Mode.create ? colors.green(`cd ${data.root}`) : ''
|
||||
|
||||
@ -1,165 +0,0 @@
|
||||
import { defineNotesConfig } from 'vuepress-theme-plume'
|
||||
|
||||
export const zhNotes = defineNotesConfig({
|
||||
dir: 'notes',
|
||||
link: '/',
|
||||
notes: [
|
||||
{
|
||||
dir: 'theme/guide',
|
||||
link: '/guide/',
|
||||
sidebar: [
|
||||
{
|
||||
text: '快速开始',
|
||||
collapsed: false,
|
||||
icon: 'carbon:idea',
|
||||
items: ['介绍', '安装与使用', '博客', '知识笔记', '编写文章', '国际化', '部署'],
|
||||
},
|
||||
{
|
||||
text: '写作',
|
||||
icon: 'fluent-mdl2:edit-create',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'markdown',
|
||||
icon: 'material-symbols:markdown-outline',
|
||||
prefix: 'markdown',
|
||||
collapsed: true,
|
||||
items: ['基础', '扩展', '进阶'],
|
||||
},
|
||||
{
|
||||
text: '代码块',
|
||||
prefix: '代码',
|
||||
icon: 'ph:code-bold',
|
||||
collapsed: true,
|
||||
items: ['介绍', '特性支持', '代码组', '导入代码', 'twoslash'],
|
||||
},
|
||||
{
|
||||
text: '代码演示',
|
||||
prefix: '代码演示',
|
||||
icon: 'carbon:demo',
|
||||
collapsed: true,
|
||||
items: ['前端', 'rust', 'golang', 'kotlin', 'codepen', 'jsFiddle', 'codeSandbox', 'replit'],
|
||||
},
|
||||
{
|
||||
text: '图表',
|
||||
icon: 'mdi:chart-line',
|
||||
prefix: '图表',
|
||||
collapsed: true,
|
||||
items: ['chart', 'echarts', 'mermaid', 'flowchart'],
|
||||
},
|
||||
{
|
||||
text: '资源嵌入',
|
||||
icon: 'dashicons:embed-video',
|
||||
prefix: '嵌入',
|
||||
collapsed: true,
|
||||
items: ['pdf', 'bilibili', 'youtube'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '功能',
|
||||
icon: 'lucide:box',
|
||||
collapsed: false,
|
||||
prefix: '功能',
|
||||
items: ['图标', '代码复制', '内容搜索', '评论', '加密', '文章水印', '友情链接页', 'seo', 'sitemap'],
|
||||
},
|
||||
{
|
||||
text: '组件',
|
||||
prefix: '组件',
|
||||
icon: 'uiw:component',
|
||||
collapsed: false,
|
||||
items: ['徽章', '图标', '隐秘文本', '卡片', '链接卡片', '图片卡片', '卡片容器', '首页布局容器', 'repoCard', 'npmBadge'],
|
||||
},
|
||||
{
|
||||
text: '自定义',
|
||||
icon: 'material-symbols:dashboard-customize-outline-rounded',
|
||||
collapsed: false,
|
||||
items: ['自定义首页', '自定义样式', '布局插槽', '组件覆写'],
|
||||
},
|
||||
{
|
||||
text: 'API',
|
||||
icon: 'mdi:api',
|
||||
collapsed: false,
|
||||
items: ['api-客户端', 'api-node'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
dir: 'theme/config',
|
||||
link: '/config/',
|
||||
sidebar: [
|
||||
{
|
||||
text: '配置',
|
||||
collapsed: false,
|
||||
items: [
|
||||
'配置说明',
|
||||
'多语言配置',
|
||||
'主题配置',
|
||||
'导航栏配置',
|
||||
'notes配置',
|
||||
'侧边栏配置',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'frontmatter',
|
||||
prefix: 'frontmatter',
|
||||
collapsed: false,
|
||||
items: ['basic', 'home', 'post', 'friend'],
|
||||
},
|
||||
{
|
||||
text: '内置插件',
|
||||
prefix: 'plugins',
|
||||
collapsed: false,
|
||||
items: ['', '代码高亮', '搜索', '阅读统计', 'markdown增强', 'markdownPower', '百度统计', '水印'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
dir: 'plugins',
|
||||
link: '/plugins/',
|
||||
sidebar: [
|
||||
{
|
||||
text: '插件',
|
||||
link: '/plugins/',
|
||||
items: [
|
||||
// 'caniuse',
|
||||
// 'iconify',
|
||||
'shiki',
|
||||
'md-power',
|
||||
'content-updated',
|
||||
{
|
||||
text: 'plugin-netlify-functions',
|
||||
dir: 'netlify-functions',
|
||||
link: '/plugins/plugin-netlify-functions/',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'介绍',
|
||||
'使用',
|
||||
'功能',
|
||||
'api',
|
||||
'functions',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
dir: 'tools',
|
||||
link: '/tools/',
|
||||
sidebar: [
|
||||
{
|
||||
text: '工具',
|
||||
icon: 'tabler:tools',
|
||||
items: ['custom-theme', 'home-hero-tint-plate', 'caniuse'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export const enNotes = defineNotesConfig({
|
||||
dir: 'en/notes',
|
||||
link: '/',
|
||||
notes: [],
|
||||
})
|
||||
7
docs/.vuepress/notes/en/index.ts
Normal file
7
docs/.vuepress/notes/en/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineNotesConfig } from 'vuepress-theme-plume'
|
||||
|
||||
export const enNotes = defineNotesConfig({
|
||||
dir: 'en/notes',
|
||||
link: '/',
|
||||
notes: [],
|
||||
})
|
||||
2
docs/.vuepress/notes/index.ts
Normal file
2
docs/.vuepress/notes/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './en/index.js'
|
||||
export * from './zh/index.js'
|
||||
16
docs/.vuepress/notes/zh/index.ts
Normal file
16
docs/.vuepress/notes/zh/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { defineNotesConfig } from 'vuepress-theme-plume'
|
||||
import { themeGuide } from './theme-guide'
|
||||
import { themeConfig } from './theme-config'
|
||||
import { plugins } from './plugins'
|
||||
import { tools } from './tools'
|
||||
|
||||
export const zhNotes = defineNotesConfig({
|
||||
dir: 'notes',
|
||||
link: '/',
|
||||
notes: [
|
||||
themeGuide,
|
||||
themeConfig,
|
||||
plugins,
|
||||
tools,
|
||||
],
|
||||
})
|
||||
32
docs/.vuepress/notes/zh/plugins.ts
Normal file
32
docs/.vuepress/notes/zh/plugins.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { defineNoteConfig } from 'vuepress-theme-plume'
|
||||
|
||||
export const plugins = defineNoteConfig({
|
||||
dir: 'plugins',
|
||||
link: '/plugins/',
|
||||
sidebar: [
|
||||
{
|
||||
text: '插件',
|
||||
link: '/plugins/',
|
||||
items: [
|
||||
// 'caniuse',
|
||||
// 'iconify',
|
||||
'shiki',
|
||||
'md-power',
|
||||
'content-updated',
|
||||
{
|
||||
text: 'plugin-netlify-functions',
|
||||
dir: 'netlify-functions',
|
||||
link: '/plugins/plugin-netlify-functions/',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'介绍',
|
||||
'使用',
|
||||
'功能',
|
||||
'api',
|
||||
'functions',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
46
docs/.vuepress/notes/zh/theme-config.ts
Normal file
46
docs/.vuepress/notes/zh/theme-config.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { defineNoteConfig } from 'vuepress-theme-plume'
|
||||
|
||||
export const themeConfig = defineNoteConfig({
|
||||
dir: 'theme/config',
|
||||
link: '/config/',
|
||||
sidebar: [
|
||||
{
|
||||
text: '配置',
|
||||
collapsed: false,
|
||||
items: [
|
||||
'配置说明',
|
||||
'多语言配置',
|
||||
'主题配置',
|
||||
'导航栏配置',
|
||||
'notes配置',
|
||||
'侧边栏配置',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'frontmatter',
|
||||
prefix: 'frontmatter',
|
||||
collapsed: false,
|
||||
items: [
|
||||
'basic',
|
||||
'home',
|
||||
'post',
|
||||
'friend',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '内置插件',
|
||||
prefix: 'plugins',
|
||||
collapsed: false,
|
||||
items: [
|
||||
'',
|
||||
'代码高亮',
|
||||
'搜索',
|
||||
'阅读统计',
|
||||
'markdown增强',
|
||||
'markdownPower',
|
||||
'百度统计',
|
||||
'水印',
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
147
docs/.vuepress/notes/zh/theme-guide.ts
Normal file
147
docs/.vuepress/notes/zh/theme-guide.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import { defineNoteConfig } from 'vuepress-theme-plume'
|
||||
|
||||
export const themeGuide = defineNoteConfig({
|
||||
dir: 'theme/guide',
|
||||
link: '/guide/',
|
||||
sidebar: [
|
||||
{
|
||||
text: '快速开始',
|
||||
collapsed: false,
|
||||
icon: 'carbon:idea',
|
||||
items: [
|
||||
'介绍',
|
||||
'安装与使用',
|
||||
'博客',
|
||||
'知识笔记',
|
||||
'编写文章',
|
||||
'国际化',
|
||||
'部署',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '写作',
|
||||
icon: 'fluent-mdl2:edit-create',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'markdown',
|
||||
icon: 'material-symbols:markdown-outline',
|
||||
prefix: 'markdown',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'基础',
|
||||
'扩展',
|
||||
'进阶',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '代码块',
|
||||
prefix: '代码',
|
||||
icon: 'ph:code-bold',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'介绍',
|
||||
'特性支持',
|
||||
'代码组',
|
||||
'导入代码',
|
||||
'twoslash',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '代码演示',
|
||||
prefix: '代码演示',
|
||||
icon: 'carbon:demo',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'前端',
|
||||
'rust',
|
||||
'golang',
|
||||
'kotlin',
|
||||
'codepen',
|
||||
'jsFiddle',
|
||||
'codeSandbox',
|
||||
'replit',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '图表',
|
||||
icon: 'mdi:chart-line',
|
||||
prefix: '图表',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'chart',
|
||||
'echarts',
|
||||
'mermaid',
|
||||
'flowchart',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '资源嵌入',
|
||||
icon: 'dashicons:embed-video',
|
||||
prefix: '嵌入',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'pdf',
|
||||
'bilibili',
|
||||
'youtube',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '功能',
|
||||
icon: 'lucide:box',
|
||||
collapsed: false,
|
||||
prefix: '功能',
|
||||
items: [
|
||||
'图标',
|
||||
'代码复制',
|
||||
'内容搜索',
|
||||
'评论',
|
||||
'加密',
|
||||
'文章水印',
|
||||
'友情链接页',
|
||||
'seo',
|
||||
'sitemap',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '组件',
|
||||
prefix: '组件',
|
||||
icon: 'uiw:component',
|
||||
collapsed: false,
|
||||
items: [
|
||||
'徽章',
|
||||
'图标',
|
||||
'隐秘文本',
|
||||
'卡片',
|
||||
'链接卡片',
|
||||
'图片卡片',
|
||||
'卡片容器',
|
||||
'首页布局容器',
|
||||
'repoCard',
|
||||
'npmBadge',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '自定义',
|
||||
icon: 'material-symbols:dashboard-customize-outline-rounded',
|
||||
collapsed: false,
|
||||
items: [
|
||||
'自定义首页',
|
||||
'自定义样式',
|
||||
'布局插槽',
|
||||
'组件覆写',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'API',
|
||||
icon: 'mdi:api',
|
||||
collapsed: false,
|
||||
items: [
|
||||
'api-客户端',
|
||||
'api-node',
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
17
docs/.vuepress/notes/zh/tools.ts
Normal file
17
docs/.vuepress/notes/zh/tools.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { defineNoteConfig } from 'vuepress-theme-plume'
|
||||
|
||||
export const tools = defineNoteConfig({
|
||||
dir: 'tools',
|
||||
link: '/tools/',
|
||||
sidebar: [
|
||||
{
|
||||
text: '工具',
|
||||
icon: 'tabler:tools',
|
||||
items: [
|
||||
'custom-theme',
|
||||
'home-hero-tint-plate',
|
||||
'caniuse',
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -1,5 +1,5 @@
|
||||
import { defineThemeConfig } from 'vuepress-theme-plume'
|
||||
import { enNotes, zhNotes } from './notes.js'
|
||||
import { enNotes, zhNotes } from './notes/index.js'
|
||||
import { enNavbar, zhNavbar } from './navbar.js'
|
||||
|
||||
export default defineThemeConfig({
|
||||
|
||||
@ -6,7 +6,7 @@ export const theme: Theme = plumeTheme({
|
||||
hostname: process.env.SITE_HOST || 'https://plume.pengzhanbo.cn',
|
||||
plugins: {
|
||||
|
||||
shiki: { twoslash: true },
|
||||
shiki: { twoslash: true, lineNumbers: 10 },
|
||||
|
||||
markdownEnhance: {
|
||||
demo: true,
|
||||
|
||||
@ -55,3 +55,8 @@ permalink: /guide/components/cark/
|
||||
</template>
|
||||
这里是卡片内容。
|
||||
</Card>
|
||||
|
||||
:::info
|
||||
在插槽内也可以使用 markdown 语法,但需要注意的是,markdown 语法需要与 标签之间间隔一行。
|
||||
否则将被识别为普通文本。
|
||||
:::
|
||||
|
||||
@ -39,3 +39,48 @@ permalink: /guide/components/link-card/
|
||||
|
||||
<LinkCard title="卡片标题" href="/" description="这里是卡片内容" />
|
||||
<LinkCard icon="twemoji:astonished-face" title="卡片标题" href="/" />
|
||||
|
||||
使用组件插槽,可以实现更丰富的表现。
|
||||
|
||||
**输入:**
|
||||
|
||||
```md :no-line-numbers
|
||||
<LinkCard title="卡片标题" href="/">
|
||||
|
||||
- 这里是卡片内容
|
||||
- 这里是卡片内容
|
||||
|
||||
</LinkCard>
|
||||
|
||||
<LinkCard href="/">
|
||||
<template #title>
|
||||
<span style="color: red" >卡片标题</span>
|
||||
</template>
|
||||
|
||||
- 这里是卡片内容
|
||||
- 这里是卡片内容
|
||||
|
||||
</LinkCard>
|
||||
```
|
||||
|
||||
**输出:**
|
||||
<LinkCard title="卡片标题" href="/">
|
||||
|
||||
- 这里是卡片内容
|
||||
- 这里是卡片内容
|
||||
|
||||
</LinkCard>
|
||||
|
||||
<LinkCard href="/">
|
||||
<template #title>
|
||||
<span style="color: red" >卡片标题</span>
|
||||
</template>
|
||||
|
||||
- 这里是卡片内容
|
||||
- 这里是卡片内容
|
||||
</LinkCard>
|
||||
|
||||
:::info
|
||||
在插槽内也可以使用 markdown 语法,但需要注意的是,markdown 语法需要与 标签之间间隔一行。
|
||||
否则将被识别为普通文本。
|
||||
:::
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"shiki": "^1.14.1",
|
||||
"tm-grammars": "^1.17.4",
|
||||
"tm-grammars": "^1.17.8",
|
||||
"tm-themes": "^1.8.1",
|
||||
"vue": "^3.4.38"
|
||||
},
|
||||
|
||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@ -166,8 +166,8 @@ importers:
|
||||
specifier: ^1.14.1
|
||||
version: 1.14.1
|
||||
tm-grammars:
|
||||
specifier: ^1.17.4
|
||||
version: 1.17.4
|
||||
specifier: ^1.17.8
|
||||
version: 1.17.8
|
||||
tm-themes:
|
||||
specifier: ^1.8.1
|
||||
version: 1.8.1
|
||||
@ -403,9 +403,6 @@ packages:
|
||||
peerDependencies:
|
||||
'@algolia/client-search': '>= 4.9.1 < 6'
|
||||
algoliasearch: '>= 4.9.1 < 6'
|
||||
peerDependenciesMeta:
|
||||
'@algolia/client-search':
|
||||
optional: true
|
||||
|
||||
'@algolia/cache-browser-local-storage@4.20.0':
|
||||
resolution: {integrity: sha512-uujahcBt4DxduBTvYdwO3sBfHuJvJokiC3BP1+O70fglmE1ShkH8lpXqZBac1rrU3FnNYSUs4pL9lBdTKeRPOQ==}
|
||||
@ -5369,8 +5366,8 @@ packages:
|
||||
tinyexec@0.2.0:
|
||||
resolution: {integrity: sha512-au8dwv4xKSDR+Fw52csDo3wcDztPdne2oM1o/7LFro4h6bdFmvyUAeAfX40pwDtzHgRFqz1XWaUqgKS2G83/ig==}
|
||||
|
||||
tm-grammars@1.17.4:
|
||||
resolution: {integrity: sha512-FuZ+f/pwYcKy4Ci56r75d4mRLBsdLUJunIgF+1u5Oy0co9laQqZFH1rss+F6/vdx1vJljPubZj9clKxrwTKg0A==}
|
||||
tm-grammars@1.17.8:
|
||||
resolution: {integrity: sha512-Qw67JNutL9LCt8FFw5RfsogeQ40iSeqrTHDSp0ecnY/b+ZweK8izlw6y/ZMje2+I6DMtTiBOCgmXEf+2oH11jQ==}
|
||||
|
||||
tm-themes@1.8.1:
|
||||
resolution: {integrity: sha512-jTUfDRn5TysYhkxxEWBQDo1C1n4yoHcnfNNqXkVxIMGQCgal/9poGuMBsfbnZCPEmFVcN2rtrUwaOJ8s2hVQXg==}
|
||||
@ -5907,9 +5904,8 @@ snapshots:
|
||||
|
||||
'@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)':
|
||||
dependencies:
|
||||
algoliasearch: 4.20.0
|
||||
optionalDependencies:
|
||||
'@algolia/client-search': 4.20.0
|
||||
algoliasearch: 4.20.0
|
||||
|
||||
'@algolia/cache-browser-local-storage@4.20.0':
|
||||
dependencies:
|
||||
@ -11462,7 +11458,7 @@ snapshots:
|
||||
|
||||
tinyexec@0.2.0: {}
|
||||
|
||||
tm-grammars@1.17.4: {}
|
||||
tm-grammars@1.17.8: {}
|
||||
|
||||
tm-themes@1.8.1: {}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import VPShortPostList from '@theme/Blog/VPShortPostList.vue'
|
||||
import { useArchives, useBlogNavTitle } from '../../composables/index.js'
|
||||
import { useArchives, useInternalLink } from '../../composables/index.js'
|
||||
|
||||
const title = useBlogNavTitle('archive')
|
||||
const { archive: archiveLink } = useInternalLink()
|
||||
const { archives } = useArchives()
|
||||
</script>
|
||||
|
||||
@ -12,7 +12,7 @@ const { archives } = useArchives()
|
||||
|
||||
<h2 class="archives-title">
|
||||
<span class="vpi-archive icon" />
|
||||
<span>{{ title }}</span>
|
||||
<span>{{ archiveLink.text }}</span>
|
||||
</h2>
|
||||
<div v-if="archives.length" class="archives">
|
||||
<template v-for="archive in archives" :key="archive.label">
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import VPCategories from '@theme/Blog/VPCategories.vue'
|
||||
import { useBlogCategory, useBlogNavTitle } from '../../composables/index.js'
|
||||
import { useBlogCategory, useInternalLink } from '../../composables/index.js'
|
||||
|
||||
const title = useBlogNavTitle('category')
|
||||
const { categories: categoriesLink } = useInternalLink()
|
||||
const { categories } = useBlogCategory()
|
||||
</script>
|
||||
|
||||
@ -12,7 +12,7 @@ const { categories } = useBlogCategory()
|
||||
|
||||
<h2 class="categories-title">
|
||||
<span class="vpi-category icon" />
|
||||
<span>{{ title }}</span>
|
||||
<span>{{ categoriesLink.text }}</span>
|
||||
</h2>
|
||||
|
||||
<slot name="blog-categories-content-before" />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import VPShortPostList from '@theme/Blog/VPShortPostList.vue'
|
||||
import { useBlogNavTitle, useTags } from '../../composables/index.js'
|
||||
import { useInternalLink, useTags } from '../../composables/index.js'
|
||||
|
||||
const { tags: tagsLink } = useInternalLink()
|
||||
const { tags, currentTag, postList, handleTagClick } = useTags()
|
||||
const title = useBlogNavTitle('tag')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -13,7 +13,7 @@ const title = useBlogNavTitle('tag')
|
||||
<div class="tags-nav">
|
||||
<h2 class="tags-title">
|
||||
<span class="vpi-tag icon" />
|
||||
<span>{{ title }}</span>
|
||||
<span>{{ tagsLink.text }}</span>
|
||||
</h2>
|
||||
<slot name="blog-tags-title-after" />
|
||||
<div class="tags">
|
||||
|
||||
@ -2,13 +2,14 @@
|
||||
import { computed } from 'vue'
|
||||
import VPLink from '@theme/VPLink.vue'
|
||||
import type { PlumeThemeBlogPostItem } from '../../../shared/index.js'
|
||||
import { useTagColors } from '../../composables/index.js'
|
||||
import { useInternalLink, useTagColors } from '../../composables/index.js'
|
||||
|
||||
const props = defineProps<{
|
||||
post: PlumeThemeBlogPostItem
|
||||
}>()
|
||||
|
||||
const colors = useTagColors()
|
||||
const { categories: categoriesLink, tags: tagsLink } = useInternalLink()
|
||||
|
||||
const sticky = computed(() => {
|
||||
if (typeof props.post.sticky === 'boolean') {
|
||||
@ -53,19 +54,22 @@ const createTime = computed(() =>
|
||||
<div v-if="categoryList.length" class="category-list">
|
||||
<span class="icon vpi-folder" />
|
||||
<template v-for="(cate, i) in categoryList" :key="i">
|
||||
<span>{{ cate.name }}</span>
|
||||
<VPLink :href="`${categoriesLink.link}?id=${cate.id}`">
|
||||
{{ cate.name }}
|
||||
</VPLink>
|
||||
<span v-if="i !== categoryList.length - 1">/</span>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="tags.length" class="tag-list">
|
||||
<span class="icon vpi-tag" />
|
||||
<template v-for="tag in tags" :key="tag.name">
|
||||
<span
|
||||
<VPLink
|
||||
class="tag"
|
||||
:class="tag.className"
|
||||
:href="`${tagsLink.link}?tag=${tag.name}`"
|
||||
>
|
||||
{{ tag.name }}
|
||||
</span>
|
||||
</VPLink>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="createTime" class="create-time">
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRouteLocale } from 'vuepress/client'
|
||||
import VPLink from '@theme/VPLink.vue'
|
||||
import {
|
||||
useBlogExtract,
|
||||
useBlogNavTitle,
|
||||
useBlogPageData,
|
||||
useData,
|
||||
useInternalLink,
|
||||
useSidebarData,
|
||||
} from '../composables/index.js'
|
||||
import type { ResolvedSidebarItem } from '../../shared/index.js'
|
||||
@ -18,9 +16,8 @@ interface Breadcrumb {
|
||||
}
|
||||
|
||||
const { page } = useData<'post'>()
|
||||
const routeLocale = useRouteLocale()
|
||||
const { isBlogPost } = useBlogPageData()
|
||||
const { categoriesLink, blogLink } = useBlogExtract()
|
||||
const { home, blog, categories } = useInternalLink()
|
||||
const sidebar = useSidebarData()
|
||||
|
||||
const hasBreadcrumb = computed(() => {
|
||||
@ -28,21 +25,19 @@ const hasBreadcrumb = computed(() => {
|
||||
return page.value.categoryList.length > 0
|
||||
return sidebar.value.length > 0
|
||||
})
|
||||
const homeTitle = useBlogNavTitle('home')
|
||||
const blogTile = useBlogNavTitle('blog')
|
||||
|
||||
const breadcrumbList = computed<Breadcrumb[]>(() => {
|
||||
if (!hasBreadcrumb.value)
|
||||
return []
|
||||
const list: Breadcrumb[] = [{ text: homeTitle.value, link: routeLocale.value }]
|
||||
const list: Breadcrumb[] = [{ text: home.value.text, link: home.value.link }]
|
||||
|
||||
if (isBlogPost.value) {
|
||||
list.push({ text: blogTile.value, link: blogLink.value })
|
||||
list.push({ text: blog.value.text, link: blog.value.link })
|
||||
const categoryList = page.value.categoryList ?? []
|
||||
for (const category of categoryList) {
|
||||
list.push({
|
||||
text: category.name,
|
||||
link: `${categoriesLink.value}?id=${category.id}`,
|
||||
link: `${categories.value.link}?id=${category.id}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -90,7 +85,7 @@ function resolveSidebar(
|
||||
{{ text }}
|
||||
</VPLink>
|
||||
<span v-if="index !== breadcrumbList.length - 1" class="vpi-chevron-right" />
|
||||
<meta property="position" :content="index + 1">
|
||||
<meta property="position" :content="`${index + 1}`">
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
@ -98,19 +93,19 @@ function resolveSidebar(
|
||||
|
||||
<style scoped>
|
||||
.vp-breadcrumb {
|
||||
padding-left: 1rem;
|
||||
padding-left: 8px;
|
||||
margin-bottom: 2rem;
|
||||
border-left: solid 4px var(--vp-c-brand-1);
|
||||
border-left: solid 2px var(--vp-c-brand-1);
|
||||
transition: border-left var(--t-color);
|
||||
}
|
||||
|
||||
.vp-breadcrumb ol {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@ -120,6 +115,7 @@ function resolveSidebar(
|
||||
}
|
||||
|
||||
.vp-breadcrumb .breadcrumb {
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-brand-2);
|
||||
transition: color var(--t-color);
|
||||
}
|
||||
@ -133,7 +129,8 @@ function resolveSidebar(
|
||||
}
|
||||
|
||||
.vp-breadcrumb .vpi-chevron-right {
|
||||
margin-left: 8px;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-left: 4px;
|
||||
color: var(--vp-c-border);
|
||||
transition: color var(--t-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import VPLink from '@theme/VPLink.vue'
|
||||
import { useReadingTimeLocale } from '@vuepress/plugin-reading-time/client'
|
||||
import { useData, useTagColors } from '../composables/index.js'
|
||||
import { useData, useInternalLink, useTagColors } from '../composables/index.js'
|
||||
|
||||
const { page, frontmatter: matter } = useData<'post'>()
|
||||
const colors = useTagColors()
|
||||
const readingTime = useReadingTimeLocale()
|
||||
const { tags: tagsLink } = useInternalLink()
|
||||
|
||||
const createTime = computed(() => {
|
||||
if (matter.value.createTime)
|
||||
@ -40,14 +42,15 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
||||
</p>
|
||||
<p v-if="tags.length > 0">
|
||||
<span class="vpi-tag icon" />
|
||||
<span
|
||||
<VPLink
|
||||
v-for="tag in tags"
|
||||
:key="tag.name"
|
||||
class="tag"
|
||||
:class="tag.className"
|
||||
:href="`${tagsLink.link}?tag=${tag.name}`"
|
||||
>
|
||||
{{ tag.name }}
|
||||
</span>
|
||||
</VPLink>
|
||||
</p>
|
||||
<p v-if="createTime" class="create-time">
|
||||
<span class="vpi-clock icon" /><span>{{ createTime }}</span>
|
||||
|
||||
@ -52,11 +52,12 @@ defineProps<{
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.vp-link-card .body > :last-child {
|
||||
margin-bottom: 0;
|
||||
.vp-link-card .body > * {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.vp-link-card .link {
|
||||
|
||||
@ -1,64 +1,44 @@
|
||||
import { useRouteLocale } from 'vuepress/client'
|
||||
import { computed } from 'vue'
|
||||
import type { PresetLocale } from '../../shared/index.js'
|
||||
import { useLocalePostList } from './blog-data.js'
|
||||
import { useTags } from './blog-tags.js'
|
||||
import { type BlogCategory, useBlogCategory } from './blog-category.js'
|
||||
import { useData } from './data.js'
|
||||
import { useLocaleLink } from './locale.js'
|
||||
|
||||
declare const __PLUME_PRESET_LOCALE__: Record<string, PresetLocale>
|
||||
|
||||
const presetLocales = __PLUME_PRESET_LOCALE__
|
||||
|
||||
export function useBlogNavTitle(name: keyof PresetLocale) {
|
||||
const locale = useRouteLocale()
|
||||
|
||||
return computed(() => presetLocales[locale.value]?.[name] || presetLocales['/'][name])
|
||||
}
|
||||
import { useInternalLink } from './internal-link.js'
|
||||
|
||||
export function useBlogExtract() {
|
||||
const { theme } = useData()
|
||||
const locale = useRouteLocale()
|
||||
const postList = useLocalePostList()
|
||||
const { tags: tagsList } = useTags()
|
||||
const { categories: categoryList } = useBlogCategory()
|
||||
const blog = computed(() => theme.value.blog || {})
|
||||
const links = useInternalLink()
|
||||
|
||||
const hasBlogExtract = computed(() =>
|
||||
blog.value.archives !== false
|
||||
|| blog.value.tags !== false
|
||||
|| blog.value.categories !== false,
|
||||
)
|
||||
const blogLink = useLocaleLink(blog.value.link || 'blog/')
|
||||
const tagsLink = useLocaleLink(blog.value.tagsLink || 'blog/tags/')
|
||||
const archiveLink = useLocaleLink(blog.value.archivesLink || 'blog/archives/')
|
||||
const categoriesLink = useLocaleLink(blog.value.categoriesLink || 'blog/categories/')
|
||||
|
||||
const tags = computed(() => ({
|
||||
link: tagsLink.value,
|
||||
text: presetLocales[locale.value]?.tag || presetLocales['/'].tag,
|
||||
link: links.tags.value.link,
|
||||
text: links.tags.value.text,
|
||||
total: tagsList.value.length,
|
||||
}))
|
||||
|
||||
const archives = computed(() => ({
|
||||
link: archiveLink.value,
|
||||
text: presetLocales[locale.value]?.archive || presetLocales['/'].archive,
|
||||
link: links.archive.value.link,
|
||||
text: links.archive.value.text,
|
||||
total: postList.value.length,
|
||||
}))
|
||||
|
||||
const categories = computed(() => ({
|
||||
link: categoriesLink.value,
|
||||
text: presetLocales[locale.value]?.category || presetLocales['/'].category,
|
||||
link: links.categories.value.link,
|
||||
text: links.categories.value.text,
|
||||
total: getCategoriesTotal(categoryList.value),
|
||||
}))
|
||||
|
||||
return {
|
||||
hasBlogExtract,
|
||||
blogLink,
|
||||
tagsLink,
|
||||
archiveLink,
|
||||
categoriesLink,
|
||||
tags,
|
||||
archives,
|
||||
categories,
|
||||
|
||||
@ -20,6 +20,7 @@ export * from './contributors.js'
|
||||
|
||||
export * from './home.js'
|
||||
|
||||
export * from './internal-link.js'
|
||||
export * from './blog-data.js'
|
||||
export * from './blog-post-list.js'
|
||||
export * from './blog-extract.js'
|
||||
@ -32,8 +33,8 @@ export * from './page.js'
|
||||
export * from './encrypt-data.js'
|
||||
export * from './encrypt.js'
|
||||
|
||||
export * from './preset-locales.js'
|
||||
export * from './link.js'
|
||||
export * from './locale.js'
|
||||
export * from './route-query.js'
|
||||
|
||||
export * from './watermark.js'
|
||||
|
||||
38
theme/src/client/composables/internal-link.ts
Normal file
38
theme/src/client/composables/internal-link.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRouteLocale } from 'vuepress/client'
|
||||
import type { PresetLocale } from '../../shared/index.js'
|
||||
import { useData } from './data.js'
|
||||
import { getPresetLocaleData } from './preset-locales.js'
|
||||
|
||||
export interface InternalLink {
|
||||
text: string
|
||||
link: string
|
||||
}
|
||||
|
||||
export function useInternalLink() {
|
||||
const { theme } = useData()
|
||||
const routeLocale = useRouteLocale()
|
||||
|
||||
function resolveLink(name: keyof PresetLocale, link: string): InternalLink {
|
||||
return {
|
||||
link: (routeLocale.value + link).replace(/\/+/g, '/'),
|
||||
text: getPresetLocaleData(routeLocale.value, name),
|
||||
}
|
||||
}
|
||||
|
||||
const blogData = computed(() => theme.value.blog || {})
|
||||
|
||||
const home = computed(() => resolveLink('home', '/'))
|
||||
const blog = computed(() => resolveLink('blog', blogData.value.link || 'blog/'))
|
||||
const tags = computed(() => resolveLink('tag', blogData.value.tagsLink || 'blog/tags/'))
|
||||
const archive = computed(() => resolveLink('archive', blogData.value.archivesLink || 'blog/archives/'))
|
||||
const categories = computed(() => resolveLink('category', blogData.value.categoriesLink || 'blog/categories/'))
|
||||
|
||||
return {
|
||||
home,
|
||||
blog,
|
||||
tags,
|
||||
archive,
|
||||
categories,
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { useRouteLocale } from 'vuepress/client'
|
||||
import { computed } from 'vue'
|
||||
|
||||
export function useLocaleLink(link: string) {
|
||||
const prefix = useRouteLocale()
|
||||
|
||||
return computed(() => {
|
||||
return (prefix.value + link).replace(/\/+/g, '/')
|
||||
})
|
||||
}
|
||||
9
theme/src/client/composables/preset-locales.ts
Normal file
9
theme/src/client/composables/preset-locales.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { PresetLocale } from '../../shared/index.js'
|
||||
|
||||
declare const __PLUME_PRESET_LOCALE__: Record<string, PresetLocale>
|
||||
|
||||
export const presetLocales = __PLUME_PRESET_LOCALE__
|
||||
|
||||
export function getPresetLocaleData(locale: string, name: keyof PresetLocale) {
|
||||
return presetLocales[locale]?.[name] || presetLocales['/'][name]
|
||||
}
|
||||
@ -95,6 +95,7 @@ export function getSidebar(routePath: string, routeLocal: string): ResolvedSideb
|
||||
return resolveSidebarItems(_sidebar, routeLocal)
|
||||
}
|
||||
else if (isPlainObject(_sidebar)) {
|
||||
routePath = decodeURIComponent(routePath)
|
||||
const dir
|
||||
= Object.keys(_sidebar)
|
||||
.sort((a, b) => b.split('/').length - a.split('/').length)
|
||||
|
||||
@ -311,6 +311,12 @@ html:not(.dark) .vp-code span {
|
||||
transform: translateX(calc(-100% - 1px));
|
||||
}
|
||||
|
||||
@media (max-width: 419px) {
|
||||
.vp-doc div[class*="language-"] > button.copy {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Collapsed lines
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
@ -13,6 +13,6 @@
|
||||
}
|
||||
|
||||
/* ------------------ Plugin Comment ------------------ */
|
||||
#vp-comment {
|
||||
.vp-comment {
|
||||
margin-top: 80px;
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
|
||||
@media (max-width: 419px) {
|
||||
.vp-doc .hint-container div[class*="language-"] {
|
||||
margin: 0.85rem -0.75rem 0.85rem -1rem;
|
||||
margin: 0.85rem -0.75rem;
|
||||
}
|
||||
|
||||
.vp-doc .hint-container .vp-code-tabs-nav {
|
||||
@ -247,9 +247,10 @@
|
||||
}
|
||||
|
||||
.vp-doc .vp-code-demo .vp-code-demo-code-wrapper {
|
||||
margin-bottom: -0.9rem;
|
||||
margin-bottom: -13px;
|
||||
}
|
||||
|
||||
|
||||
.vp-doc .vp-code-demo .vp-code-demo-toggle-button {
|
||||
margin: 0 12px 0 8px;
|
||||
background-color: var(--vp-c-gray-2);
|
||||
@ -275,7 +276,10 @@
|
||||
}
|
||||
|
||||
.vp-doc .vp-code-demo .vp-code-demo-codes div[class*="language-"] {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 2px dashed var(--vp-c-divider);
|
||||
border-radius: 0;
|
||||
transition: border-bottom var(--t-color);
|
||||
}
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import type {
|
||||
AutoFrontmatterObject,
|
||||
PlumeThemeLocaleOptions,
|
||||
} from '../../shared/index.js'
|
||||
import { getThemeConfig } from '../loadConfig/index.js'
|
||||
import { readMarkdown, readMarkdownList } from './readFile.js'
|
||||
import { resolveOptions } from './resolveOptions.js'
|
||||
|
||||
@ -26,8 +27,6 @@ export interface Generate {
|
||||
}
|
||||
|
||||
let generate: Generate | null = null
|
||||
let generated = false
|
||||
const whenGenerated: (() => void)[] = []
|
||||
|
||||
export function initAutoFrontmatter(
|
||||
localeOptions: PlumeThemeLocaleOptions,
|
||||
@ -64,19 +63,14 @@ export function initAutoFrontmatter(
|
||||
export async function generateAutoFrontmatter(app: App) {
|
||||
if (!generate)
|
||||
return
|
||||
generated = false
|
||||
const markdownList = await readMarkdownList(app.dir.source(), generate.globFilter)
|
||||
await promiseParallel(
|
||||
markdownList.map(file => () => generator(file)),
|
||||
64,
|
||||
)
|
||||
|
||||
generated = true
|
||||
whenGenerated.forEach(resolve => resolve())
|
||||
whenGenerated.length = 0
|
||||
}
|
||||
|
||||
export async function watchAutoFrontmatter(app: App, watchers: any[], enable?: () => boolean) {
|
||||
export async function watchAutoFrontmatter(app: App, watchers: any[]) {
|
||||
if (!generate)
|
||||
return
|
||||
|
||||
@ -87,7 +81,7 @@ export async function watchAutoFrontmatter(app: App, watchers: any[], enable?: (
|
||||
})
|
||||
|
||||
watcher.on('add', async (relativePath) => {
|
||||
const enabled = enable ? enable() : true
|
||||
const enabled = getThemeConfig().autoFrontmatter !== false
|
||||
if (!generate!.globFilter(relativePath) || !enabled)
|
||||
return
|
||||
const file = await readMarkdown(app.dir.source(), relativePath)
|
||||
@ -132,12 +126,3 @@ async function generator(file: AutoFrontmatterMarkdownFile): Promise<void> {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
export function waitForAutoFrontmatter() {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (generate && !generated)
|
||||
whenGenerated.push(resolve)
|
||||
else
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
||||
@ -65,13 +65,12 @@ export async function initConfigLoader(
|
||||
loader.loaded = true
|
||||
loader.dependencies = [...dependencies]
|
||||
updateResolvedConfig(app, config)
|
||||
runChangeEvents()
|
||||
|
||||
loader.whenLoaded.forEach(fn => fn(loader!.resolvedConfig))
|
||||
loader.whenLoaded = []
|
||||
}
|
||||
|
||||
export function watchConfigFile(app: App, watchers: any[]) {
|
||||
export function watchConfigFile(app: App, watchers: any[], onChange: ChangeEvent) {
|
||||
if (!loader || !loader.configFile)
|
||||
return
|
||||
|
||||
@ -82,6 +81,8 @@ export function watchConfigFile(app: App, watchers: any[]) {
|
||||
|
||||
addDependencies(watcher)
|
||||
|
||||
onConfigChange(onChange)
|
||||
|
||||
watcher.on('change', async () => {
|
||||
if (loader) {
|
||||
loader.loaded = false
|
||||
@ -121,7 +122,7 @@ export function waitForConfigLoaded() {
|
||||
})
|
||||
}
|
||||
|
||||
export function getResolvedThemeConfig() {
|
||||
export function getThemeConfig() {
|
||||
return loader!.resolvedConfig
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
resolveSearchOptions,
|
||||
} from '../config/index.js'
|
||||
import { customContainerPlugins } from './containerPlugins.js'
|
||||
import { markdownTitlePlugin } from './markdown-title.js'
|
||||
|
||||
export interface SetupPluginOptions {
|
||||
app: App
|
||||
@ -39,7 +40,7 @@ export function getPlugins({
|
||||
const isProd = !app.env.isDev
|
||||
|
||||
const plugins: PluginConfig = [
|
||||
|
||||
markdownTitlePlugin(),
|
||||
fontsPlugin(),
|
||||
contentUpdatePlugin(),
|
||||
activeHeaderLinksPlugin({
|
||||
|
||||
46
theme/src/node/plugins/markdown-title.ts
Normal file
46
theme/src/node/plugins/markdown-title.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import type { MarkdownEnv } from 'vuepress/markdown'
|
||||
|
||||
const REG_HEADING = /^#\s*?(\S.*)?\n/
|
||||
|
||||
export function markdownTitlePlugin(): Plugin {
|
||||
return {
|
||||
name: '@vuepress-plume/plugin-markdown-title',
|
||||
|
||||
extendsMarkdown(md) {
|
||||
const render = md.render
|
||||
md.render = (source, env: MarkdownEnv) => {
|
||||
if (!env.filePathRelative)
|
||||
return render(source, env)
|
||||
|
||||
let { matter, content } = parseSource(source.trim())
|
||||
let title = ''
|
||||
content = content.trim().replace(REG_HEADING, (_, match) => {
|
||||
title = match.trim()
|
||||
return ''
|
||||
})
|
||||
source = `${matter}\n${content}`
|
||||
const result = render(source, env)
|
||||
if (title)
|
||||
env.title = title
|
||||
return result
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function parseSource(source: string) {
|
||||
const char = '---'
|
||||
|
||||
if (!source.startsWith(char)) {
|
||||
return { matter: '', content: source }
|
||||
}
|
||||
else {
|
||||
const end = source.indexOf(`\n${char}`)
|
||||
const len = char.length + 1
|
||||
return {
|
||||
matter: source.slice(0, end + len),
|
||||
content: source.slice(end + len),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import type { App } from 'vuepress'
|
||||
import { watch } from 'chokidar'
|
||||
import { getResolvedThemeConfig } from '../loadConfig/index.js'
|
||||
import { getThemeConfig } from '../loadConfig/index.js'
|
||||
import { prepareArticleTagColors } from './prepareArticleTagColor.js'
|
||||
import { preparedBlogData } from './prepareBlogData.js'
|
||||
import { prepareEncrypt } from './prepareEncrypt.js'
|
||||
@ -10,7 +10,7 @@ import { prepareIcons } from './prepareIcons.js'
|
||||
export async function prepareData(
|
||||
app: App,
|
||||
): Promise<void> {
|
||||
const { localeOptions, encrypt } = getResolvedThemeConfig()
|
||||
const { localeOptions, encrypt } = getThemeConfig()
|
||||
await Promise.all([
|
||||
prepareArticleTagColors(app),
|
||||
preparedBlogData(app, localeOptions, encrypt),
|
||||
|
||||
@ -93,8 +93,8 @@ function getAutoDirSidebar(
|
||||
|
||||
while (nowIndex < maxIndex) {
|
||||
pages = pages.sort((prev, next) => {
|
||||
const pi = prev.splitPath?.[nowIndex]?.match(/(\d+)\.(?=[^/]+$)/)?.[1]
|
||||
const ni = next.splitPath?.[nowIndex]?.match(/(\d+)\.(?=[^/]+$)/)?.[1]
|
||||
const pi = prev.splitPath?.[nowIndex]?.match(/(?:(\d+)\.)?(?=[^/]+$)/)?.[1]
|
||||
const ni = next.splitPath?.[nowIndex]?.match(/(?:(\d+)\.)?(?=[^/]+$)/)?.[1]
|
||||
if (!pi || !ni)
|
||||
return 0
|
||||
return Number.parseFloat(pi) < Number.parseFloat(ni) ? -1 : 1
|
||||
|
||||
@ -4,58 +4,24 @@ import type { PlumeThemeOptions, PlumeThemePageData } from '../shared/index.js'
|
||||
import { getPlugins } from './plugins/index.js'
|
||||
import { extendsPageData, setupPage } from './setupPages.js'
|
||||
import { THEME_NAME, resolve, templates } from './utils/index.js'
|
||||
import {
|
||||
extendsBundlerOptions,
|
||||
resolveAlias,
|
||||
resolvePageHead,
|
||||
resolveProvideData,
|
||||
resolveThemeOptions,
|
||||
templateBuildRenderer,
|
||||
} from './config/index.js'
|
||||
import {
|
||||
getResolvedThemeConfig,
|
||||
initConfigLoader,
|
||||
onConfigChange,
|
||||
waitForConfigLoaded,
|
||||
watchConfigFile,
|
||||
} from './loadConfig/index.js'
|
||||
import {
|
||||
generateAutoFrontmatter,
|
||||
initAutoFrontmatter,
|
||||
waitForAutoFrontmatter,
|
||||
watchAutoFrontmatter,
|
||||
} from './autoFrontmatter/index.js'
|
||||
import { extendsBundlerOptions, resolveAlias, resolvePageHead, resolveProvideData, resolveThemeOptions, templateBuildRenderer } from './config/index.js'
|
||||
import { getThemeConfig, initConfigLoader, waitForConfigLoaded, watchConfigFile } from './loadConfig/index.js'
|
||||
import { generateAutoFrontmatter, initAutoFrontmatter, watchAutoFrontmatter } from './autoFrontmatter/index.js'
|
||||
import { prepareData, watchPrepare } from './prepare/index.js'
|
||||
import { prepareThemeData } from './prepare/prepareThemeData.js'
|
||||
|
||||
export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
||||
const {
|
||||
localeOptions,
|
||||
pluginOptions,
|
||||
hostname,
|
||||
configFile,
|
||||
cache,
|
||||
} = resolveThemeOptions(options)
|
||||
const { localeOptions, pluginOptions, hostname, configFile, cache } = resolveThemeOptions(options)
|
||||
|
||||
return (app) => {
|
||||
initConfigLoader(app, localeOptions, {
|
||||
configFile,
|
||||
onChange: ({ localeOptions, autoFrontmatter }) => {
|
||||
autoFrontmatter ??= pluginOptions.frontmatter
|
||||
if (autoFrontmatter !== false) {
|
||||
if (autoFrontmatter !== false)
|
||||
initAutoFrontmatter(localeOptions, autoFrontmatter)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
waitForConfigLoaded().then(async ({ autoFrontmatter }) => {
|
||||
autoFrontmatter ??= pluginOptions.frontmatter
|
||||
if (autoFrontmatter !== false) {
|
||||
await sleep(100)
|
||||
generateAutoFrontmatter(app)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
name: THEME_NAME,
|
||||
|
||||
@ -69,42 +35,46 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
||||
|
||||
plugins: getPlugins({ app, pluginOptions, hostname, cache }),
|
||||
|
||||
extendsBundlerOptions,
|
||||
|
||||
templateBuildRenderer,
|
||||
|
||||
extendsMarkdown: async (_, app) => {
|
||||
const { autoFrontmatter, localeOptions } = await waitForConfigLoaded()
|
||||
if (autoFrontmatter !== false) {
|
||||
initAutoFrontmatter(localeOptions, autoFrontmatter)
|
||||
await generateAutoFrontmatter(app)
|
||||
// wait for autoFrontmatter generated
|
||||
// i/o performance
|
||||
await sleep(100)
|
||||
}
|
||||
},
|
||||
|
||||
extendsPage: async (page) => {
|
||||
const { localeOptions } = getThemeConfig()
|
||||
extendsPageData(page as Page<PlumeThemePageData>, localeOptions)
|
||||
resolvePageHead(page, localeOptions)
|
||||
},
|
||||
|
||||
onInitialized: async (app) => {
|
||||
const { localeOptions } = await waitForConfigLoaded()
|
||||
const { localeOptions } = getThemeConfig()
|
||||
await setupPage(app, localeOptions)
|
||||
},
|
||||
|
||||
onPrepared: async (app) => {
|
||||
onConfigChange(async ({ localeOptions }) => {
|
||||
await prepareThemeData(app, localeOptions)
|
||||
await prepareData(app)
|
||||
})
|
||||
const { localeOptions } = await waitForConfigLoaded()
|
||||
const { localeOptions } = getThemeConfig()
|
||||
await prepareThemeData(app, localeOptions)
|
||||
await prepareData(app)
|
||||
},
|
||||
|
||||
onWatched: (app, watchers) => {
|
||||
watchConfigFile(app, watchers)
|
||||
watchPrepare(app, watchers)
|
||||
watchAutoFrontmatter(app, watchers, () => {
|
||||
const autoFrontmatter = getResolvedThemeConfig().autoFrontmatter ?? pluginOptions.frontmatter
|
||||
return autoFrontmatter !== false
|
||||
watchConfigFile(app, watchers, async ({ localeOptions }) => {
|
||||
await prepareThemeData(app, localeOptions)
|
||||
await prepareData(app)
|
||||
})
|
||||
watchAutoFrontmatter(app, watchers)
|
||||
watchPrepare(app, watchers)
|
||||
},
|
||||
|
||||
extendsPage: async (page) => {
|
||||
const { localeOptions, autoFrontmatter } = await waitForConfigLoaded()
|
||||
if ((autoFrontmatter ?? pluginOptions.frontmatter) !== false) {
|
||||
await waitForAutoFrontmatter()
|
||||
}
|
||||
extendsPageData(page as Page<PlumeThemePageData>, localeOptions)
|
||||
resolvePageHead(page, localeOptions)
|
||||
},
|
||||
|
||||
extendsBundlerOptions,
|
||||
|
||||
templateBuildRenderer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import type { MarkdownEnhancePluginOptions } from 'vuepress-plugin-md-enhance'
|
||||
import type { ReadingTimePluginOptions } from '@vuepress/plugin-reading-time'
|
||||
import type { MarkdownPowerPluginOptions } from 'vuepress-plugin-md-power'
|
||||
import type { WatermarkPluginOptions } from '@vuepress/plugin-watermark'
|
||||
import type { AutoFrontmatter } from '../auto-frontmatter.js'
|
||||
|
||||
export interface PlumeThemePluginOptions {
|
||||
/**
|
||||
@ -62,11 +61,6 @@ export interface PlumeThemePluginOptions {
|
||||
*/
|
||||
baiduTongji?: false | { key: string }
|
||||
|
||||
/**
|
||||
* @deprecated 使用 `autoFrontmatter` 代替
|
||||
*/
|
||||
frontmatter?: Omit<AutoFrontmatter, 'frontmatter'>
|
||||
|
||||
/**
|
||||
* 阅读时间、字数统计
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user