From 9f6368b7a4c677a353055357bb7722c7feeded78 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 21 Jul 2024 00:47:05 +0800 Subject: [PATCH 01/13] fix(plugin-md-power): fix incorrect code editor layout --- .../plugin-md-power/src/client/components/CodeEditor.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/plugin-md-power/src/client/components/CodeEditor.vue b/plugins/plugin-md-power/src/client/components/CodeEditor.vue index 10c24cce..5bec721c 100644 --- a/plugins/plugin-md-power/src/client/components/CodeEditor.vue +++ b/plugins/plugin-md-power/src/client/components/CodeEditor.vue @@ -106,12 +106,13 @@ onUnmounted(() => { z-index: 1; box-sizing: border-box; display: block; - padding: 1.3rem 1.5rem; + padding: 20px 24px; overflow-x: auto; font-family: var(--vp-font-family-mono); font-size: var(--vp-code-font-size); -webkit-hyphens: none; hyphens: none; + line-height: var(--vp-code-line-height); color: transparent; text-align: left; word-break: normal; @@ -139,7 +140,7 @@ onUnmounted(() => { } :deep(div[class*="language-"].line-numbers-mode) + .code-repl-input { - padding-left: 1rem; - margin-left: 2rem; + padding-left: 24px; + margin-left: 32px; } From 9f8de682fa9f26d4df83a1028d1e7bee557bea10 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 21 Jul 2024 00:47:56 +0800 Subject: [PATCH 02/13] perf(theme): improve media print layout --- theme/src/client/components/Nav/VPNavBar.vue | 6 ++++++ theme/src/client/styles/code.css | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/theme/src/client/components/Nav/VPNavBar.vue b/theme/src/client/components/Nav/VPNavBar.vue index 797d0156..1faf3b8c 100644 --- a/theme/src/client/components/Nav/VPNavBar.vue +++ b/theme/src/client/components/Nav/VPNavBar.vue @@ -286,4 +286,10 @@ watchPostEffect(() => { background-color: var(--vp-c-gutter); } } + +@media print { + .vp-navbar .hamburger { + display: none; + } +} diff --git a/theme/src/client/styles/code.css b/theme/src/client/styles/code.css index 187bac30..b66be19d 100644 --- a/theme/src/client/styles/code.css +++ b/theme/src/client/styles/code.css @@ -135,6 +135,16 @@ html:not(.dark) .vp-code span { counter-increment: line-number; } +@media print { + .vp-doc div[class*="language-"].line-numbers-mode { + padding-left: 0; + } + + .vp-doc div[class*="language-"].line-numbers-mode .line-numbers { + display: none; + } +} + @media (max-width: 639px) { .vp-doc li div[class*="language-"] { border-radius: 8px 0 0 8px; From 688c96452e39f179636ff486e3541652debddfaa Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 21 Jul 2024 00:50:11 +0800 Subject: [PATCH 03/13] perf(theme): improve navbar logo height --- theme/src/client/components/Nav/VPNavBarTitle.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/src/client/components/Nav/VPNavBarTitle.vue b/theme/src/client/components/Nav/VPNavBarTitle.vue index d9cab868..385dde97 100644 --- a/theme/src/client/components/Nav/VPNavBarTitle.vue +++ b/theme/src/client/components/Nav/VPNavBarTitle.vue @@ -50,7 +50,7 @@ const routeLocale = useRouteLocale() } :deep(.logo) { - height: var(--vp-nav-logo-height, 24px); + height: min(var(--vp-nav-logo-height, 24px), 48px); margin-right: 8px; } From 62ac0b3371d6c33e923fc8bcd98149190cf436df Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 21 Jul 2024 00:52:03 +0800 Subject: [PATCH 04/13] feat(theme): add `@vuepress/plugin-cache` --- .vscode/launch.json | 10 +- docs/notes/theme/config/主题配置.md | 20 +++ plugins/plugin-shikiji/src/node/highlight.ts | 15 +- .../plugin-shikiji/src/node/shikiPlugin.ts | 2 +- pnpm-lock.yaml | 35 +++-- theme/package.json | 11 ++ theme/src/node/config/resolveThemeOption.ts | 2 + theme/src/node/extendsMarkdown.ts | 134 ------------------ theme/src/node/plugins/getPlugins.ts | 7 + theme/src/node/theme.ts | 12 +- theme/src/shared/options/index.ts | 10 ++ theme/src/shared/options/plugins.ts | 5 +- 12 files changed, 97 insertions(+), 166 deletions(-) delete mode 100644 theme/src/node/extendsMarkdown.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 7d29fa0a..d4ebb81e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,13 +5,19 @@ "name": "dev", "request": "launch", "type": "node-terminal", - "command": "pnpm run dev" + "command": "pnpm dev" + }, + { + "name": "build", + "request": "launch", + "type": "node-terminal", + "command": "pnpm build" }, { "name": "docs:dev", "type": "node-terminal", "request": "launch", - "command": "pnpm run docs:dev" + "command": "pnpm docs:dev" }, { "name": "docs:build", diff --git a/docs/notes/theme/config/主题配置.md b/docs/notes/theme/config/主题配置.md index 7c818f00..7a14c0d3 100644 --- a/docs/notes/theme/config/主题配置.md +++ b/docs/notes/theme/config/主题配置.md @@ -161,6 +161,26 @@ interface BlogOptions { } ``` +### cache + +- 类型: `false | 'memory' | 'filesystem'` +- 默认值: `filesystem` +- 详情: + + 是否启用 编译缓存,或配置缓存方式 + + 此配置项用于解决 VuePress 启动速度慢的问题,在首次启动服务时,对编译结果进行缓存,二次启动时 + 直接读取缓存,跳过编译,从而加快启动速度。 + + - `false`:禁用 缓存 + - `'memory'`:使用内存缓存,此方式可获得更快的启动速度,但随着项目文件数量增加,内存占用会增加, + 适合文章数量较少的项目使用 + - `'filesystem'`:使用文件系统缓存,此方式可获得相对快且稳定的启动速度,更适合内容多的项目使用 + + ::: warning + 该字段不支持在 [主题配置文件 `plume.config.js`](./配置说明.md#主题配置文件) 中进行配置。 + ::: + ### locales - 类型: `Record` diff --git a/plugins/plugin-shikiji/src/node/highlight.ts b/plugins/plugin-shikiji/src/node/highlight.ts index 31caddf6..49e1f69e 100644 --- a/plugins/plugin-shikiji/src/node/highlight.ts +++ b/plugins/plugin-shikiji/src/node/highlight.ts @@ -19,11 +19,10 @@ import { transformerRenderWhitespace, } from '@shikijs/transformers' import type { HighlighterOptions, ThemeOptions } from './types.js' -import { LRUCache, attrsToLines, resolveLanguage } from './utils/index.js' +import { attrsToLines, resolveLanguage } from './utils/index.js' import { defaultHoverInfoProcessor, transformerTwoslash } from './twoslash/rendererTransformer.js' const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) -const cache = new LRUCache(64) const vueRE = /-vue$/ const mustacheRE = /\{\{.*?\}\}/g @@ -32,7 +31,6 @@ const decorationsRE = /^\/\/ @decorations:(.*)\n/ export async function highlight( theme: ThemeOptions, options: HighlighterOptions, - isDev: boolean, ): Promise<(str: string, lang: string, attrs: string) => string> { const { defaultHighlightLang: defaultLang = '', @@ -95,14 +93,6 @@ export async function highlight( let lang = resolveLanguage(language) || defaultLang const vPre = vueRE.test(lang) ? '' : 'v-pre' - const key = str + language + attrs - - if (isDev) { - const rendered = cache.get(key) - if (rendered) - return rendered - } - if (lang) { const langLoaded = loadedLanguages.includes(lang as any) if (!langLoaded && !isPlainLang(lang) && !isSpecialLang(lang)) { @@ -183,9 +173,6 @@ export async function highlight( const rendered = restoreMustache(highlighted) - if (isDev) - cache.set(key, rendered) - return rendered } catch (e) { diff --git a/plugins/plugin-shikiji/src/node/shikiPlugin.ts b/plugins/plugin-shikiji/src/node/shikiPlugin.ts index bedf633c..2d961a9d 100644 --- a/plugins/plugin-shikiji/src/node/shikiPlugin.ts +++ b/plugins/plugin-shikiji/src/node/shikiPlugin.ts @@ -51,7 +51,7 @@ export function shikiPlugin({ extendsMarkdown: async (md, app) => { const theme = options.theme ?? { light: 'github-light', dark: 'github-dark' } - md.options.highlight = await highlight(theme, options, app.env.isDev) + md.options.highlight = await highlight(theme, options) md.use(highlightLinesPlugin) md.use(preWrapperPlugin, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad469a4c..d232ca5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -259,6 +259,9 @@ importers: theme: dependencies: + '@iconify/vue': + specifier: ^4.1.2 + version: 4.1.2(vue@3.4.33(typescript@5.5.3)) '@pengzhanbo/utils': specifier: ^1.1.2 version: 1.1.2 @@ -283,6 +286,9 @@ importers: '@vuepress/plugin-active-header-links': specifier: 2.0.0-rc.39 version: 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3))) + '@vuepress/plugin-cache': + specifier: 2.0.0-rc.39 + version: 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3))) '@vuepress/plugin-comment': specifier: 2.0.0-rc.39 version: 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3))) @@ -361,6 +367,10 @@ importers: vuepress-plugin-md-power: specifier: workspace:* version: link:../plugins/plugin-md-power + devDependencies: + '@iconify/json': + specifier: ^2.2.229 + version: 2.2.229 packages: @@ -1934,6 +1944,11 @@ packages: peerDependencies: vuepress: 2.0.0-rc.14 + '@vuepress/plugin-cache@2.0.0-rc.39': + resolution: {integrity: sha512-PVsC797lGMuu8L7jtW9vv2hYM+d5qq5fbWwBJuSyRXEdpcwryhAjGWnz9F19dYe5KWLYG6EbCoANTQObmiyBag==} + peerDependencies: + vuepress: 2.0.0-rc.14 + '@vuepress/plugin-comment@2.0.0-rc.39': resolution: {integrity: sha512-/oCS+0wH/MtE4c1HUKlqH/tj70oXSz/tfR1hsHj8F8wiZ+IVJxexvtzMKk0vdRmYnH4nqeZh6dg5ggSJjrLEZQ==} peerDependencies: @@ -3981,14 +3996,13 @@ packages: resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==} engines: {node: '>=0.10.0'} - lru-cache@10.0.1: - resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} - engines: {node: 14 || >=16.14} - lru-cache@10.0.2: resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==} engines: {node: 14 || >=16.14} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.0.0: resolution: {integrity: sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==} engines: {node: 20 || >=22} @@ -7269,6 +7283,11 @@ snapshots: - '@vue/composition-api' - typescript + '@vuepress/plugin-cache@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))': + dependencies: + lru-cache: 10.4.3 + vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)) + '@vuepress/plugin-comment@2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)))': dependencies: '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.3)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3))) @@ -9534,11 +9553,11 @@ snapshots: longest@2.0.1: {} - lru-cache@10.0.1: {} - lru-cache@10.0.2: dependencies: - semver: 7.6.0 + semver: 7.6.3 + + lru-cache@10.4.3: {} lru-cache@11.0.0: {} @@ -10331,7 +10350,7 @@ snapshots: path-scurry@1.10.1: dependencies: - lru-cache: 10.0.1 + lru-cache: 10.0.2 minipass: 5.0.0 path-scurry@2.0.0: diff --git a/theme/package.json b/theme/package.json index b7aee393..0b6be2f4 100644 --- a/theme/package.json +++ b/theme/package.json @@ -59,9 +59,16 @@ "tsup:watch": "tsup --config tsup.config.ts --watch" }, "peerDependencies": { + "@iconify/json": "^2", "vuepress": "2.0.0-rc.14" }, + "peerDependenciesMeta": { + "@iconify/json": { + "optional": true + } + }, "dependencies": { + "@iconify/vue": "^4.1.2", "@pengzhanbo/utils": "^1.1.2", "@vuepress-plume/plugin-content-update": "workspace:*", "@vuepress-plume/plugin-fonts": "workspace:*", @@ -70,6 +77,7 @@ "@vuepress-plume/plugin-shikiji": "workspace:*", "@vuepress/helper": "2.0.0-rc.39", "@vuepress/plugin-active-header-links": "2.0.0-rc.39", + "@vuepress/plugin-cache": "2.0.0-rc.39", "@vuepress/plugin-comment": "2.0.0-rc.39", "@vuepress/plugin-docsearch": "2.0.0-rc.39", "@vuepress/plugin-git": "2.0.0-rc.38", @@ -95,5 +103,8 @@ "vue-router": "^4.4.0", "vuepress-plugin-md-enhance": "2.0.0-rc.52", "vuepress-plugin-md-power": "workspace:*" + }, + "devDependencies": { + "@iconify/json": "^2.2.229" } } diff --git a/theme/src/node/config/resolveThemeOption.ts b/theme/src/node/config/resolveThemeOption.ts index 54648591..040da522 100644 --- a/theme/src/node/config/resolveThemeOption.ts +++ b/theme/src/node/config/resolveThemeOption.ts @@ -6,6 +6,7 @@ export function resolveThemeOptions({ plugins, hostname, configFile, + cache, ...localeOptions }: PlumeThemeOptions) { const pluginOptions = plugins ?? themePlugins ?? {} @@ -17,6 +18,7 @@ export function resolveThemeOptions({ } return { + cache, configFile, pluginOptions, hostname, diff --git a/theme/src/node/extendsMarkdown.ts b/theme/src/node/extendsMarkdown.ts deleted file mode 100644 index b1e2bbb3..00000000 --- a/theme/src/node/extendsMarkdown.ts +++ /dev/null @@ -1,134 +0,0 @@ -/** - * 针对主题使用了 shiki + twoslash, 以及各种各样的对 markdown 的扩展, - * 导致了 markdown render 的速度变得越来越慢,如果每次启动都全量编译,那么时间开销会非常夸张。 - * 因此,对 markdown render 包装一层 缓存,通过 content hash 对比内容是否有更新, - * 没有更新的直接应用缓存从而跳过编译过程,加快启动速度。 - * - * 此功能计划做成独立的插件,但还不确定是放在 vuepress/ecosystem 还是在 主题插件内, - * 也有可能到 vuepress/core 仓库中进行更深度的优化。 - * 因此,先在本主题中进行 实验性验证。 - * - * 使用此功能后,本主题原本的启动耗时,由每次 13s 左右 优化到 二次启动时 1.2s 左右。 - * 基本只剩下 vuepress 本身的开销和 加载 shiki 所有语言带来 0.5s 左右的开销。 - */ -import process from 'node:process' -import { fs, path } from 'vuepress/utils' -import type { App } from 'vuepress' -import type { Markdown, MarkdownEnv } from 'vuepress/markdown' -import { hash } from './utils/index.js' - -export interface CacheData { - content: string - env: MarkdownEnv -} - -// { [filepath]: CacheDta } -export type Cache = Record - -// { [filepath]: hash } -export type Metadata = Record - -const CACHE_DIR = 'markdown/rendered' -const META_FILE = '_metadata.json' - -export async function extendsMarkdown(md: Markdown, app: App): Promise { - if (app.env.isBuild && !fs.existsSync(app.dir.cache(CACHE_DIR))) { - return - } - const basename = app.dir.cache(CACHE_DIR) - - await fs.ensureDir(basename) - - const speed = checkIOSpeed(basename) - - const metaFilepath = `${basename}/${META_FILE}` - - const metadata = (await readFile(metaFilepath)) || {} - - let timer: ReturnType | null = null - const update = (filepath: string, data: CacheData): void => { - writeFile(`${basename}/${filepath}`, data) - - if (timer) { - clearTimeout(timer) - } - timer = setTimeout(async () => writeFile(metaFilepath, metadata), 200) - } - const rawRender = md.render - md.render = (input, env: MarkdownEnv) => { - const filepath = env.filePathRelative - - if (!filepath) { - return rawRender(input, env) - } - - const key = hash(input) - const filename = normalizeFilename(filepath) - - if (metadata[filepath] === key) { - const cached = readFileSync(`${basename}/${filename}`) - if (cached) { - Object.assign(env, cached.env) - return cached.content - } - else { - metadata[filepath] = '' - } - } - const start = performance.now() - const content = rawRender(input, env) - - /** - * High-frequency I/O is also a time-consuming operation, - * therefore, for render operations with low overhead, caching is not performed. - */ - if (performance.now() - start > speed) { - metadata[filepath] = key - update(filename, { content, env }) - } - return content - } -} - -function normalizeFilename(filename: string): string { - return hash(filename).slice(0, 10) -} - -async function readFile(filepath: string): Promise { - try { - const content = await fs.readFile(filepath, 'utf-8') - return JSON.parse(content) as T - } - catch { - return null - } -} - -function readFileSync(filepath: string): T | null { - try { - const content = fs.readFileSync(filepath, 'utf-8') - return JSON.parse(content) as T - } - catch { - return null - } -} - -async function writeFile(filepath: string, data: T): Promise { - return await fs.writeFile(filepath, JSON.stringify(data), 'utf-8') -} - -export function checkIOSpeed(cwd = process.cwd()): number { - try { - const tmp = path.join(cwd, 'tmp') - fs.writeFileSync(tmp, '{}', 'utf-8') - const start = performance.now() - readFileSync(tmp) - const end = performance.now() - fs.unlinkSync(tmp) - return end - start - } - catch { - return 0.15 - } -} diff --git a/theme/src/node/plugins/getPlugins.ts b/theme/src/node/plugins/getPlugins.ts index 1be808c9..02db9c3d 100644 --- a/theme/src/node/plugins/getPlugins.ts +++ b/theme/src/node/plugins/getPlugins.ts @@ -1,5 +1,6 @@ import type { App, PluginConfig } from 'vuepress/core' import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links' +import { cachePlugin } from '@vuepress/plugin-cache' import { docsearchPlugin } from '@vuepress/plugin-docsearch' import { gitPlugin } from '@vuepress/plugin-git' import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe' @@ -27,12 +28,14 @@ export interface SetupPluginOptions { app: App pluginOptions: PlumeThemePluginOptions hostname?: string + cache?: false | 'memory' | 'filesystem' } export function getPlugins({ app, pluginOptions, hostname, + cache, }: SetupPluginOptions): PluginConfig { const isProd = !app.env.isDev @@ -156,5 +159,9 @@ export function getPlugins({ plugins.push(seoPlugin({ hostname })) } + if (cache !== false) { + plugins.push(cachePlugin({ type: cache || 'filesystem' })) + } + return plugins } diff --git a/theme/src/node/theme.ts b/theme/src/node/theme.ts index bb05790f..77123e9c 100644 --- a/theme/src/node/theme.ts +++ b/theme/src/node/theme.ts @@ -26,7 +26,6 @@ import { } from './autoFrontmatter/index.js' import { prepareData, watchPrepare } from './prepare/index.js' import { prepareThemeData } from './prepare/prepareThemeData.js' -import { extendsMarkdown } from './extendsMarkdown.js' export function plumeTheme(options: PlumeThemeOptions = {}): Theme { const { @@ -34,6 +33,7 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme { pluginOptions, hostname, configFile, + cache, } = resolveThemeOptions(options) return (app) => { @@ -65,7 +65,7 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme { alias: resolveAlias(), - plugins: getPlugins({ app, pluginOptions, hostname }), + plugins: getPlugins({ app, pluginOptions, hostname, cache }), onInitialized: async (app) => { const { localeOptions } = await waitForConfigLoaded() @@ -90,14 +90,14 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme { }, extendsPage: async (page) => { - const { localeOptions } = await waitForConfigLoaded() - await waitForAutoFrontmatter() + const { localeOptions, autoFrontmatter } = await waitForConfigLoaded() + if ((autoFrontmatter ?? pluginOptions.frontmatter) !== false) { + await waitForAutoFrontmatter() + } extendsPageData(page as Page, localeOptions) resolvePageHead(page, localeOptions) }, - extendsMarkdown, - extendsBundlerOptions, templateBuildRenderer, diff --git a/theme/src/shared/options/index.ts b/theme/src/shared/options/index.ts index fcc83290..5e080e76 100644 --- a/theme/src/shared/options/index.ts +++ b/theme/src/shared/options/index.ts @@ -23,6 +23,13 @@ export interface PlumeThemeOptions extends PlumeThemeLocaleOptions { */ hostname?: string + /** + * 是否启用编译缓存 + * + * @default 'filesystem' + */ + cache?: false | 'memory' | 'filesystem' + /** * 加密配置 */ @@ -33,6 +40,9 @@ export interface PlumeThemeOptions extends PlumeThemeLocaleOptions { */ configFile?: string + /** + * 自动插入 frontmatter + */ autoFrontmatter?: false | Omit } diff --git a/theme/src/shared/options/plugins.ts b/theme/src/shared/options/plugins.ts index 8bc1cac5..a50abc2e 100644 --- a/theme/src/shared/options/plugins.ts +++ b/theme/src/shared/options/plugins.ts @@ -60,13 +60,16 @@ export interface PlumeThemePluginOptions { * @deprecated * 请使用 [@vuepress/plugin-baidu-analytics](https://ecosystem.vuejs.press/zh/plugins/analytics/baidu-analytics.html) 代替 */ - baiduTongji?: never + baiduTongji?: false | { key: string } /** * @deprecated 使用 `autoFrontmatter` 代替 */ frontmatter?: Omit + /** + * 阅读时间、字数统计 + */ readingTime?: false | ReadingTimePluginOptions /** From 6a1152fa936472ee52664d706c83ffda45c9009f Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 21 Jul 2024 00:52:32 +0800 Subject: [PATCH 05/13] chore: improve tsup config --- plugins/plugin-content-update/tsup.config.ts | 1 + plugins/plugin-fonts/tsup.config.ts | 1 + plugins/plugin-search/tsup.config.ts | 1 + theme/tsup.config.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/plugins/plugin-content-update/tsup.config.ts b/plugins/plugin-content-update/tsup.config.ts index 82f5d4ea..0e2f5dc4 100644 --- a/plugins/plugin-content-update/tsup.config.ts +++ b/plugins/plugin-content-update/tsup.config.ts @@ -39,6 +39,7 @@ export default defineConfig(() => { ...DEFAULT_OPTIONS, entry: ['./src/client/config.ts'], outDir: './lib/client', + dts: false, external: [...clientExternal, './components/Content.js'], }, // client/index.js diff --git a/plugins/plugin-fonts/tsup.config.ts b/plugins/plugin-fonts/tsup.config.ts index 5ee9cc4e..339fd4bc 100644 --- a/plugins/plugin-fonts/tsup.config.ts +++ b/plugins/plugin-fonts/tsup.config.ts @@ -25,6 +25,7 @@ export default defineConfig(() => { ...DEFAULT_OPTIONS, entry: ['./src/client/config.ts'], outDir: './lib/client', + dts: false, external: clientExternal, }, ] diff --git a/plugins/plugin-search/tsup.config.ts b/plugins/plugin-search/tsup.config.ts index 5977d1b0..1789565c 100644 --- a/plugins/plugin-search/tsup.config.ts +++ b/plugins/plugin-search/tsup.config.ts @@ -53,6 +53,7 @@ export default defineConfig(() => { entry: ['./src/client/config.ts'], outDir: './lib/client', external: clientExternal, + dts: false, }, // client/index.js { diff --git a/theme/tsup.config.ts b/theme/tsup.config.ts index 74408186..56142148 100644 --- a/theme/tsup.config.ts +++ b/theme/tsup.config.ts @@ -58,6 +58,7 @@ export default defineConfig((cli) => { ...DEFAULT_OPTIONS, entry: ['./src/client/config.ts'], outDir: './lib/client', + dts: false, external: [ ...clientExternal, './composables/index.js', From 2fc28878fa837adac68e357c14e3ee4947ba5ba1 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 21 Jul 2024 11:22:59 +0800 Subject: [PATCH 06/13] chore: update deps --- pnpm-lock.yaml | 12 +++++++++++- theme/package.json | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d232ca5b..a1528d53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -259,6 +259,9 @@ importers: theme: dependencies: + '@iconify/utils': + specifier: ^2.1.25 + version: 2.1.25 '@iconify/vue': specifier: ^4.1.2 version: 4.1.2(vue@3.4.33(typescript@5.5.3)) @@ -349,6 +352,9 @@ importers: katex: specifier: ^0.16.11 version: 0.16.11 + local-pkg: + specifier: ^0.5.0 + version: 0.5.0 nanoid: specifier: ^5.0.7 version: 5.0.7 @@ -397,6 +403,9 @@ 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==} @@ -5699,8 +5708,9 @@ snapshots: '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)': dependencies: - '@algolia/client-search': 4.20.0 algoliasearch: 4.20.0 + optionalDependencies: + '@algolia/client-search': 4.20.0 '@algolia/cache-browser-local-storage@4.20.0': dependencies: diff --git a/theme/package.json b/theme/package.json index 0b6be2f4..bd70097e 100644 --- a/theme/package.json +++ b/theme/package.json @@ -68,6 +68,7 @@ } }, "dependencies": { + "@iconify/utils": "^2.1.25", "@iconify/vue": "^4.1.2", "@pengzhanbo/utils": "^1.1.2", "@vuepress-plume/plugin-content-update": "workspace:*", @@ -98,6 +99,7 @@ "gray-matter": "^4.0.3", "json2yaml": "^1.1.0", "katex": "^0.16.11", + "local-pkg": "^0.5.0", "nanoid": "^5.0.7", "vue": "^3.4.33", "vue-router": "^4.4.0", From 5d0d626eeff2bf7db73f73275728ba7c3e15757e Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Mon, 22 Jul 2024 00:42:41 +0800 Subject: [PATCH 07/13] feat(theme): add support for `iconify` localization --- .../src/node/features/icons/writer.ts | 58 ++++-- pnpm-lock.yaml | 9 +- theme/package.json | 1 - theme/src/client/components/VPIcon.vue | 66 +++++- theme/src/client/components/VPIconify.vue | 75 +++++++ .../src/client/components/VPSidebarGroup.vue | 1 + theme/src/client/composables/icons.ts | 16 ++ theme/src/client/composables/index.ts | 1 + theme/src/client/globalComponents.ts | 10 +- theme/src/client/shim.d.ts | 7 + theme/src/client/styles/code.css | 2 +- theme/src/client/styles/icons.css | 4 + theme/src/node/plugins/getPlugins.ts | 2 - theme/src/node/prepare/index.ts | 2 + theme/src/node/prepare/prepareIcons.ts | 196 ++++++++++++++++++ theme/src/node/utils/index.ts | 1 + theme/src/node/utils/interopDefault.ts | 6 + 17 files changed, 409 insertions(+), 48 deletions(-) create mode 100644 theme/src/client/components/VPIconify.vue create mode 100644 theme/src/client/composables/icons.ts create mode 100644 theme/src/node/prepare/prepareIcons.ts create mode 100644 theme/src/node/utils/interopDefault.ts diff --git a/plugins/plugin-md-power/src/node/features/icons/writer.ts b/plugins/plugin-md-power/src/node/features/icons/writer.ts index 5ee96470..44388a81 100644 --- a/plugins/plugin-md-power/src/node/features/icons/writer.ts +++ b/plugins/plugin-md-power/src/node/features/icons/writer.ts @@ -10,6 +10,7 @@ import { parseRect } from '../../utils/parseRect.js' export interface IconCacheItem { className: string + background: boolean content: string } @@ -18,6 +19,8 @@ const iconDataCache = new Map() const URL_CONTENT_RE = /(url\([\s\S]+?\))/ const CSS_PATH = 'internal/md-power/icons.css' +let locate: ((name: string) => any) | undefined + function resolveOption(opt?: boolean | IconsOptions): Required { const options = typeof opt === 'object' ? opt : {} options.prefix ??= 'vp-mdi' @@ -65,19 +68,18 @@ export function createIconCSSWriter(app: App, opt?: boolean | IconsOptions) { if (!isInstalled) return - if (cache.has(iconName)) - return cache.get(iconName)!.className + if (cache.has(iconName)) { + const item = cache.get(iconName)! + return `${item.className}${item.background ? ' bg' : ''}` + } const item: IconCacheItem = { className: `${prefix}-${nanoid()}`, - content: '', + ...genIcon(iconName), } cache.set(iconName, item) - genIconContent(iconName, (content) => { - item.content = content - writeCss() - }) - return item.className + writeCss() + return `${item.className}${item.background ? ' bg' : ''}` } async function initIcon() { @@ -89,6 +91,11 @@ export function createIconCSSWriter(app: App, opt?: boolean | IconsOptions) { return } + if (!locate) { + const mod = await interopDefault(import('@iconify/json')) + locate = mod.locate + } + return await writeCss() } @@ -97,12 +104,13 @@ export function createIconCSSWriter(app: App, opt?: boolean | IconsOptions) { function getDefaultContent(options: Required) { const { prefix, size, color } = options - return `[class^="${prefix}-"], -[class*=" ${prefix}-"] { + return `[class^="${prefix}-"] { display: inline-block; width: ${size}; height: ${size}; vertical-align: middle; +} +[class^="${prefix}-"]:not(.bg) { color: inherit; background-color: ${color}; -webkit-mask: var(--svg) no-repeat; @@ -110,24 +118,29 @@ function getDefaultContent(options: Required) { -webkit-mask-size: 100% 100%; mask-size: 100% 100%; } +[class^="${prefix}-"].bg { + background-color: transparent; + background-image: var(--svg); + background-repeat: no-repeat; + background-size: 100% 100%; +} ` } -let locate: ((name: string) => any) | undefined - -async function genIconContent(iconName: string, cb: (content: string) => void) { +function genIcon(iconName: string): { + content: string + background: boolean +} { if (!locate) { - const mod = await interopDefault(import('@iconify/json')) - locate = mod.locate + return { content: '', background: false } } - const [collect, name] = iconName.split(':') let iconJson: any = iconDataCache.get(collect) if (!iconJson) { const filename = locate(collect) try { - iconJson = JSON.parse(await fs.readFile(filename, 'utf-8')) + iconJson = JSON.parse(fs.readFileSync(filename, 'utf-8')) iconDataCache.set(collect, iconJson) } catch { @@ -135,14 +148,19 @@ async function genIconContent(iconName: string, cb: (content: string) => void) { } } const data = getIconData(iconJson, name) - if (!data) - return logger.error(`[plugin-md-power] Can not read icon in ${collect}, ${name} is missing!`) + if (!data) { + logger.error(`[plugin-md-power] Can not read icon in ${collect}, ${name} is missing!`) + return { content: '', background: false } + } const content = getIconContentCSS(data, { height: data.height || 24, }) const match = content.match(URL_CONTENT_RE) - return cb(match ? match[1] : '') + return { + content: match ? match[1] : '', + background: !data.body.includes('currentColor'), + } } function existsSync(fp: string) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1528d53..f06d3b91 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -274,9 +274,6 @@ importers: '@vuepress-plume/plugin-fonts': specifier: workspace:* version: link:../plugins/plugin-fonts - '@vuepress-plume/plugin-iconify': - specifier: workspace:* - version: link:../plugins/plugin-iconify '@vuepress-plume/plugin-search': specifier: workspace:* version: link:../plugins/plugin-search @@ -403,9 +400,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==} @@ -5708,9 +5702,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: diff --git a/theme/package.json b/theme/package.json index bd70097e..6c958d2b 100644 --- a/theme/package.json +++ b/theme/package.json @@ -73,7 +73,6 @@ "@pengzhanbo/utils": "^1.1.2", "@vuepress-plume/plugin-content-update": "workspace:*", "@vuepress-plume/plugin-fonts": "workspace:*", - "@vuepress-plume/plugin-iconify": "workspace:*", "@vuepress-plume/plugin-search": "workspace:*", "@vuepress-plume/plugin-shikiji": "workspace:*", "@vuepress/helper": "2.0.0-rc.39", diff --git a/theme/src/client/components/VPIcon.vue b/theme/src/client/components/VPIcon.vue index 5a587d9a..d8ec94c8 100644 --- a/theme/src/client/components/VPIcon.vue +++ b/theme/src/client/components/VPIcon.vue @@ -2,35 +2,83 @@ import { computed } from 'vue' import { isLinkHttp } from 'vuepress/shared' import { withBase } from 'vuepress/client' +import VPIconify from '@theme/VPIconify.vue' +import { useIconsData } from '../composables/index.js' const props = defineProps<{ name: string | { svg: string } + size?: string | number + color?: string }>() -const isLink = computed(() => - typeof props.name === 'string' && (isLinkHttp(props.name) || props.name[0] === '/'), -) -const isSvg = computed(() => typeof props.name === 'object' && !!props.name.svg) +const iconsData = useIconsData() + +const type = computed(() => { + if (typeof props.name === 'string' && (isLinkHttp(props.name) || props.name[0] === '/')) { + return 'link' + } + if (typeof props.name === 'object' && !!props.name.svg) { + return 'svg' + } + if (typeof props.name === 'string' && iconsData.value[props.name]) { + return 'local' + } + return 'remote' +}) const svg = computed(() => { - if (isSvg.value) + if (type.value === 'svg') return (props.name as { svg: string }).svg return '' }) const link = computed(() => { - if (isLink.value) { + if (type.value === 'link') { const link = props.name as string return isLinkHttp(link) ? link : withBase(link) } return '' }) + +const className = computed(() => { + if (type.value === 'local') { + const name = props.name as string + return iconsData.value[name] || '' + } + return '' +}) + +const size = computed(() => { + const size = props.size + if (!size) + return undefined + if (String(Number(size)) === size) + return `${size}px` + + return size +}) + +const style = computed(() => ({ + 'background-color': props.color, + 'width': size.value, + 'height': size.value, +})) diff --git a/theme/src/client/components/VPSidebarGroup.vue b/theme/src/client/components/VPSidebarGroup.vue index 502b9179..d34683d8 100644 --- a/theme/src/client/components/VPSidebarGroup.vue +++ b/theme/src/client/components/VPSidebarGroup.vue @@ -44,6 +44,7 @@ onBeforeUnmount(() => { .group + .group { padding-top: 10px; border-top: 1px solid var(--vp-c-divider); + transition: border var(--t-color); } @media (min-width: 960px) { diff --git a/theme/src/client/composables/icons.ts b/theme/src/client/composables/icons.ts new file mode 100644 index 00000000..d382fcbf --- /dev/null +++ b/theme/src/client/composables/icons.ts @@ -0,0 +1,16 @@ +import { icons } from '@internal/iconify' +import { ref } from 'vue' +import type { Ref } from 'vue' + +type IconsData = Record +type IconsDataRef = Ref + +const iconsData: IconsDataRef = ref(icons) + +export const useIconsData = (): IconsDataRef => iconsData + +if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) { + __VUE_HMR_RUNTIME__.updateIcons = (data: IconsData) => { + iconsData.value = data + } +} diff --git a/theme/src/client/composables/index.ts b/theme/src/client/composables/index.ts index 162d5cb1..7610b76b 100644 --- a/theme/src/client/composables/index.ts +++ b/theme/src/client/composables/index.ts @@ -1,6 +1,7 @@ export * from './theme-data.js' export * from './dark-mode.js' export * from './data.js' +export * from './icons.js' export * from './scroll-promise.js' export * from './scroll-behavior.js' diff --git a/theme/src/client/globalComponents.ts b/theme/src/client/globalComponents.ts index 2c619d18..4f099711 100644 --- a/theme/src/client/globalComponents.ts +++ b/theme/src/client/globalComponents.ts @@ -5,6 +5,7 @@ import VPCard from '@theme/global/VPCard.vue' import VPLinkCard from '@theme/global/VPLinkCard.vue' import VPBadge from '@theme/global/VPBadge.vue' import VPCardGrid from '@theme/global/VPCardGrid.vue' +import VPIcon from '@theme/VPIcon.vue' export function globalComponents(app: App) { app.component('Badge', VPBadge) @@ -36,13 +37,8 @@ export function globalComponents(app: App) { return null }) - app.component('Icon', (props) => { - const Iconify = app.component('Iconify') - if (Iconify) - return h(Iconify, props) - - return null - }) + app.component('Icon', VPIcon) + app.component('VPIcon', VPIcon) /** @deprecated */ app.component('HomeBox', VPHomeBox) diff --git a/theme/src/client/shim.d.ts b/theme/src/client/shim.d.ts index 50d20c20..e456184f 100644 --- a/theme/src/client/shim.d.ts +++ b/theme/src/client/shim.d.ts @@ -58,3 +58,10 @@ declare module '@internal/encrypt' { encrypt, } } + +declare module '@internal/iconify' { + const icons: Record + export { + icons, + } +} diff --git a/theme/src/client/styles/code.css b/theme/src/client/styles/code.css index b66be19d..dcdd388a 100644 --- a/theme/src/client/styles/code.css +++ b/theme/src/client/styles/code.css @@ -12,7 +12,7 @@ html:not(.dark) .vp-code span { margin: 16px -24px; overflow-x: auto; background-color: var(--vp-code-block-bg); - transition: background-color 0.5s; + transition: background-color var(--t-color); } @media (min-width: 640px) { diff --git a/theme/src/client/styles/icons.css b/theme/src/client/styles/icons.css index 689d65d3..97dcdf79 100644 --- a/theme/src/client/styles/icons.css +++ b/theme/src/client/styles/icons.css @@ -1,14 +1,18 @@ [class^="vpi-"], [class*=" vpi-"], .vp-icon { + display: inline-block; width: 1em; height: 1em; + vertical-align: middle; } [class^="vpi-"].bg, [class*=" vpi-"].bg, .vp-icon.bg { background-color: transparent; + background-image: var(--icon); + background-repeat: no-repeat; background-size: 100% 100%; } diff --git a/theme/src/node/plugins/getPlugins.ts b/theme/src/node/plugins/getPlugins.ts index 02db9c3d..9bae6cfe 100644 --- a/theme/src/node/plugins/getPlugins.ts +++ b/theme/src/node/plugins/getPlugins.ts @@ -5,7 +5,6 @@ import { docsearchPlugin } from '@vuepress/plugin-docsearch' import { gitPlugin } from '@vuepress/plugin-git' import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe' import { nprogressPlugin } from '@vuepress/plugin-nprogress' -import { iconifyPlugin } from '@vuepress-plume/plugin-iconify' import { shikiPlugin } from '@vuepress-plume/plugin-shikiji' import { commentPlugin } from '@vuepress/plugin-comment' import { type MarkdownEnhancePluginOptions, mdEnhancePlugin } from 'vuepress-plugin-md-enhance' @@ -41,7 +40,6 @@ export function getPlugins({ const plugins: PluginConfig = [ - iconifyPlugin(), fontsPlugin(), contentUpdatePlugin(), activeHeaderLinksPlugin({ diff --git a/theme/src/node/prepare/index.ts b/theme/src/node/prepare/index.ts index ca117c99..00a39ca7 100644 --- a/theme/src/node/prepare/index.ts +++ b/theme/src/node/prepare/index.ts @@ -5,6 +5,7 @@ import { prepareArticleTagColors } from './prepareArticleTagColor.js' import { preparedBlogData } from './prepareBlogData.js' import { prepareEncrypt } from './prepareEncrypt.js' import { prepareSidebar } from './prepareSidebar.js' +import { prepareIcons } from './prepareIcons.js' export async function prepareData( app: App, @@ -15,6 +16,7 @@ export async function prepareData( preparedBlogData(app, localeOptions, encrypt), prepareSidebar(app, localeOptions), prepareEncrypt(app, encrypt), + prepareIcons(app, localeOptions), ]) } diff --git a/theme/src/node/prepare/prepareIcons.ts b/theme/src/node/prepare/prepareIcons.ts new file mode 100644 index 00000000..44dd6120 --- /dev/null +++ b/theme/src/node/prepare/prepareIcons.ts @@ -0,0 +1,196 @@ +import type { App, Page } from 'vuepress' +import { isArray, isString, uniq } from '@pengzhanbo/utils' +import { fs } from 'vuepress/utils' +import { entries, isLinkAbsolute, isLinkHttp, isPlainObject } from '@vuepress/helper' +import { isPackageExists } from 'local-pkg' +import { getIconContentCSS, getIconData } from '@iconify/utils' +import type { NavItem, PlumeThemeLocaleOptions, Sidebar } from '../../shared/index.js' +import { interopDefault, logger, nanoid, resolveContent, writeTemp } from '../utils/index.js' + +interface IconData { + className: string + background?: boolean + content: string +} + +type CollectMap = Record +type IconDataMap = Record + +const ICON_REGEXP = /<(?:VP)?Icon(?:ify)?([^>]*)>/g +const ICON_NAME_REGEXP = /name="([^"]+)"/ +const URL_CONTENT_REGEXP = /(url\([\s\S]+\))/ +const JS_FILENAME = 'internal/iconify.js' +const CSS_FILENAME = 'internal/iconify.css' + +const isInstalled = isPackageExists('@iconify/json') +let locate!: ((name: string) => any) + +// { iconName: { className, content } } +const cache: IconDataMap = {} + +export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOptions) { + if (!isInstalled) { + await writeTemp(app, JS_FILENAME, resolveContent(app, { name: 'icons', content: '{}' })) + return + } + + const iconList: string[] = [] + app.pages.forEach(page => iconList.push(...getIconsWithPage(page))) + iconList.push(...getIconWithThemeConfig(localeOptions)) + + const collectMap: CollectMap = {} + uniq(iconList).filter(icon => !cache[icon]).forEach((iconName) => { + const [collect, name] = iconName.split(':') + if (!collectMap[collect]) + collectMap[collect] = [] + + collectMap[collect].push(name) + }) + + if (!locate) { + const mod = await interopDefault(import('@iconify/json')) + locate = mod.locate + } + + const unknownList = (await Promise.all( + entries(collectMap).map(([collect, names]) => resolveCollect(collect, names)), + )).flat() + + if (unknownList.length) { + logger.warn(`[iconify] Unknown icons: ${unknownList.join(', ')}`) + } + + let cssCode = '' + const map: Record = {} + for (const [iconName, { className, content, background }] of entries(cache)) { + map[iconName] = `${className}${background ? ' bg' : ''}` + cssCode += `.${className} {\n --icon: ${content};\n}\n` + } + + await Promise.all([ + writeTemp(app, CSS_FILENAME, cssCode), + writeTemp(app, JS_FILENAME, resolveContent(app, { + name: 'icons', + content: map, + before: `import './iconify.css'`, + })), + ]) +} + +function getIconsWithPage(page: Page): string[] { + const list = page.contentRendered + .match(ICON_REGEXP)?.map(match => match.match(ICON_NAME_REGEXP)?.[1]) + .filter(Boolean) as string[] || [] + + if (page.frontmatter.icon && isString(page.frontmatter.icon)) { + list.push(page.frontmatter.icon) + } + + return list +} + +function getIconWithThemeConfig(localeOptions: PlumeThemeLocaleOptions): string[] { + const list: string[] = [] + // navbar notes sidebar + const locales = localeOptions.locales || {} + entries(locales).forEach(([, { navbar, sidebar, notes }]) => { + if (navbar) { + list.push(...getIconWithNavbar(navbar)) + } + const sidebarList: Sidebar[] = Object.values(sidebar || {}) as Sidebar[] + if (notes) { + notes.notes.forEach((note) => { + if (note.sidebar) + sidebarList.push(note.sidebar) + }) + } + sidebarList.forEach(sidebar => list.push(...getIconWithSidebar(sidebar))) + }) + + return list +} + +function getIconWithNavbar(navbar: NavItem[]): string[] { + const list: string[] = [] + navbar.forEach((item) => { + if (typeof item !== 'string') { + if (typeof item.icon === 'string' && !isLinkHttp(item.icon) && !isLinkAbsolute(item.icon)) + list.push(item.icon) + if (item.items?.length) + list.push(...getIconWithNavbar(item.items)) + } + }) + return list +} + +function getIconWithSidebar(sidebar: Sidebar): string[] { + const list: string[] = [] + if (isArray(sidebar)) { + sidebar.forEach((item) => { + if (typeof item !== 'string') { + if (typeof item.icon === 'string' && !isLinkHttp(item.icon) && !isLinkAbsolute(item.icon)) + list.push(item.icon) + if (item.items?.length) + list.push(...getIconWithSidebar(item.items)) + } + }) + } + else if (isPlainObject(sidebar)) { + entries(sidebar).forEach(([, item]) => { + if (typeof item !== 'string') { + if (isArray(item)) { + list.push(...getIconWithSidebar(item)) + } + else if (item.items?.length) { + list.push(...getIconWithSidebar(item.items)) + } + } + }) + } + return list +} + +async function resolveCollect(collect: string, names: string[]) { + const filepath = locate(collect) + const config = await readJSON(filepath) + + if (!config) { + logger.warn(`[iconify] Can not find icon collect: ${collect}!`) + return [] + } + + const unknownList: string[] = [] + + for (const name of names) { + const data = getIconData(config, name) + const icon = `${collect}:${name}` + if (!data) { + unknownList.push(icon) + } + else if (!cache[icon]) { + const content = getIconContentCSS(data, { + height: data.height || 24, + }) + const matched = content.match(URL_CONTENT_REGEXP)?.[1] ?? '' + /** + * @see - https://iconify.design/docs/libraries/utils/get-icon-css.html#options + */ + const background = !data.body.includes('currentColor') + cache[icon] = { + className: `vpi-${nanoid()}`, + background, + content: matched, + } + } + } + return unknownList +} + +async function readJSON(filepath: string) { + try { + return await fs.readJSON(filepath, 'utf-8') + } + catch { + return null + } +} diff --git a/theme/src/node/utils/index.ts b/theme/src/node/utils/index.ts index 35c75a61..0efa81b7 100644 --- a/theme/src/node/utils/index.ts +++ b/theme/src/node/utils/index.ts @@ -7,5 +7,6 @@ export const logger = new Logger(THEME_NAME) export * from './hash.js' export * from './path.js' export * from './package.js' +export * from './interopDefault.js' export * from './resolveContent.js' export * from './writeTemp.js' diff --git a/theme/src/node/utils/interopDefault.ts b/theme/src/node/utils/interopDefault.ts new file mode 100644 index 00000000..5be4386c --- /dev/null +++ b/theme/src/node/utils/interopDefault.ts @@ -0,0 +1,6 @@ +export type Awaitable = T | Promise + +export async function interopDefault(m: Awaitable): Promise { + const resolved = await m + return (resolved as any).default || resolved +} From 87466969b8406141ac134a9930682a1204e68bbd Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Mon, 22 Jul 2024 15:23:10 +0800 Subject: [PATCH 08/13] docs: update docs --- docs/.vuepress/notes.ts | 2 +- docs/notes/theme/guide/markdown/进阶.md | 6 +- docs/notes/theme/guide/功能/图标.md | 85 +++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 docs/notes/theme/guide/功能/图标.md diff --git a/docs/.vuepress/notes.ts b/docs/.vuepress/notes.ts index 64627dad..b0e17600 100644 --- a/docs/.vuepress/notes.ts +++ b/docs/.vuepress/notes.ts @@ -56,7 +56,7 @@ export const zhNotes = definePlumeNotesConfig({ icon: 'lucide:box', collapsed: false, dir: '功能', - items: ['代码复制', '内容搜索', '评论', '加密', '组件', '文章水印', '友情链接页', 'seo', 'sitemap'], + items: ['图标', '代码复制', '内容搜索', '评论', '加密', '组件', '文章水印', '友情链接页', 'seo', 'sitemap'], }, { text: '自定义', diff --git a/docs/notes/theme/guide/markdown/进阶.md b/docs/notes/theme/guide/markdown/进阶.md index 0f2a7366..5475d9ac 100644 --- a/docs/notes/theme/guide/markdown/进阶.md +++ b/docs/notes/theme/guide/markdown/进阶.md @@ -447,7 +447,7 @@ interface PlotOptions { ## iconify 图标 在 Markdown 文件中使用 [iconify](https://iconify.design/) 的图标。 主题虽然提供了 -[``](/guide/features/component/#iconify) 组件来支持在 markdown 中使用图标, +[``](../功能/组件.md#图标) 组件来支持在 markdown 中使用图标, 但是它需要从远程加载图标,可能速度比较慢。 为此,主题提供了另一种可选的方式,以更简单的方式,在 Markdown 中使用图标,并且将 图标资源编译到 @@ -537,6 +537,8 @@ github: :[tdesign:logo-github-filled]: 修改颜色::[tdesign:logo-github-filled /#f00]: 修改大小::[tdesign:logo-github-filled 36px]: 修改大小颜色::[tdesign:logo-github-filled 36px/#f00]: + +彩色图标 :[skill-icons:vscode-dark 36px]: ``` 输出: @@ -546,6 +548,8 @@ github: :[tdesign:logo-github-filled]: 修改大小::[tdesign:logo-github-filled 36px]: 修改大小颜色::[tdesign:logo-github-filled 36px/#f00]: +彩色图标 :[skill-icons:vscode-dark 36px]: + ## can I use 此功能默认不启用,你可以在配置文件中启用它。 diff --git a/docs/notes/theme/guide/功能/图标.md b/docs/notes/theme/guide/功能/图标.md new file mode 100644 index 00000000..f43357cb --- /dev/null +++ b/docs/notes/theme/guide/功能/图标.md @@ -0,0 +1,85 @@ +--- +title: 图标 +icon: raphael:smile2 +author: pengzhanbo +createTime: 2024/07/22 10:45:47 +permalink: /guide/features/icon/ +--- + +## 概述 + +主题支持 [iconify](https://icon-sets.iconify.design/) 的所有图标,并提供了不同的方式来使用它们。 + +## 组件 + +通过 `` 组件来使用图标。 + +你可以在 markdown 文件中使用该 组件。 + +### 属性 + +`` 组件接受一个 `name` 属性,用于指定图标的名称。还支持传入 `color` 和 `size` 属性来设置图标的颜色和大小。 +但对于 彩色图标,`color` 属性设置无效。 + +| 属性 | 类型 | 描述 | +| ----- | ------------------ | -------------------------------------------------------------------------- | +| name | `string` | 图标名称,在 [iconify](https://icon-sets.iconify.design/) 可获取对应的名称 | +| color | `string` | 图标颜色,仅纯色图标支持设置颜色 | +| size | `number \| string` | 设置图标大小,默认单位为 `px` ,可自定义单位 | + +**示例:** + +````md +- 纯色图标: +- 定义纯色图标的颜色和大小: +- 彩色图标: +- 定义彩色图标的大小: +```` + +- 纯色图标: +- 定义纯色图标的颜色和大小: +- 彩色图标: +- 定义彩色图标的大小: + +### 加载图标 + +`` 组件默认通过 远程请求 `CDN` 获取图标资源,但这可能受到网络环境的影响,出现加载失败 +或者延迟显示的情况。 + +为了解决这一问题,主题建议 在你的站点项目中安装 `@iconify/json` 包。 +主题会检查当前项目是否安装了 `@iconify/json`,如果安装了该包,则主题自动解析所使用到的图标, +并处理为本地图标资源,在构建时打包到 `dist` 目录中。 + +由于 `@iconify/json` 包比较大,需要手动进行安装: + +::: code-tabs +@tab pnpm + +```sh +pnpm add @iconify/json +``` + +@tab yarn + +```sh +yarn add @iconify/json +``` + +@tab npm + +```sh +npm install @iconify/json +``` + +::: + +## markdown 语法糖 + +相关内容请查看 [iconify-图标 语法糖](../markdown/进阶.md#iconify-图标) + +--- + +::: tip 说明 +[navbar](../../config/主题配置.md#navbar) 配置和 [notes](../../config/主题配置.md#notes) 配置 +中的 `icon` 选项,也支持传入 iconify 图标名,并且当安装了 `@iconify/json`,也会自动解析为本地图标资源。 +::: From 78449a88fe691ecca774aa88be83adda1253eb74 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Mon, 22 Jul 2024 21:34:53 +0800 Subject: [PATCH 09/13] docs: update docs --- docs/notes/theme/guide/markdown/进阶.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/notes/theme/guide/markdown/进阶.md b/docs/notes/theme/guide/markdown/进阶.md index 5475d9ac..d0649d64 100644 --- a/docs/notes/theme/guide/markdown/进阶.md +++ b/docs/notes/theme/guide/markdown/进阶.md @@ -446,11 +446,10 @@ interface PlotOptions { ## iconify 图标 -在 Markdown 文件中使用 [iconify](https://iconify.design/) 的图标。 主题虽然提供了 +在 Markdown 文件中使用 [iconify](https://iconify.design/) 的图标。 主题一方面提供了 [``](../功能/组件.md#图标) 组件来支持在 markdown 中使用图标, -但是它需要从远程加载图标,可能速度比较慢。 -为此,主题提供了另一种可选的方式,以更简单的方式,在 Markdown 中使用图标,并且将 图标资源编译到 +一方面,主题还提供了另一种可选的方式,以更简单的方式,在 Markdown 中使用图标,并且将 图标资源编译到 本地静态资源中。 ### 配置 From fa0693198559a0cb03755725cb2454ed20cb3017 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Tue, 23 Jul 2024 00:17:12 +0800 Subject: [PATCH 10/13] perf(theme): optimize `` --- theme/src/client/components/VPIcon.vue | 4 +-- theme/src/client/components/VPIconify.vue | 9 +------ theme/src/client/components/VPSidebarItem.vue | 26 +++++++++---------- theme/src/client/components/global/VPCard.vue | 2 +- .../client/components/global/VPLinkCard.vue | 2 +- theme/src/client/styles/utils.css | 4 +-- 6 files changed, 20 insertions(+), 27 deletions(-) diff --git a/theme/src/client/components/VPIcon.vue b/theme/src/client/components/VPIcon.vue index d8ec94c8..a85f6df8 100644 --- a/theme/src/client/components/VPIcon.vue +++ b/theme/src/client/components/VPIcon.vue @@ -69,13 +69,13 @@ const style = computed(() => ({ diff --git a/theme/src/client/components/VPIconify.vue b/theme/src/client/components/VPIconify.vue index dab195c8..d2492c09 100644 --- a/theme/src/client/components/VPIconify.vue +++ b/theme/src/client/components/VPIconify.vue @@ -58,7 +58,7 @@ const bind = computed(() => ({ - - diff --git a/theme/src/client/components/VPSidebarItem.vue b/theme/src/client/components/VPSidebarItem.vue index 29ddd419..ca951b5f 100644 --- a/theme/src/client/components/VPSidebarItem.vue +++ b/theme/src/client/components/VPSidebarItem.vue @@ -220,7 +220,7 @@ function onCaretClick() { transition: color var(--t-color); } -.item :deep(.vp-iconify) { +.item :deep(.vp-icon) { margin: 0 0.25rem 0 0; font-size: 0.9em; color: var(--vp-c-text-2); @@ -240,21 +240,21 @@ function onCaretClick() { color: var(--vp-c-text-1); } -.vp-sidebar-item.level-0.is-active > .item > :deep(.vp-iconify), -.vp-sidebar-item.level-1.is-active > .item > :deep(.vp-iconify), -.vp-sidebar-item.level-2.is-active > .item > :deep(.vp-iconify), -.vp-sidebar-item.level-3.is-active > .item > :deep(.vp-iconify), -.vp-sidebar-item.level-4.is-active > .item > :deep(.vp-iconify), -.vp-sidebar-item.level-5.is-active > .item > :deep(.vp-iconify) { +.vp-sidebar-item.level-0.is-active > .item > :deep(.vp-icon), +.vp-sidebar-item.level-1.is-active > .item > :deep(.vp-icon), +.vp-sidebar-item.level-2.is-active > .item > :deep(.vp-icon), +.vp-sidebar-item.level-3.is-active > .item > :deep(.vp-icon), +.vp-sidebar-item.level-4.is-active > .item > :deep(.vp-icon), +.vp-sidebar-item.level-5.is-active > .item > :deep(.vp-icon) { color: var(--vp-c-brand-1); } -.vp-sidebar-item.level-0.is-link > .item:hover :deep(.vp-iconify), -.vp-sidebar-item.level-1.is-link > .item:hover :deep(.vp-iconify), -.vp-sidebar-item.level-2.is-link > .item:hover :deep(.vp-iconify), -.vp-sidebar-item.level-3.is-link > .item:hover :deep(.vp-iconify), -.vp-sidebar-item.level-4.is-link > .item:hover :deep(.vp-iconify), -.vp-sidebar-item.level-5.is-link > .item:hover :deep(.vp-iconify) { +.vp-sidebar-item.level-0.is-link > .item:hover :deep(.vp-icon), +.vp-sidebar-item.level-1.is-link > .item:hover :deep(.vp-icon), +.vp-sidebar-item.level-2.is-link > .item:hover :deep(.vp-icon), +.vp-sidebar-item.level-3.is-link > .item:hover :deep(.vp-icon), +.vp-sidebar-item.level-4.is-link > .item:hover :deep(.vp-icon), +.vp-sidebar-item.level-5.is-link > .item:hover :deep(.vp-icon) { color: var(--vp-c-brand-1); } diff --git a/theme/src/client/components/global/VPCard.vue b/theme/src/client/components/global/VPCard.vue index 4bb1193f..0ac973bf 100644 --- a/theme/src/client/components/global/VPCard.vue +++ b/theme/src/client/components/global/VPCard.vue @@ -49,7 +49,7 @@ const icon = computed(() => { box-shadow: var(--vp-shadow-2); } -.vp-card-wrapper :deep(.vp-iconify), +.vp-card-wrapper :deep(.vp-icon), .vp-card-wrapper :deep(.vp__img) { margin: 0; } diff --git a/theme/src/client/components/global/VPLinkCard.vue b/theme/src/client/components/global/VPLinkCard.vue index 933f2ca9..c484c935 100644 --- a/theme/src/client/components/global/VPLinkCard.vue +++ b/theme/src/client/components/global/VPLinkCard.vue @@ -75,7 +75,7 @@ defineProps<{ content: ""; } -.vp-link-card .link :deep(.vp-iconify), +.vp-link-card .link :deep(.vp-icon), .vp-link-card .link :deep(.vp__img) { margin: 0; } diff --git a/theme/src/client/styles/utils.css b/theme/src/client/styles/utils.css index 2e17332d..41ae252e 100644 --- a/theme/src/client/styles/utils.css +++ b/theme/src/client/styles/utils.css @@ -8,8 +8,8 @@ white-space: nowrap; } -.vp-iconify { - margin: 0.3em; +.vp-icon { + margin: 0 0.3em; } .smooth { From f32511be6a8e70ae8a566970f2392431e1e2523d Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Tue, 23 Jul 2024 00:28:58 +0800 Subject: [PATCH 11/13] refactor!: removed `plugin-iconify` --- plugins/plugin-iconify/LICENSE | 21 ----- plugins/plugin-iconify/README.md | 47 ----------- plugins/plugin-iconify/package.json | 51 ------------ .../src/client/components/Iconify.vue | 78 ------------------- .../src/client/composables/iconify.ts | 32 -------- .../src/client/composables/index.ts | 1 - plugins/plugin-iconify/src/client/config.ts | 11 --- plugins/plugin-iconify/src/node/index.ts | 6 -- plugins/plugin-iconify/src/node/plugin.ts | 24 ------ plugins/plugin-iconify/src/shim.d.ts | 6 -- plugins/plugin-iconify/tsup.config.ts | 38 --------- .../node/autoFrontmatter/resolveOptions.ts | 10 +-- 12 files changed, 4 insertions(+), 321 deletions(-) delete mode 100644 plugins/plugin-iconify/LICENSE delete mode 100644 plugins/plugin-iconify/README.md delete mode 100644 plugins/plugin-iconify/package.json delete mode 100644 plugins/plugin-iconify/src/client/components/Iconify.vue delete mode 100644 plugins/plugin-iconify/src/client/composables/iconify.ts delete mode 100644 plugins/plugin-iconify/src/client/composables/index.ts delete mode 100644 plugins/plugin-iconify/src/client/config.ts delete mode 100644 plugins/plugin-iconify/src/node/index.ts delete mode 100644 plugins/plugin-iconify/src/node/plugin.ts delete mode 100644 plugins/plugin-iconify/src/shim.d.ts delete mode 100644 plugins/plugin-iconify/tsup.config.ts diff --git a/plugins/plugin-iconify/LICENSE b/plugins/plugin-iconify/LICENSE deleted file mode 100644 index 9f677c90..00000000 --- a/plugins/plugin-iconify/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (C) 2021 - PRESENT by pengzhanbo - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/plugins/plugin-iconify/README.md b/plugins/plugin-iconify/README.md deleted file mode 100644 index eed2a744..00000000 --- a/plugins/plugin-iconify/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# `@vuepress-plume/plugin-iconify` - -添加 `iconify` 图标库支持。并注入全局组件 `` - -## Install - -```sh -npm install @vuepress-plume/plugin-iconify -# or -pnpm add @vuepress-plume/plugin-iconify -# or -yarn add @vuepress-plume/plugin-iconify -``` - -## Usage - -``` js -// .vuepress/config.[jt]s -import { iconifyPlugin } from '@vuepress-plume/plugin-iconify' - -export default { - // ... - plugins: [ - iconifyPlugin() - ] - // ... -} -``` - -## Options - -```ts -interface IconifyOptions { - /** - * 组件名, 默认 `Iconify` - */ - componentName?: string - color?: string - size?: string | number -} -``` - -## Component - -```vue - -``` diff --git a/plugins/plugin-iconify/package.json b/plugins/plugin-iconify/package.json deleted file mode 100644 index 5c707da0..00000000 --- a/plugins/plugin-iconify/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@vuepress-plume/plugin-iconify", - "type": "module", - "version": "1.0.0-rc.81", - "description": "The Plugin for VuePress 2 - iconify", - "author": "pengzhanbo ", - "license": "MIT", - "homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git", - "directory": "plugins/plugin-iconify" - }, - "bugs": { - "url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues" - }, - "exports": { - ".": { - "types": "./lib/node/index.d.ts", - "import": "./lib/node/index.js" - }, - "./package.json": "./package.json" - }, - "main": "lib/node/index.js", - "types": "./lib/node/index.d.ts", - "files": [ - "lib" - ], - "scripts": { - "build": "pnpm run copy && pnpm run tsup", - "clean": "rimraf --glob ./lib", - "copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib", - "tsup": "tsup --config tsup.config.ts" - }, - "peerDependencies": { - "vuepress": "2.0.0-rc.14" - }, - "dependencies": { - "@iconify/vue": "^4.1.2", - "vue": "^3.4.33" - }, - "publishConfig": { - "access": "public" - }, - "keyword": [ - "VuePress", - "vuepress plugin", - "iconify", - "vuepress-plugin-iconify" - ] -} diff --git a/plugins/plugin-iconify/src/client/components/Iconify.vue b/plugins/plugin-iconify/src/client/components/Iconify.vue deleted file mode 100644 index cd675ccb..00000000 --- a/plugins/plugin-iconify/src/client/components/Iconify.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - diff --git a/plugins/plugin-iconify/src/client/composables/iconify.ts b/plugins/plugin-iconify/src/client/composables/iconify.ts deleted file mode 100644 index 352a750f..00000000 --- a/plugins/plugin-iconify/src/client/composables/iconify.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { IconifyIcon } from '@iconify/vue' -import { loadIcon } from '@iconify/vue' -import { ref, watch } from 'vue' -import type { Ref } from 'vue' - -export function useIconify(name: Ref) { - const icon = ref(null) - const loaded = ref(false) - - async function loadIconComponent() { - if (icon.value) - return - - if (!__VUEPRESS_SSR__) { - try { - loaded.value = false - const cached = await loadIcon(name.value) - icon.value = cached - } - finally { - loaded.value = true - } - } - else { - loaded.value = true - } - } - - watch(() => name.value, loadIconComponent, { immediate: true }) - - return { icon, loaded } -} diff --git a/plugins/plugin-iconify/src/client/composables/index.ts b/plugins/plugin-iconify/src/client/composables/index.ts deleted file mode 100644 index be30fbe9..00000000 --- a/plugins/plugin-iconify/src/client/composables/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './iconify.js' diff --git a/plugins/plugin-iconify/src/client/config.ts b/plugins/plugin-iconify/src/client/config.ts deleted file mode 100644 index f132faf2..00000000 --- a/plugins/plugin-iconify/src/client/config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineClientConfig } from 'vuepress/client' -import type { ClientConfig } from 'vuepress/client' -import Iconify from './components/Iconify.vue' - -declare const __VP_ICONIFY_NAME__: string -export default defineClientConfig({ - enhance({ app }) { - const name = __VP_ICONIFY_NAME__ || 'Iconify' - app.component(name, Iconify) - }, -}) as ClientConfig diff --git a/plugins/plugin-iconify/src/node/index.ts b/plugins/plugin-iconify/src/node/index.ts deleted file mode 100644 index b7f9afcd..00000000 --- a/plugins/plugin-iconify/src/node/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { iconifyPlugin } from './plugin.js' - -export * from './plugin.js' - -/** @deprecated 请使用 具名导出 替代 默认导出 */ -export default iconifyPlugin diff --git a/plugins/plugin-iconify/src/node/plugin.ts b/plugins/plugin-iconify/src/node/plugin.ts deleted file mode 100644 index 72d5417d..00000000 --- a/plugins/plugin-iconify/src/node/plugin.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Plugin } from 'vuepress/core' -import { getDirname, path } from 'vuepress/utils' - -export interface IconifyPluginOptions { - componentName?: string - color?: string - size?: string | number -} - -export function iconifyPlugin({ - componentName = 'Iconify', - size = '1em', - color = 'currentColor', -}: IconifyPluginOptions = {}): Plugin { - return { - name: '@vuepress-plume/plugin-iconify', - define: { - __VP_ICONIFY_NAME__: componentName, - __VP_ICONIFY_SIZE__: size, - __VP_ICONIFY_COLOR__: color, - }, - clientConfigFile: path.resolve(getDirname(import.meta.url), '../client/config.js'), - } -} diff --git a/plugins/plugin-iconify/src/shim.d.ts b/plugins/plugin-iconify/src/shim.d.ts deleted file mode 100644 index f1c86e6f..00000000 --- a/plugins/plugin-iconify/src/shim.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module '*.vue' { - const comp: any - export default comp -} - -declare const __VUEPRESS_SSR__: boolean diff --git a/plugins/plugin-iconify/tsup.config.ts b/plugins/plugin-iconify/tsup.config.ts deleted file mode 100644 index 875ee7a5..00000000 --- a/plugins/plugin-iconify/tsup.config.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { type Options, defineConfig } from 'tsup' - -const clientExternal: (string | RegExp)[] = [ - /.*\.vue$/, - /.*\.css$/, -] - -export default defineConfig(() => { - const DEFAULT_OPTIONS: Options = { - dts: true, - sourcemap: false, - splitting: false, - format: 'esm', - } - return [ - // node - { - ...DEFAULT_OPTIONS, - entry: ['./src/node/index.ts'], - outDir: './lib/node', - target: 'node18', - }, - // client/composables/index.js - { - ...DEFAULT_OPTIONS, - entry: ['./src/client/composables/index.ts'], - outDir: './lib/client/composables', - external: clientExternal, - }, - // client/config.js - { - ...DEFAULT_OPTIONS, - entry: ['./src/client/config.ts'], - outDir: './lib/client', - external: clientExternal, - }, - ] -}) diff --git a/theme/src/node/autoFrontmatter/resolveOptions.ts b/theme/src/node/autoFrontmatter/resolveOptions.ts index 81444b22..c417240b 100644 --- a/theme/src/node/autoFrontmatter/resolveOptions.ts +++ b/theme/src/node/autoFrontmatter/resolveOptions.ts @@ -39,12 +39,10 @@ export function resolveOptions( } const notesList = resolveNotesOptions(localeOptions) - const localesNotesDirs = notesList - .flatMap(({ notes, dir }) => { - dir = removeLeadingSlash(dir || '') - return notes.map(note => normalizePath(`${dir}/${note.dir || ''}/`)) - }) - .filter(Boolean) + const localesNotesDirs = uniq(notesList + .flatMap(({ notes, dir }) => + notes.map(note => removeLeadingSlash(normalizePath(`${dir}/${note.dir || ''}/`))), + )) const baseFrontmatter: AutoFrontmatterObject = {} From 7128af8e49cf72d26da263ca208b05545e5b3109 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Tue, 23 Jul 2024 11:48:28 +0800 Subject: [PATCH 12/13] docs: update docs --- docs/notes/theme/config/plugins/百度统计.md | 36 ++------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/docs/notes/theme/config/plugins/百度统计.md b/docs/notes/theme/config/plugins/百度统计.md index 63b8b470..badcb06f 100644 --- a/docs/notes/theme/config/plugins/百度统计.md +++ b/docs/notes/theme/config/plugins/百度统计.md @@ -6,37 +6,7 @@ permalink: /config/plugins/baidu-tongji/ --- ::: caution -主题计划在 未来的版本中 从内置插件中移除此插件。 +此插件已从主题内置插件中移除。 + +如需使用相关功能,请使用 [@vuepress/plugin-baidu-analytics](https://ecosystem.vuejs.press/zh/plugins/analytics/baidu-analytics.html) 代替。 ::: - -## 概述 - -为站点添加 百度统计。该插件默认不启用。 - -关联插件: [@vuepress-plume/plugin-baidu-tongji](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-baidu-tongji) - -## 配置 - -### key - -- 类型:`string` -- 默认值:`''` - -配置百度统计的key - -### 启用 - -```ts{7-9} -import { plumeTheme } from 'vuepress-theme-plume' -import { defineUserConfig } from 'vuepress' - -export default defineUserConfig({ - theme: plumeTheme({ - plugins: { - baiduTongji: { - key: '你的百度统计key' - } - } - }), -}) -``` From 8e1d931783b29c15c2e9786a8a1dcca1bab70120 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Tue, 23 Jul 2024 11:48:53 +0800 Subject: [PATCH 13/13] chore: update pnpm lock --- pnpm-lock.yaml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f06d3b91..158988be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,18 +124,6 @@ importers: specifier: 2.0.0-rc.14 version: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)) - plugins/plugin-iconify: - dependencies: - '@iconify/vue': - specifier: ^4.1.2 - version: 4.1.2(vue@3.4.33(typescript@5.5.3)) - vue: - specifier: ^3.4.33 - version: 3.4.33(typescript@5.5.3) - vuepress: - specifier: 2.0.0-rc.14 - version: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.12.10)(jiti@1.21.6)(tsx@4.16.0)(typescript@5.5.3)(yaml@2.4.2))(typescript@5.5.3)(vue@3.4.33(typescript@5.5.3)) - plugins/plugin-md-power: dependencies: '@iconify/utils':