Merge pull request #102 from pengzhanbo/RC-70

RC-70
This commit is contained in:
pengzhanbo 2024-06-24 00:22:32 +08:00 committed by GitHub
commit f90599c788
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 519 additions and 407 deletions

View File

@ -19,7 +19,7 @@
"echarts": "^5.5.0",
"flowchart.ts": "^3.0.0",
"mermaid": "^10.9.1",
"vue": "^3.4.29",
"vue": "^3.4.30",
"vuepress-theme-plume": "workspace:~"
},
"devDependencies": {

View File

@ -2,7 +2,7 @@ import config from '@pengzhanbo/eslint-config-vue'
export default config({
// todo: 正则校验
// 当前项目中的 正则 海冰不能完全通过 规则,存在 53 个问题
// 当前项目中的 正则 还并不能完全通过 规则,存在 53 个问题
// 但处理起来比较麻烦,因此将会作为一项比较长期的工作来完成。
regexp: false,
ignores: [

View File

@ -43,7 +43,7 @@
"@vue/devtools-api": "6.6.1",
"chokidar": "^3.6.0",
"create-filter": "^1.0.1",
"vue": "^3.4.29"
"vue": "^3.4.30"
},
"publishConfig": {
"access": "public"

View File

@ -40,7 +40,7 @@
"vuepress": "2.0.0-rc.14"
},
"dependencies": {
"vue": "^3.4.29"
"vue": "^3.4.30"
},
"publishConfig": {
"access": "public"

View File

@ -42,7 +42,7 @@
},
"dependencies": {
"@vuepress-plume/plugin-content-update": "workspace:~",
"vue": "^3.4.29"
"vue": "^3.4.30"
},
"publishConfig": {
"access": "public"

View File

@ -41,7 +41,7 @@
},
"dependencies": {
"@iconify/vue": "^4.1.2",
"vue": "^3.4.29"
"vue": "^3.4.30"
},
"publishConfig": {
"access": "public"

View File

@ -52,10 +52,10 @@
"local-pkg": "^0.5.0",
"markdown-it-container": "^4.0.0",
"nanoid": "^5.0.7",
"shiki": "^1.8.0",
"tm-grammars": "^1.12.10",
"shiki": "^1.9.0",
"tm-grammars": "^1.12.11",
"tm-themes": "^1.4.3",
"vue": "^3.4.29"
"vue": "^3.4.30"
},
"devDependencies": {
"@iconify/json": "^2.2.221",

View File

@ -51,12 +51,12 @@
"cpx2": "^7.0.1",
"dotenv": "^16.4.5",
"esbuild": "^0.21.5",
"execa": "^9.2.0",
"execa": "^9.3.0",
"netlify-cli": "^17.29.0",
"portfinder": "^1.0.32"
},
"devDependencies": {
"@types/node": "^20.14.7"
"@types/node": "^20.14.8"
},
"publishConfig": {
"access": "public"

View File

@ -43,7 +43,7 @@
"@vue/devtools-api": "6.6.1",
"chokidar": "^3.6.0",
"create-filter": "^1.0.1",
"vue": "^3.4.29"
"vue": "^3.4.30"
},
"publishConfig": {
"access": "public"

View File

@ -36,7 +36,7 @@
"dependencies": {
"@netlify/functions": "^2.8.0",
"leancloud-storage": "^4.15.2",
"vue": "^3.4.29",
"vue": "^3.4.30",
"vue-router": "4.3.2",
"vuepress-plugin-netlify-functions": "workspace:~"
},

View File

@ -48,7 +48,7 @@
"mark.js": "^8.11.1",
"minisearch": "^6.3.0",
"p-map": "^7.0.2",
"vue": "^3.4.29"
"vue": "^3.4.30"
},
"publishConfig": {
"access": "public"

View File

@ -36,8 +36,8 @@
"vuepress": "2.0.0-rc.14"
},
"dependencies": {
"@shikijs/transformers": "^1.8.0",
"@shikijs/twoslash": "^1.8.0",
"@shikijs/transformers": "^1.9.0",
"@shikijs/twoslash": "^1.9.0",
"@types/hast": "^3.0.4",
"@vuepress/helper": "2.0.0-rc.37",
"floating-vue": "^5.2.2",
@ -45,9 +45,9 @@
"mdast-util-gfm": "^3.0.0",
"mdast-util-to-hast": "^13.2.0",
"nanoid": "^5.0.7",
"shiki": "^1.8.0",
"twoslash": "^0.2.8",
"twoslash-vue": "^0.2.8"
"shiki": "^1.9.0",
"twoslash": "^0.2.9",
"twoslash-vue": "^0.2.9"
},
"publishConfig": {
"access": "public"

View File

@ -4,7 +4,7 @@ import type { ShikiTransformer } from 'shiki'
import {
addClassToHast,
bundledLanguages,
getHighlighter,
createHighlighter,
isPlainLang,
isSpecialLang,
} from 'shiki'
@ -41,7 +41,7 @@ export async function highlight(
languages = Object.keys(bundledLanguages),
} = options
const highlighter = await getHighlighter({
const highlighter = await createHighlighter({
themes:
typeof theme === 'object' && 'light' in theme && 'dark' in theme
? [theme.light, theme.dark]

693
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -96,7 +96,7 @@
"katex": "^0.16.10",
"lodash.merge": "^4.6.2",
"nanoid": "^5.0.7",
"vue": "^3.4.29",
"vue": "^3.4.30",
"vue-router": "^4.4.0",
"vuepress-plugin-md-enhance": "2.0.0-rc.50",
"vuepress-plugin-md-power": "workspace:*"

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import { resolveRouteFullPath, useRouter, withBase } from 'vuepress/client'
import { isLinkExternal } from 'vuepress/shared'
import { computed, toRef } from 'vue'
import { useRouter, withBase } from 'vuepress/client'
import { useLink } from '../composables/link.js'
interface Props {
tag?: string
@ -20,24 +20,13 @@ const props = withDefaults(defineProps<Props>(), {
target: undefined,
rel: undefined,
})
const router = useRouter()
const isExternal = computed(
() => props.href && isLinkExternal(props.href),
)
const component = computed(() => {
return props.tag || props.href ? 'a' : 'button'
})
const link = computed(() => {
if (!props.href)
return undefined
if (isExternal.value)
return props.href
return resolveRouteFullPath(props.href)
})
const { link, isExternal } = useLink(toRef(props, 'href'), toRef(props, 'target'))
function linkTo(e: Event) {
if (!isExternal.value) {

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { computed } from 'vue'
import { resolveRouteFullPath, useRoute, useRouter, withBase } from 'vuepress/client'
import { isLinkExternal } from '@vuepress/helper/client'
import { computed, toRef } from 'vue'
import { useRouter, withBase } from 'vuepress/client'
import { useLink } from '../composables/link.js'
const props = defineProps<{
tag?: string
@ -12,19 +12,10 @@ const props = defineProps<{
}>()
const router = useRouter()
const route = useRoute()
const tag = computed(() => props.tag ?? (props.href ? 'a' : 'span'))
const isExternal = computed(
() => (props.href && isLinkExternal(props.href)) || props.target === '_blank',
)
const link = computed(() => {
if (!props.href)
return undefined
if (isExternal.value)
return props.href
return resolveRouteFullPath(props.href, route.path)
})
const { link, isExternal } = useLink(toRef(props, 'href'), toRef(props, 'target'))
function linkTo(e: Event) {
if (!isExternal.value) {

View File

@ -19,6 +19,7 @@ export * from './blog-tags.js'
export * from './blog-archives.js'
export * from './tag-colors.js'
export * from './link.js'
export * from './locale.js'
export * from './route-query.js'
export * from './watermark.js'

View File

@ -0,0 +1,36 @@
import { isLinkExternal } from '@vuepress/helper/client'
import { resolveRouteFullPath, useRoute } from 'vuepress/client'
import { type MaybeRefOrGetter, computed, toValue } from 'vue'
import { useData } from './data.js'
const SEARCH_RE = /\.md(?:(?:#|\?).*)?$/
export function useLink(
href: MaybeRefOrGetter<string | undefined>,
target?: MaybeRefOrGetter<string | undefined>,
) {
const route = useRoute()
const { page } = useData()
const isExternal = computed(
() => {
const link = toValue(href)
const rawTarget = toValue(target)
return (link && isLinkExternal(link)) || rawTarget === '_blank'
},
)
const link = computed(() => {
const link = toValue(href)
if (!link)
return undefined
if (isExternal.value)
return link
const currentPath = link.startsWith('./') && SEARCH_RE.test(link)
? `/${page.value.filePathRelative!}`
: route.path
return resolveRouteFullPath(link, currentPath)
})
return { isExternal, link }
}

View File

@ -0,0 +1,111 @@
/**
* 使 shiki + twoslash markdown
* markdown render
* markdown render content hash
*
*
* vuepress/ecosystem
* vuepress/core
*
*
* 使 13s 1.2s
* vuepress shiki 0.5s
*/
import { createHash } from 'node:crypto'
import type { App } from 'vuepress'
import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
import { fs } from 'vuepress/utils'
interface CacheContent {
content: string
env: MarkdownEnv
}
const cacheDir = 'markdown/render'
const metaFile = '_metadata.json'
export async function extendsMarkdown(md: Markdown, app: App): Promise<void> {
// 如果是在 构建阶段,且缓存文件夹不存在,则不进行缓存
// 因为构建阶段仅一次性产物,生成缓存资源反而会带来额外的开销
if (app.env.isBuild && !fs.existsSync(app.dir.cache())) {
return
}
await fs.ensureDir(app.dir.cache(cacheDir))
const metadata = await readMetadata(app)
const writeCache = (filepath: string, cache: CacheContent) => {
const cachePath = app.dir.cache(cacheDir, filepath)
const content = JSON.stringify(cache)
fs.writeFileSync(cachePath, content, 'utf-8')
}
const readCache = (filepath: string): CacheContent | null => {
const cachePath = app.dir.cache(cacheDir, filepath)
try {
const content = fs.readFileSync(cachePath, 'utf-8')
return JSON.parse(content) as CacheContent
}
catch {}
return null
}
const rawRender = md.render
md.render = (input, env: MarkdownEnv) => {
const filepath = env.filePathRelative
if (!filepath) {
return rawRender(input, env)
}
const hash = getContentHash(input)
const cachePath = normalizePath(filepath)
if (metadata[filepath] === hash) {
const cache = readCache(cachePath)
if (cache) {
Object.assign(env, cache.env)
return cache.content
}
}
metadata[filepath] = hash
const renderedContent = rawRender(input, env)
writeCache(cachePath, { content: renderedContent, env })
updateMetadata(app, metadata)
return renderedContent
}
}
async function readMetadata(app: App): Promise<Record<string, string>> {
const filepath = app.dir.cache(cacheDir, metaFile)
try {
const content = await fs.readFile(filepath, 'utf-8')
return JSON.parse(content)
}
catch {}
return {}
}
let timer: NodeJS.Timeout | null = null
function updateMetadata(app: App, metadata: Record<string, string>) {
const filepath = app.dir.cache(cacheDir, metaFile)
timer && clearTimeout(timer)
timer = setTimeout(
async () => await fs.writeFile(filepath, JSON.stringify(metadata), 'utf-8'),
200,
)
}
function normalizePath(filepath: string) {
return getContentHash(filepath)
}
function getContentHash(content: string): string {
const hash = createHash('md5')
hash.update(content)
return hash.digest('hex')
}

View File

@ -13,6 +13,7 @@ import {
templateBuildRenderer,
} from './config/index.js'
import { setupPrepare, watchPrepare } from './prepare/index.js'
import { extendsMarkdown } from './extendsMarkdown.js'
export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
const {
@ -55,6 +56,8 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
resolvePageHead(page, localeOptions)
},
extendsMarkdown,
extendsBundlerOptions,
templateBuildRenderer,