diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..5d126348 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..ac5bc285 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +!.vuepress/ +!.*.js +.cache/ +.temp/ +node_modules/ +dist/ diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..b3ffd39f --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,13 @@ +module.exports = { + root: true, + extends: 'vuepress', + overrides: [ + { + files: ['*.ts', '*.vue'], + extends: 'vuepress-typescript', + parserOptions: { + project: ['tsconfig.json'], + }, + }, + ], +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d022441a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +* text eol=lf +*.txt text eol=crlf + +*.png binary +*.jpg binary +*.jpeg binary +*.ico binary +*.tff binary +*.woff binary +*.woff2 binary diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..400fc670 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,16 @@ +name: Bug Report +description: Create a report to help us improve +title: '[Bug report] ' +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + id: bug-description + attributes: + label: Description + description: A clear and concise description of what the bug is. If applicable, add screenshots to help explain your problem. If you intend to submit a PR for this issue, tell us in the description. Thanks! + placeholder: Bug description + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3aee83f9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Question & Discussion + url: https://github.com/vuepress/docs/discussions + about: Please ask and answer questions here. diff --git a/.github/workflows/check-docs.yml b/.github/workflows/check-docs.yml new file mode 100644 index 00000000..4e8e9506 --- /dev/null +++ b/.github/workflows/check-docs.yml @@ -0,0 +1,53 @@ +name: check-docs + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + check-docs: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + node: ['18', '20'] + bundler: ['vite', 'webpack'] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build source code + run: pnpm build + + - name: Build docs with ${{ matrix.bundler }} + run: pnpm docs:build + env: + DOCS_BUNDLER: ${{ matrix.bundler }} + + check-docs-result: + if: ${{ always() }} + name: check-docs result + runs-on: ubuntu-latest + needs: [check-docs] + steps: + - if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} + run: exit 1 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..9f6ae92a --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,44 @@ +name: docs + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + docs: + runs-on: ubuntu-latest + + env: + DOCS_GA_ID: G-CTB8FQ7VMW + NODE_VERSION: '18' + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build documentation site + run: pnpm docs:build + + - name: Deploy to GitHub Pages + uses: crazy-max/ghaction-github-pages@v4 + with: + repo: vuepress/vuepress.github.io + target_branch: main + build_dir: docs/.vuepress/dist + env: + GH_PAT: ${{ secrets.GH_PAGES_TOKEN }} diff --git a/.github/workflows/issue-commented.yml b/.github/workflows/issue-commented.yml new file mode 100644 index 00000000..d25cc571 --- /dev/null +++ b/.github/workflows/issue-commented.yml @@ -0,0 +1,17 @@ +name: issue-commented + +on: + issue_comment: + types: [created] + +jobs: + issue-commented: + name: remove stale on commented + if: contains(github.event.issue.labels.*.name, 'stale') + runs-on: ubuntu-latest + steps: + - uses: actions-cool/issues-helper@v3 + with: + actions: 'remove-labels' + token: ${{ secrets.GITHUB_TOKEN }} + labels: 'stale' diff --git a/.github/workflows/issue-daily.yml b/.github/workflows/issue-daily.yml new file mode 100644 index 00000000..4b51b033 --- /dev/null +++ b/.github/workflows/issue-daily.yml @@ -0,0 +1,42 @@ +name: issue-daily + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + issue-label-stale: + name: label stale issues + runs-on: ubuntu-latest + steps: + - uses: actions-cool/issues-helper@v3 + with: + actions: 'check-inactive' + token: ${{ secrets.GITHUB_TOKEN }} + inactive-day: 15 + inactive-label: 'stale' + exclude-labels: 'bug, documentation, enhancement, feature, help wanted, need reproduction, need review, stale' + body: | + This issue is marked as `stale` because it has not had recent activity. Issues marked with `stale` will be closed if they have no activity within 7 days. + + issue-close-stale: + name: close stale issues + runs-on: ubuntu-latest + steps: + - uses: actions-cool/issues-helper@v3 + with: + actions: 'close-issues' + token: ${{ secrets.GITHUB_TOKEN }} + labels: 'stale' + inactive-day: 7 + + issue-close-need-reproduction: + name: close need-reproduction issues + runs-on: ubuntu-latest + steps: + - uses: actions-cool/issues-helper@v3 + with: + actions: 'close-issues' + token: ${{ secrets.GITHUB_TOKEN }} + labels: 'need reproduction' + inactive-day: 7 diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml new file mode 100644 index 00000000..e38c9047 --- /dev/null +++ b/.github/workflows/issue-labeled.yml @@ -0,0 +1,30 @@ +name: issue-labeled + +on: + issues: + types: [labeled] + +jobs: + issue-invalid: + name: close invalid issue + if: github.event.label.name == 'invalid' + runs-on: ubuntu-latest + steps: + - uses: actions-cool/issues-helper@v3 + with: + actions: 'close-issue, create-comment' + token: ${{ secrets.GITHUB_TOKEN }} + body: | + Hello @${{ github.event.issue.user.login }}. This issue is marked as `invalid` and closed. Please follow the issue template. + + issue-need-reproduction: + name: need reproduction + if: github.event.label.name == 'need reproduction' + runs-on: ubuntu-latest + steps: + - uses: actions-cool/issues-helper@v3 + with: + actions: 'create-comment' + token: ${{ secrets.GITHUB_TOKEN }} + body: | + Hello @${{ github.event.issue.user.login }}. Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository or [v2.vuepress.vuejs.org/new](https://v2.vuepress.vuejs.org/new). Issues marked with `need reproduction` will be closed if they have no activity within 7 days. diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e3fdda76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# VuePress files +docs/.vuepress/.temp/ +docs/.vuepress/.cache/ +docs/.vuepress/dist/ + +# Node modules +node_modules/ + +# MacOS Desktop Services Store +.DS_Store + +# Log files +*.log diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 00000000..d69ab0a7 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +pnpm commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..fab6428a --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +pnpm lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..94c9f11d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +pnpm-lock.yaml +*.html +*.md diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..adc12201 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "vue.volar" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..9b673398 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.insertSpaces": true, + "editor.tabSize": 2, + "files.encoding": "utf8", + "files.eol": "\n", + "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true, + "[markdown]": { + "files.trimTrailingWhitespace": false + }, + "typescript.tsdk": "node_modules/typescript/lib", + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "vue" + ], + "cSpell.words": [ + "composables", + "devtool", + "docsearch", + "envinfo", + "esbuild", + "frontmatter", + "globby", + "gtag", + "mdit", + "nprogress", + "prefetch", + "preload", + "prismjs", + "shiki", + "slugify", + "unmount", + "vuejs", + "vuepress", + "vueuse", + "zoomable" + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..9ef07511 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# VuePress Documentation Contributing Guide + +## Overview + +VuePress documentation is powered by VuePress itself, which is built from the source code of this repository. + +All the markdown source files are placed in `docs` directory. We are maintaining two translations: + +- English (en-US) in `/` path +- Chinese (zh-CN) in `/zh/` path + +We have two different deployments: + +- Release deployment powered by [Netlify](https://www.netlify.com). This deployment is built from the latest released version, so users will not see unreleased changes. The domain name is [https://v2.vuepress.vuejs.org](https://v2.vuepress.vuejs.org). +- Developer deployment powered by [GitHub Pages](https://pages.github.com). This deployment is built from the latest commit, so developers could preview the latest changes. The domain name is [https://vuepress.github.io](https://vuepress.github.io). diff --git a/CONTRIBUTING_zh.md b/CONTRIBUTING_zh.md new file mode 100644 index 00000000..d0208725 --- /dev/null +++ b/CONTRIBUTING_zh.md @@ -0,0 +1,15 @@ +# VuePress 文档贡献指南 + +## 概览 + +VuePress 的文档是由 VuePress 自己驱动的,是由该仓库中的源码构建而来。 + +所有的 Markdown 源文件都放置在 `docs` 目录下。我们维护了两种翻译: + +- 英语 (en-US) 在 `/` 路径下 +- 中文 (zh-CN) 在 `/zh/` 路径下 + +我们部署了两套站点: + +- 在 [Netlify](https://www.netlify.com) 部署的 Release 版本。该站点是从最新发布的版本中构建而来,因此用户不会看到未发布的改动。域名为 [https://v2.vuepress.vuejs.org](https://v2.vuepress.vuejs.org)。 +- 在 [GitHub Pages](https://pages.github.com) 部署的 Developer 版本。该站点是从最新的提交中构建而来,因此开发者可以预览最新的改动。域名为 [https://vuepress.github.io](https://vuepress.github.io)。 diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..44387d62 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018-present, VuePress Community + +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/README.md b/README.md new file mode 100644 index 00000000..2b646bca --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# VuePress Documentation + +VuePress documentation repository. + +## Deployments + +- Release deployment: https://v2.vuepress.vuejs.org +- Developer deployment: https://vuepress.github.io + +## License + +[MIT](https://github.com/vuepress/docs/blob/main/LICENSE) diff --git a/docs/.vuepress/components/NpmBadge.vue b/docs/.vuepress/components/NpmBadge.vue new file mode 100644 index 00000000..c9ff7be2 --- /dev/null +++ b/docs/.vuepress/components/NpmBadge.vue @@ -0,0 +1,49 @@ + + + + + + + + + diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts new file mode 100644 index 00000000..5cf08ede --- /dev/null +++ b/docs/.vuepress/config.ts @@ -0,0 +1,197 @@ +import { createRequire } from 'node:module' +import process from 'node:process' +import { viteBundler } from '@vuepress/bundler-vite' +import { webpackBundler } from '@vuepress/bundler-webpack' +import { defineUserConfig } from '@vuepress/cli' +import { docsearchPlugin } from '@vuepress/plugin-docsearch' +import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics' +import { registerComponentsPlugin } from '@vuepress/plugin-register-components' +import { shikiPlugin } from '@vuepress/plugin-shiki' +import { defaultTheme } from '@vuepress/theme-default' +import { getDirname, path } from '@vuepress/utils' +import { + head, + navbarEn, + navbarZh, + sidebarEn, + sidebarZh, +} from './configs/index.js' + +const __dirname = getDirname(import.meta.url) +const require = createRequire(import.meta.url) +const isProd = process.env.NODE_ENV === 'production' + +export default defineUserConfig({ + // set site base to default value + base: '/', + + // extra tags in `
` + head, + + // site-level locales config + locales: { + '/': { + lang: 'en-US', + title: 'VuePress', + description: 'Vue-powered Static Site Generator', + }, + '/zh/': { + lang: 'zh-CN', + title: 'VuePress', + description: 'Vue 驱动的静态网站生成器', + }, + }, + + // specify bundler via environment variable + bundler: + process.env.DOCS_BUNDLER === 'webpack' ? webpackBundler() : viteBundler(), + + // configure default theme + theme: defaultTheme({ + logo: '/images/hero.png', + repo: 'vuepress/vuepress-next', + docsDir: 'docs', + + // theme-level locales config + locales: { + /** + * English locale config + * + * As the default locale of @vuepress/theme-default is English, + * we don't need to set all of the locale fields + */ + '/': { + // navbar + navbar: navbarEn, + // sidebar + sidebar: sidebarEn, + // page meta + editLinkText: 'Edit this page on GitHub', + }, + + /** + * Chinese locale config + */ + '/zh/': { + // navbar + navbar: navbarZh, + selectLanguageName: '简体中文', + selectLanguageText: '选择语言', + selectLanguageAriaLabel: '选择语言', + // sidebar + sidebar: sidebarZh, + // page meta + editLinkText: '在 GitHub 上编辑此页', + lastUpdatedText: '上次更新', + contributorsText: '贡献者', + // custom containers + tip: '提示', + warning: '注意', + danger: '警告', + // 404 page + notFound: [ + '这里什么都没有', + '我们怎么到这来了?', + '这是一个 404 页面', + '看起来我们进入了错误的链接', + ], + backToHome: '返回首页', + // a11y + openInNewWindow: '在新窗口打开', + toggleColorMode: '切换颜色模式', + toggleSidebar: '切换侧边栏', + }, + }, + + themePlugins: { + // only enable git plugin in production mode + git: isProd, + // use shiki plugin in production mode instead + prismjs: !isProd, + }, + }), + + // configure markdown + markdown: { + importCode: { + handleImportPath: (importPath) => { + // handle @vuepress packages import path + if (importPath.startsWith('@vuepress/')) { + const packageName = importPath.match(/^(@vuepress\/[^/]*)/)![1] + return importPath + .replace( + packageName, + path.dirname(require.resolve(`${packageName}/package.json`)), + ) + .replace('/src/', '/lib/') + .replace(/hotKey\.ts$/, 'hotKey.d.ts') + } + return importPath + }, + }, + }, + + // use plugins + plugins: [ + docsearchPlugin({ + appId: '34YFD9IUQ2', + apiKey: '9a9058b8655746634e01071411c366b8', + indexName: 'vuepress', + searchParameters: { + facetFilters: ['tags:v2'], + }, + locales: { + '/zh/': { + placeholder: '搜索文档', + translations: { + button: { + buttonText: '搜索文档', + buttonAriaLabel: '搜索文档', + }, + modal: { + searchBox: { + resetButtonTitle: '清除查询条件', + resetButtonAriaLabel: '清除查询条件', + cancelButtonText: '取消', + cancelButtonAriaLabel: '取消', + }, + startScreen: { + recentSearchesTitle: '搜索历史', + noRecentSearchesText: '没有搜索历史', + saveRecentSearchButtonTitle: '保存至搜索历史', + removeRecentSearchButtonTitle: '从搜索历史中移除', + favoriteSearchesTitle: '收藏', + removeFavoriteSearchButtonTitle: '从收藏中移除', + }, + errorScreen: { + titleText: '无法获取结果', + helpText: '你可能需要检查你的网络连接', + }, + footer: { + selectText: '选择', + navigateText: '切换', + closeText: '关闭', + searchByText: '搜索提供者', + }, + noResultsScreen: { + noResultsText: '无法找到相关结果', + suggestedQueryText: '你可以尝试查询', + reportMissingResultsText: '你认为该查询应该有结果?', + reportMissingResultsLinkText: '点击反馈', + }, + }, + }, + }, + }, + }), + googleAnalyticsPlugin({ + // we have multiple deployments, which would use different id + id: process.env.DOCS_GA_ID ?? '', + }), + registerComponentsPlugin({ + componentsDir: path.resolve(__dirname, './components'), + }), + // only enable shiki plugin in production mode + isProd ? shikiPlugin({ theme: 'dark-plus' }) : [], + ], +}) diff --git a/docs/.vuepress/configs/head.ts b/docs/.vuepress/configs/head.ts new file mode 100644 index 00000000..8d20be52 --- /dev/null +++ b/docs/.vuepress/configs/head.ts @@ -0,0 +1,40 @@ +import type { HeadConfig } from '@vuepress/core' + +export const head: HeadConfig[] = [ + [ + 'link', + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: `/images/icons/favicon-16x16.png`, + }, + ], + [ + 'link', + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: `/images/icons/favicon-32x32.png`, + }, + ], + ['link', { rel: 'manifest', href: '/manifest.webmanifest' }], + ['meta', { name: 'application-name', content: 'VuePress' }], + ['meta', { name: 'apple-mobile-web-app-title', content: 'VuePress' }], + ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], + [ + 'link', + { rel: 'apple-touch-icon', href: `/images/icons/apple-touch-icon.png` }, + ], + [ + 'link', + { + rel: 'mask-icon', + href: '/images/icons/safari-pinned-tab.svg', + color: '#3eaf7c', + }, + ], + ['meta', { name: 'msapplication-TileColor', content: '#3eaf7c' }], + ['meta', { name: 'theme-color', content: '#3eaf7c' }], +] diff --git a/docs/.vuepress/configs/index.ts b/docs/.vuepress/configs/index.ts new file mode 100644 index 00000000..b23dc923 --- /dev/null +++ b/docs/.vuepress/configs/index.ts @@ -0,0 +1,3 @@ +export * from './head.js' +export * from './navbar/index.js' +export * from './sidebar/index.js' diff --git a/docs/.vuepress/configs/meta.ts b/docs/.vuepress/configs/meta.ts new file mode 100644 index 00000000..64f53d0c --- /dev/null +++ b/docs/.vuepress/configs/meta.ts @@ -0,0 +1,8 @@ +import { createRequire } from 'node:module' +import { fs } from '@vuepress/utils' + +const require = createRequire(import.meta.url) + +export const version = fs.readJsonSync( + require.resolve('@vuepress/core/package.json'), +).version diff --git a/docs/.vuepress/configs/navbar/en.ts b/docs/.vuepress/configs/navbar/en.ts new file mode 100644 index 00000000..73874ecc --- /dev/null +++ b/docs/.vuepress/configs/navbar/en.ts @@ -0,0 +1,143 @@ +import type { NavbarConfig } from '@vuepress/theme-default' +import { version } from '../meta.js' + +export const navbarEn: NavbarConfig = [ + { + text: 'Guide', + link: '/guide/', + }, + { + text: 'Reference', + children: [ + { + text: 'VuePress', + children: [ + { + text: 'CLI', + link: '/reference/cli.html', + }, + '/reference/config.md', + '/reference/frontmatter.md', + '/reference/components.md', + '/reference/plugin-api.md', + '/reference/theme-api.md', + '/reference/client-api.md', + '/reference/node-api.md', + ], + }, + { + text: 'Bundlers', + children: [ + '/reference/bundler/vite.md', + '/reference/bundler/webpack.md', + ], + }, + { + text: 'Default Theme', + children: [ + '/reference/default-theme/config.md', + '/reference/default-theme/frontmatter.md', + '/reference/default-theme/components.md', + '/reference/default-theme/markdown.md', + '/reference/default-theme/styles.md', + '/reference/default-theme/extending.md', + ], + }, + ], + }, + { + text: 'Plugins', + children: [ + { + text: 'Common Features', + children: [ + '/reference/plugin/back-to-top.md', + '/reference/plugin/container.md', + '/reference/plugin/external-link-icon.md', + '/reference/plugin/google-analytics.md', + '/reference/plugin/medium-zoom.md', + '/reference/plugin/nprogress.md', + '/reference/plugin/register-components.md', + ], + }, + { + text: 'Content Search', + children: [ + '/reference/plugin/docsearch.md', + '/reference/plugin/search.md', + ], + }, + { + text: 'PWA', + children: [ + '/reference/plugin/pwa.md', + '/reference/plugin/pwa-popup.md', + ], + }, + { + text: 'Syntax Highlighting', + children: [ + '/reference/plugin/prismjs.md', + '/reference/plugin/shiki.md', + ], + }, + { + text: 'Theme Development', + children: [ + '/reference/plugin/active-header-links.md', + '/reference/plugin/git.md', + '/reference/plugin/palette.md', + '/reference/plugin/theme-data.md', + '/reference/plugin/toc.md', + ], + }, + ], + }, + { + text: 'Learn More', + children: [ + { + text: 'Advanced', + children: [ + '/advanced/architecture.md', + '/advanced/plugin.md', + '/advanced/theme.md', + { + text: 'Cookbook', + link: '/advanced/cookbook/', + }, + ], + }, + { + text: 'Resources', + children: [ + { + text: 'Contributing Guide', + link: 'https://github.com/vuepress/vuepress-next/blob/main/CONTRIBUTING.md', + }, + { + text: 'Awesome VuePress', + link: 'https://github.com/vuepress/awesome-vuepress', + }, + ], + }, + ], + }, + { + text: `v${version}`, + children: [ + { + text: 'Changelog', + link: 'https://github.com/vuepress/vuepress-next/blob/main/CHANGELOG.md', + }, + { + text: 'v1.x', + link: 'https://v1.vuepress.vuejs.org', + }, + { + text: 'v0.x', + link: 'https://v0.vuepress.vuejs.org', + }, + ], + }, +] diff --git a/docs/.vuepress/configs/navbar/index.ts b/docs/.vuepress/configs/navbar/index.ts new file mode 100644 index 00000000..7183393c --- /dev/null +++ b/docs/.vuepress/configs/navbar/index.ts @@ -0,0 +1,2 @@ +export * from './en.js' +export * from './zh.js' diff --git a/docs/.vuepress/configs/navbar/zh.ts b/docs/.vuepress/configs/navbar/zh.ts new file mode 100644 index 00000000..17f77fd3 --- /dev/null +++ b/docs/.vuepress/configs/navbar/zh.ts @@ -0,0 +1,140 @@ +import type { NavbarConfig } from '@vuepress/theme-default' +import { version } from '../meta.js' + +export const navbarZh: NavbarConfig = [ + { + text: '指南', + link: '/zh/guide/', + }, + { + text: '参考', + children: [ + { + text: 'VuePress', + children: [ + '/zh/reference/cli.md', + '/zh/reference/config.md', + '/zh/reference/frontmatter.md', + '/zh/reference/components.md', + '/zh/reference/plugin-api.md', + '/zh/reference/theme-api.md', + '/zh/reference/client-api.md', + '/zh/reference/node-api.md', + ], + }, + { + text: '打包工具', + children: [ + '/zh/reference/bundler/vite.md', + '/zh/reference/bundler/webpack.md', + ], + }, + { + text: '默认主题', + children: [ + '/zh/reference/default-theme/config.md', + '/zh/reference/default-theme/frontmatter.md', + '/zh/reference/default-theme/components.md', + '/zh/reference/default-theme/markdown.md', + '/zh/reference/default-theme/styles.md', + '/zh/reference/default-theme/extending.md', + ], + }, + ], + }, + { + text: '插件', + children: [ + { + text: '常用功能', + children: [ + '/zh/reference/plugin/back-to-top.md', + '/zh/reference/plugin/container.md', + '/zh/reference/plugin/external-link-icon.md', + '/zh/reference/plugin/google-analytics.md', + '/zh/reference/plugin/medium-zoom.md', + '/zh/reference/plugin/nprogress.md', + '/zh/reference/plugin/register-components.md', + ], + }, + { + text: '内容搜索', + children: [ + '/zh/reference/plugin/docsearch.md', + '/zh/reference/plugin/search.md', + ], + }, + { + text: 'PWA', + children: [ + '/zh/reference/plugin/pwa.md', + '/zh/reference/plugin/pwa-popup.md', + ], + }, + { + text: '语法高亮', + children: [ + '/zh/reference/plugin/prismjs.md', + '/zh/reference/plugin/shiki.md', + ], + }, + { + text: '主题开发', + children: [ + '/zh/reference/plugin/active-header-links.md', + '/zh/reference/plugin/git.md', + '/zh/reference/plugin/palette.md', + '/zh/reference/plugin/theme-data.md', + '/zh/reference/plugin/toc.md', + ], + }, + ], + }, + { + text: '了解更多', + children: [ + { + text: '深入', + children: [ + '/zh/advanced/architecture.md', + '/zh/advanced/plugin.md', + '/zh/advanced/theme.md', + { + text: 'Cookbook', + link: '/zh/advanced/cookbook/', + }, + ], + }, + { + text: '其他资源', + children: [ + { + text: '贡献指南', + link: 'https://github.com/vuepress/vuepress-next/blob/main/CONTRIBUTING_zh.md', + }, + { + text: 'Awesome VuePress', + link: 'https://github.com/vuepress/awesome-vuepress', + }, + ], + }, + ], + }, + { + text: `v${version}`, + children: [ + { + text: '更新日志', + link: 'https://github.com/vuepress/vuepress-next/blob/main/CHANGELOG.md', + }, + { + text: 'v1.x', + link: 'https://v1.vuepress.vuejs.org/zh/', + }, + { + text: 'v0.x', + link: 'https://v0.vuepress.vuejs.org/zh/', + }, + ], + }, +] diff --git a/docs/.vuepress/configs/sidebar/en.ts b/docs/.vuepress/configs/sidebar/en.ts new file mode 100644 index 00000000..b358ab59 --- /dev/null +++ b/docs/.vuepress/configs/sidebar/en.ts @@ -0,0 +1,126 @@ +import type { SidebarConfig } from '@vuepress/theme-default' + +export const sidebarEn: SidebarConfig = { + '/guide/': [ + { + text: 'Guide', + children: [ + '/guide/README.md', + '/guide/getting-started.md', + '/guide/configuration.md', + '/guide/page.md', + '/guide/markdown.md', + '/guide/assets.md', + '/guide/i18n.md', + '/guide/deployment.md', + '/guide/theme.md', + '/guide/plugin.md', + '/guide/bundler.md', + '/guide/migration.md', + ], + }, + ], + '/advanced/': [ + { + text: 'Advanced', + children: [ + '/advanced/architecture.md', + '/advanced/plugin.md', + '/advanced/theme.md', + ], + }, + { + text: 'Cookbook', + children: [ + '/advanced/cookbook/README.md', + '/advanced/cookbook/usage-of-client-config.md', + '/advanced/cookbook/adding-extra-pages.md', + '/advanced/cookbook/making-a-theme-extendable.md', + '/advanced/cookbook/passing-data-to-client-code.md', + '/advanced/cookbook/markdown-and-vue-sfc.md', + ], + }, + ], + '/reference/': [ + { + text: 'VuePress Reference', + collapsible: true, + children: [ + '/reference/cli.md', + '/reference/config.md', + '/reference/frontmatter.md', + '/reference/components.md', + '/reference/plugin-api.md', + '/reference/theme-api.md', + '/reference/client-api.md', + '/reference/node-api.md', + ], + }, + { + text: 'Bundlers Reference', + collapsible: true, + children: ['/reference/bundler/vite.md', '/reference/bundler/webpack.md'], + }, + { + text: 'Default Theme Reference', + collapsible: true, + children: [ + '/reference/default-theme/config.md', + '/reference/default-theme/frontmatter.md', + '/reference/default-theme/components.md', + '/reference/default-theme/markdown.md', + '/reference/default-theme/styles.md', + '/reference/default-theme/extending.md', + ], + }, + { + text: 'Official Plugins Reference', + collapsible: true, + children: [ + { + text: 'Common Features', + children: [ + '/reference/plugin/back-to-top.md', + '/reference/plugin/container.md', + '/reference/plugin/external-link-icon.md', + '/reference/plugin/google-analytics.md', + '/reference/plugin/medium-zoom.md', + '/reference/plugin/nprogress.md', + '/reference/plugin/register-components.md', + ], + }, + { + text: 'Content Search', + children: [ + '/reference/plugin/docsearch.md', + '/reference/plugin/search.md', + ], + }, + { + text: 'PWA', + children: [ + '/reference/plugin/pwa.md', + '/reference/plugin/pwa-popup.md', + ], + }, + { + text: 'Syntax Highlighting', + children: [ + '/reference/plugin/prismjs.md', + '/reference/plugin/shiki.md', + ], + }, + { + text: 'Theme Development', + children: [ + '/reference/plugin/active-header-links.md', + '/reference/plugin/git.md', + '/reference/plugin/palette.md', + '/reference/plugin/theme-data.md', + '/reference/plugin/toc.md', + ], + }, + ], + }, + ], +} diff --git a/docs/.vuepress/configs/sidebar/index.ts b/docs/.vuepress/configs/sidebar/index.ts new file mode 100644 index 00000000..7183393c --- /dev/null +++ b/docs/.vuepress/configs/sidebar/index.ts @@ -0,0 +1,2 @@ +export * from './en.js' +export * from './zh.js' diff --git a/docs/.vuepress/configs/sidebar/zh.ts b/docs/.vuepress/configs/sidebar/zh.ts new file mode 100644 index 00000000..fc75e512 --- /dev/null +++ b/docs/.vuepress/configs/sidebar/zh.ts @@ -0,0 +1,129 @@ +import type { SidebarConfig } from '@vuepress/theme-default' + +export const sidebarZh: SidebarConfig = { + '/zh/guide/': [ + { + text: '指南', + children: [ + '/zh/guide/README.md', + '/zh/guide/getting-started.md', + '/zh/guide/configuration.md', + '/zh/guide/page.md', + '/zh/guide/markdown.md', + '/zh/guide/assets.md', + '/zh/guide/i18n.md', + '/zh/guide/deployment.md', + '/zh/guide/theme.md', + '/zh/guide/plugin.md', + '/zh/guide/bundler.md', + '/zh/guide/migration.md', + ], + }, + ], + '/zh/advanced/': [ + { + text: '深入', + children: [ + '/zh/advanced/architecture.md', + '/zh/advanced/plugin.md', + '/zh/advanced/theme.md', + ], + }, + { + text: 'Cookbook', + children: [ + '/zh/advanced/cookbook/README.md', + '/zh/advanced/cookbook/usage-of-client-config.md', + '/zh/advanced/cookbook/adding-extra-pages.md', + '/zh/advanced/cookbook/making-a-theme-extendable.md', + '/zh/advanced/cookbook/passing-data-to-client-code.md', + '/zh/advanced/cookbook/markdown-and-vue-sfc.md', + ], + }, + ], + '/zh/reference/': [ + { + text: 'VuePress 参考', + collapsible: true, + children: [ + '/zh/reference/cli.md', + '/zh/reference/config.md', + '/zh/reference/frontmatter.md', + '/zh/reference/components.md', + '/zh/reference/plugin-api.md', + '/zh/reference/theme-api.md', + '/zh/reference/client-api.md', + '/zh/reference/node-api.md', + ], + }, + { + text: '打包工具参考', + collapsible: true, + children: [ + '/zh/reference/bundler/vite.md', + '/zh/reference/bundler/webpack.md', + ], + }, + { + text: '默认主题参考', + collapsible: true, + children: [ + '/zh/reference/default-theme/config.md', + '/zh/reference/default-theme/frontmatter.md', + '/zh/reference/default-theme/components.md', + '/zh/reference/default-theme/markdown.md', + '/zh/reference/default-theme/styles.md', + '/zh/reference/default-theme/extending.md', + ], + }, + { + text: '官方插件参考', + collapsible: true, + children: [ + { + text: '常用功能', + children: [ + '/zh/reference/plugin/back-to-top.md', + '/zh/reference/plugin/container.md', + '/zh/reference/plugin/external-link-icon.md', + '/zh/reference/plugin/google-analytics.md', + '/zh/reference/plugin/medium-zoom.md', + '/zh/reference/plugin/nprogress.md', + '/zh/reference/plugin/register-components.md', + ], + }, + { + text: '内容搜索', + children: [ + '/zh/reference/plugin/docsearch.md', + '/zh/reference/plugin/search.md', + ], + }, + { + text: 'PWA', + children: [ + '/zh/reference/plugin/pwa.md', + '/zh/reference/plugin/pwa-popup.md', + ], + }, + { + text: '语法高亮', + children: [ + '/zh/reference/plugin/prismjs.md', + '/zh/reference/plugin/shiki.md', + ], + }, + { + text: '主题开发', + children: [ + '/zh/reference/plugin/active-header-links.md', + '/zh/reference/plugin/git.md', + '/zh/reference/plugin/palette.md', + '/zh/reference/plugin/theme-data.md', + '/zh/reference/plugin/toc.md', + ], + }, + ], + }, + ], +} diff --git a/docs/.vuepress/public/browserconfig.xml b/docs/.vuepress/public/browserconfig.xml new file mode 100644 index 00000000..5bdd109f --- /dev/null +++ b/docs/.vuepress/public/browserconfig.xml @@ -0,0 +1,9 @@ + +` tag or not. + + The wrapper is required by the `highlightLines` and `lineNumbers`. That means, if you disable `preWrapper`, the line highlighting and line numbers will also be disabled. + +::: tip +You can disable it if you want to implement them in client side. For example, [Prismjs Line Highlight](https://prismjs.com/plugins/line-highlight/) or [Prismjs Line Numbers](https://prismjs.com/plugins/line-numbers/). +::: + +#### markdown.code.vPre.block + +- Type: `boolean` + +- Default: `true` + +- Details: + + Add `v-pre` directive to `` tag of code block or not. + +- Also see: + - [Guide > Markdown > Syntax Extensions > Code Blocks > Wrap with v-pre](../guide/markdown.md#wrap-with-v-pre) + +#### markdown.code.vPre.inline + +- Type: `boolean` + +- Default: `true` + +- Details: + + Add `v-pre` directive to `` tag of inline code or not. + +- Also see: + - [Guide > Markdown > Syntax Extensions > Code Blocks > Wrap with v-pre](../guide/markdown.md#wrap-with-v-pre) + +### markdown.component + +- Type: `undefined | false` + +- Details: + + Options for [@mdit-vue/plugin-component](https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-component). + + Set to `false` to disable this plugin. + +::: danger +You should not configure it unless you understand what it is for. +::: + +### markdown.emoji + +- Type: `EmojiPluginOptions | false` + +- Details: + + Options for [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji). + + Set to `false` to disable this plugin. + +- Also see: + - [Guide > Markdown > Syntax Extensions > Emoji](../guide/markdown.md#emoji) + +### markdown.frontmatter + +- Type: `FrontmatterPluginOptions | false` + +- Details: + + Options for [@mdit-vue/plugin-frontmatter](https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-frontmatter). + + Set to `false` to disable this plugin. + +- Also see: + - [Guide > Page > Frontmatter](../guide/page.md#frontmatter) + - [Node API > Page Properties > frontmatter](./node-api.md#frontmatter) + +::: danger +You should not configure it unless you understand what it is for. +::: + +### markdown.headers + +- Type: `HeadersPluginOptions | false` + +- Default: + +```ts +const defaultOptions = { + level: [2, 3], +} +``` + +- Details: + + Options for [@mdit-vue/plugin-headers](https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-headers). + + Set to `false` to disable this plugin. + +- Also see: + - [Node API > Page Properties > headers](./node-api.md#headers) + +### markdown.importCode + +- Type: `ImportCodePluginOptions | false` + +- Details: + + Options for VuePress built-in markdown-it import-code plugin. + + Set to `false` to disable this plugin. + +- Also see: + - [Guide > Markdown > Syntax Extensions > Import Code Blocks](../guide/markdown.md#import-code-blocks) + +#### markdown.importCode.handleImportPath + +- Type: `(str: string) => string` + +- Default: `(str) => str` + +- Details: + + A function to handle the import path of the import code syntax. + +### markdown.links + +- Type: `LinksPluginOptions | false` + +- Details: + + Options for VuePress built-in markdown-it links plugin. + + It will convert internal links to `
`, and add extra attributes and icon to external links. + + Set to `false` to disable this plugin. + +- Also see: + - [Guide > Markdown > Syntax Extensions > Links](../guide/markdown.md#links) + +#### markdown.links.internalTag + +- Type: `'a' | 'RouterLink'` + +- Default: `'RouterLink'` + +- Details: + + Tag for internal links. + + By default, this plugin will transform internal links to ` `. You can set this option to `'a'` to disable this feature. + +#### markdown.links.externalAttrs + +- Type: `Record ` + +- Default: `{ target: '_blank', rel: 'noopener noreferrer' }` + +- Details: + + Additional attributes for external links. + +### markdown.sfc + +- Type: `SfcPluginOptions | false` + +- Details: + + Options for [@mdit-vue/plugin-sfc](https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-sfc). + + Set to `false` to disable this plugin. + +- Also see: + - [Cookbook > Markdown and Vue SFC](../advanced/cookbook/markdown-and-vue-sfc.md) + - [Node API > Page Properties > sfcBlocks](./node-api.md#sfcblocks) + +### markdown.slugify + +- Type: `(str: string) => string` + +- Details: + + The default slugify function. + +### markdown.title + +- Type: `undefined | false` + +- Details: + + Options for [@mdit-vue/plugin-title](https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-title). + + Set to `false` to disable this plugin. + +::: danger +You should not configure it unless you understand what it is for. +::: + +### markdown.toc + +- Type: `TocPluginOptions | false` + +- Default: + +```ts +const defaultOptions = { + level: [2, 3], +} +``` + +- Details: + + Options for [@mdit-vue/plugin-toc](https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-toc). + + Set to `false` to disable this plugin. + +- Also see: + - [Guide > Markdown > Syntax Extensions > Table of Contents](../guide/markdown.md#table-of-contents) + +## Plugin Config + +### plugins + +- Type: `(Plugin | Plugin[])[]` + +- Details: + + Plugins to use. + + This option accepts an array, each item of which could be a plugin or an array of plugins. + +- Also see: + - [Guide > Plugin](../guide/plugin.md) + +## Plugin API + +User config file also works as a VuePress plugin, so all of the Plugin APIs are available except the `name` and `multiple` options. + +Please check out [Plugin API Reference](./plugin-api.md) for a full list of Plugin APIs. diff --git a/docs/reference/default-theme/components.md b/docs/reference/default-theme/components.md new file mode 100644 index 00000000..931fb27b --- /dev/null +++ b/docs/reference/default-theme/components.md @@ -0,0 +1,124 @@ +# Built-in Components + + + +## Badge + +- Props: + - type + - Type: `'tip' | 'warning' | 'danger'` + - Default: `'tip'` + - text + - Type: `string` + - Default: `''` + - vertical + - Type: `'top' | 'middle' | 'bottom' | undefined` + - Default: `undefined` + +- Example: + +**Input** + +```md +- VuePress - +- VuePress - +- VuePress - +``` + +**Output** + +- VuePress - +- VuePress - +- VuePress - + +## CodeGroup + +- Details: + + Wrapper of the [CodeGroupItem](#codegroupitem) components. + +## CodeGroupItem + +- Props: + - title + - Type: `string` + - Required: `true` + - active + - Type: `boolean` + - Default: `false` + +- Details: + + This component must be placed inside a [CodeGroup](#codegroup) component. + + Use the `active` prop to set the initial active item, or the first item will be activated by default. + +- Example: + +**Input** + +````md + + +```` + +**Output** + ++ +```bash:no-line-numbers +pnpm install +``` + + + ++ +```bash:no-line-numbers +yarn install +``` + + + ++ +```bash:no-line-numbers +npm install +``` + + ++ + +::: warning +You must add an empty line between the starting tag of `+ +```bash:no-line-numbers +pnpm install +``` + + + ++ +```bash:no-line-numbers +yarn install +``` + + + ++ +```bash:no-line-numbers +npm install +``` + + +` and the code fence, otherwise the code fence will not be parsed correctly by Markdown. + +All content must be valid Markdown first, and then a Vue SFC. + +Learn more: [Cookbook > Markdown and Vue SFC](../../advanced/cookbook/markdown-and-vue-sfc.md) + +Alternatively, you can use the [custom containers](./markdown.md#custom-containers). +::: diff --git a/docs/reference/default-theme/config.md b/docs/reference/default-theme/config.md new file mode 100644 index 00000000..c34bc9ac --- /dev/null +++ b/docs/reference/default-theme/config.md @@ -0,0 +1,743 @@ +# Config + + + +```ts +import { defaultTheme } from '@vuepress/theme-default' + +export default { + theme: defaultTheme({ + // set config here + }), +} +``` + +## Basic Config + +### locales + +- Type: `{ [path: string]: Partial }` + +- Default: `{}` + +- Details: + + Specify locales for i18n support. + + All the options inside the [Locale Config](#locale-config) section can be used in locales. + + This option will only take effect in default theme, so don't confuse with `locales` in [Site Config](../config.md#locales). + +- Also see: + - [Guide > I18n](../../guide/i18n.md) + +## Locale Config + +Config of this section can be used as normal config, and can also be used in the [locales](#locales) option. + +### colorMode + +- Type: `'auto' | 'light' | 'dark'` + +- Default: `'auto'` + +- Details: + + Default color mode. + + If set to `'auto'`, the initial color mode will be automatically set according to [prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). + +- Also see: + - [Default Theme > Config > colorModeSwitch](#colormodeswitch) + +### colorModeSwitch + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable color mode switching or not. + + If set to `true`, a button to switch color mode will be displayed in the navbar. + +- Also see: + - [Default Theme > Config > colorMode](#colormode) + - [Default Theme > Config > toggleColorMode](#togglecolormode) + +### home + +- Type: `string` + +- Default: `/` + +- Details: + + Specify the path of the homepage. + + This will be used for: + + - the logo link of the navbar + - the _back to home_ link of the 404 page + +### navbar + +- Type: `false | (NavbarItem | NavbarGroup | string)[]` + +- Default: `[]` + +- Details: + + Configuration of navbar. + + Set to `false` to disable navbar. + + To configure the navbar items, you can set it to a _navbar array_, each item of which could be a `NavbarItem` object, a `NavbarGroup` object, or a string: + + - A `NavbarItem` object should have a `text` field and a `link` field, could have an optional `activeMatch` field. + - A `NavbarGroup` object should have a `text` field and a `children` field. The `children` field should be a _navbar array_, too. + - A string should be the path to the target page file. It will be converted to a `NavbarItem` object, using the page title as `text`, and the page route path as `link`. + +- Example 1: + +```ts +export default { + theme: defaultTheme({ + navbar: [ + // NavbarItem + { + text: 'Foo', + link: '/foo/', + }, + // NavbarGroup + { + text: 'Group', + children: ['/group/foo.md', '/group/bar.md'], + }, + // string - page file path + '/bar/README.md', + ], + }), +} +``` + +- Example 2: + +```ts +export default { + theme: defaultTheme({ + navbar: [ + // nested group - max depth is 2 + { + text: 'Group', + children: [ + { + text: 'SubGroup', + children: ['/group/sub/foo.md', '/group/sub/bar.md'], + }, + ], + }, + // control when should the item be active + { + text: 'Group 2', + children: [ + { + text: 'Always active', + link: '/', + // this item will always be active + activeMatch: '/', + }, + { + text: 'Active on /foo/', + link: '/not-foo/', + // this item will be active when current route path starts with /foo/ + // regular expression is supported + activeMatch: '^/foo/', + }, + ], + }, + ], + }), +} +``` + +### logo + +- Type: `null | string` + +- Details: + + Specify the url of logo image. + + The logo image will be displayed at the left end of the navbar. + + Set to `null` to disable logo. + +- Example: + +```ts +export default { + theme: defaultTheme({ + // public file path + logo: '/hero.png', + // url + logo: 'https://vuejs.org/images/logo.png', + }), +} +``` + +- Also see: + - [Guide > Assets > Public Files](../../guide/assets.md#public-files) + +### logoDark + +- Type: `null | string` + +- Details: + + Specify the url of logo image to be used in dark mode. + + You can make use of this option if you want to use different logo config in dark mode. + + Set to `null` to disable logo in dark mode. Omit this option to use [logo](#logo) in dark mode. + +- Also see: + - [Default Theme > Config > logo](#logo) + - [Default Theme > Config > colorMode](#colormode) + +### repo + +- Type: `string` + +- Details: + + Specify the repository url of your project. + + This will be used as the link of the _repository link_, which will be displayed as the last item of the navbar. + +```ts +export default { + theme: defaultTheme({ + // If you set it in the form of `organization/repository` + // we will take it as a GitHub repo + repo: 'vuejs/vuepress', + // You can also set it to a URL directly + repo: 'https://gitlab.com/foo/bar', + }), +} +``` + +### repoLabel + +- Type: `string` + +- Details: + + Specify the repository label of your project. + + This will be used as the text of the _repository link_, which will be displayed as the last item of the navbar. + + If you don't set this option explicitly, it will be automatically inferred from the [repo](#repo) option. + +### selectLanguageText + +- Type: `string` + +- Details: + + Specify the text of the _select language menu_. + + The _select language menu_ will appear next to the repository button in the navbar when you set multiple [locales](../config.md#locales) in your site config. + +### selectLanguageAriaLabel + +- Type: `string` + +- Details: + + Specify the `aria-label` attribute of the _select language menu_. + + This is mainly for a11y purpose. + +### selectLanguageName + +- Type: `string` + +- Details: + + Specify the name of the language of a locale. + + This option will **only take effect inside** the [locales](#locales) of your theme config. It will be used as the language name of the locale, which will be displayed in the _select language menu_. + +- Example: + +```ts +export default { + locales: { + '/': { + lang: 'en-US', + }, + '/zh/': { + lang: 'zh-CN', + }, + }, + theme: defaultTheme({ + locales: { + '/': { + selectLanguageName: 'English', + }, + '/zh/': { + selectLanguageName: '简体中文', + }, + }, + }), +} +``` + +### sidebar + +- Type: `false | 'auto' | SidebarConfigArray | SidebarConfigObject` + +- Default: `'auto'` + +- Details: + + Configuration of sidebar. + + You can override this global option via [sidebar](./frontmatter.md#sidebar) frontmatter in your pages. + + Set to `false` to disable sidebar. + + If you set it to `'auto'`, the sidebar will be automatically generated from the page headers. + + To configure the sidebar items manually, you can set this option to a _sidebar array_, each item of which could be a `SidebarItem` object or a string: + + - A `SidebarItem` object should have a `text` field, could have an optional `link` field, an optional `children` field and an optional `collapsible` field. The `children` field should be a _sidebar array_. The `collapsible` field controls whether the item is collapsible. + - A string should be the path to the target page file. It will be converted to a `SidebarItem` object, whose `text` is the page title, `link` is the page route path, and `children` is automatically generated from the page headers. + + If you want to set different sidebar for different sub paths, you can set this option to a _sidebar object_: + + - The key should be the path prefix. + - The value should be a _sidebar array_. + +- Example 1: + +```ts +export default { + theme: defaultTheme({ + // sidebar array + // all pages will use the same sidebar + sidebar: [ + // SidebarItem + { + text: 'Foo', + link: '/foo/', + children: [ + // SidebarItem + { + text: 'github', + link: 'https://github.com', + children: [], + }, + // string - page file path + '/foo/bar.md', + ], + }, + // string - page file path + '/bar/README.md', + ], + }), +} +``` + +- Example 2: + +```ts +export default { + theme: defaultTheme({ + // sidebar object + // pages under different sub paths will use different sidebar + sidebar: { + '/guide/': [ + { + text: 'Guide', + children: ['/guide/README.md', '/guide/getting-started.md'], + }, + ], + '/reference/': [ + { + text: 'Reference', + children: ['/reference/cli.md', '/reference/config.md'], + }, + ], + }, + }), +} +``` + +- Example 3: + +```ts +export default { + theme: defaultTheme({ + // collapsible sidebar + sidebar: { + '/reference/': [ + { + text: 'VuePress Reference', + collapsible: true, + children: ['/reference/cli.md', '/reference/config.md'], + }, + { + text: 'Bundlers Reference', + collapsible: true, + children: ['/reference/bundler/vite.md', '/reference/bundler/webpack.md'], + }, + ], + }, + }), +} +``` + +### sidebarDepth + +- Type: `number` + +- Default: `2` + +- Details: + + Set the maximum depth of the sidebar children which are automatically generated from the page headers. + + - Set to `0` to disable all levels of headers. + - Set to `1` to include ` ` headers. + - Set to `2` to include `
` and `
` headers. + - ... + + The max value depends on which levels of headers you have extracted via [markdown.headers.level](../config.md#markdown-headers). + + The default value of `markdown.headers.level` is `[2, 3]`, so the default max value of `sidebarDepth` is `2`. + + You can override this global option via [sidebarDepth](./frontmatter.md#sidebardepth) frontmatter in your pages. + +### editLink + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable the _edit this page_ link or not. + + You can override this global option via [editLink](./frontmatter.md#editlink) frontmatter in your pages. + +### editLinkText + +- Type: `string` + +- Default: `'Edit this page'` + +- Details: + + Specify the text of the _edit this page_ link. + +### editLinkPattern + +- Type: `string` + +- Details: + + Specify the pattern of the _edit this page_ link. + + This will be used for generating the _edit this page_ link. + + If you don't set this option, the pattern will be inferred from the [docsRepo](#docsrepo) option. But if your documentation repository is not hosted on a common platform, for example, GitHub, GitLab, Bitbucket, Gitee, etc., you have to set this option explicitly to make the _edit this page_ link work. + +- Usage: + + | Pattern | Description | + | --------- | --------------------------------------------------------------------------------------------------- | + | `:repo` | The docs repo url, i.e. [docsRepo](#docsrepo) | + | `:branch` | The docs repo branch, i.e. [docsBranch](#docsbranch) | + | `:path` | The path of the page source file, i.e. [docsDir](#docsdir) joins the relative path of the page file | + +- Example: + +```ts +export default { + theme: defaultTheme({ + docsRepo: 'https://gitlab.com/owner/name', + docsBranch: 'master', + docsDir: 'docs', + editLinkPattern: ':repo/-/edit/:branch/:path', + }), +} +``` + +The generated link will look like `'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md'`. + +### docsRepo + +- Type: `string` + +- Details: + + Specify the repository url of your documentation source files. + + This will be used for generating the _edit this page_ link. + + If you don't set this option, it will use the [repo](#repo) option by default. But if your documentation source files are in a different repository, you will need to set this option. + +### docsBranch + +- Type: `string` + +- Default: `'main'` + +- Details: + + Specify the repository branch of your documentation source files. + + This will be used for generating the _edit this page_ link. + +### docsDir + +- Type: `string` + +- Default: `''` + +- Details: + + Specify the directory of your documentation source files in the repository. + + This will be used for generating the _edit this page_ link. + +### lastUpdated + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable the _last updated timestamp_ or not. + + You can override this global option via [lastUpdated](./frontmatter.md#lastupdated) frontmatter in your pages. Notice that if you have already set this option to `false`, this feature will be disabled totally and could not be enabled in locales nor page frontmatter. + +### lastUpdatedText + +- Type: `string` + +- Default: `'Last Updated'` + +- Details: + + Specify the text of the _last updated timestamp_ label. + +### contributors + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable the _contributors list_ or not. + + You can override this global option via [contributors](./frontmatter.md#contributors) frontmatter in your pages. Notice that if you have already set this option to `false`, this feature will be disabled totally and could not be enabled in locales nor page frontmatter. + +### contributorsText + +- Type: `string` + +- Default: `'Contributors'` + +- Details: + + Specify the text of the _contributors list_ label. + +### tip + +- Type: `string` + +- Default: `'TIP'` + +- Details: + + Specify the default title of the tip [custom containers](./markdown.md#custom-containers). + +### warning + +- Type: `string` + +- Default: `'WARNING'` + +- Details: + + Specify the default title of the warning [custom containers](./markdown.md#custom-containers). + +### danger + +- Type: `string` + +- Default: `'DANGER'` + +- Details: + + Specify the default title of the danger [custom containers](./markdown.md#custom-containers). + +### notFound + +- Type: `string[]` + +- Default: `['Not Found']` + +- Details: + + Specify the messages of the 404 page. + + The message will be randomly picked from the array when users enter the 404 page. + +### backToHome + +- Type: `string` + +- Default: `'Back to home'` + +- Details: + + Specify the text of the _back to home_ link in the 404 page. + +### openInNewWindow + +- Type: `string` + +- Default: `'open in new window'` + +- Details: + + Specify the `sr-only` text of the [ExternalLinkIcon](../plugin/external-link-icon.md#externallinkicon). + + This is mainly for a11y purpose. + +- Also see: + - [Default Theme > Config Reference > themePlugins.externalLinkIcon](#themeplugins-externallinkicon) + +### toggleColorMode + +- Type: `string` + +- Default: `'toggle color mode'` + +- Details: + + Title text for the color mode toggle button. + + This is mainly for a11y purpose. + +- Also see: + - [Default Theme > Config > colorModeSwitch](#colormodeswitch) + +### toggleSidebar + +- Type: `string` + +- Default: `'toggle sidebar'` + +- Details: + + Title text for sidebar toggle button. + + This is mainly for a11y purpose. + +## Plugins Config + +### themePlugins + +- Details: + + Configure the plugins that used by default theme. + + Default theme is using some plugins by default. You can disable a plugin if you really do not want to use it. Make sure you understand what the plugin is for before disabling it. + +### themePlugins.activeHeaderLinks + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable [@vuepress/plugin-active-header-links](../plugin/active-header-links.md) or not. + +### themePlugins.backToTop + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable [@vuepress/plugin-back-to-top](../plugin/back-to-top.md) or not. + +### themePlugins.container + +- Type: `Record
` + +- Details: + + Enable custom containers that powered by [@vuepress/plugin-container](../plugin/container.md) or not. + + `ContainerType` type is: + + - `tip` + - `warning` + - `danger` + - `details` + - `codeGroup` + - `codeGroupItem` + +- Also see: + - [Default Theme > Markdown > Custom Containers](./markdown.md#custom-containers) + +### themePlugins.externalLinkIcon + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable [@vuepress/plugin-external-link-icon](../plugin/external-link-icon.md) or not. + +### themePlugins.git + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable [@vuepress/plugin-git](../plugin/git.md) or not. + +### themePlugins.mediumZoom + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable [@vuepress/plugin-medium-zoom](../plugin/medium-zoom.md) or not. + +### themePlugins.nprogress + +- Type: `boolean` + +- Default: `true` + +- Details: + + Enable [@vuepress/plugin-nprogress](../plugin/nprogress.md) or not. diff --git a/docs/reference/default-theme/extending.md b/docs/reference/default-theme/extending.md new file mode 100644 index 00000000..ffa3b5fb --- /dev/null +++ b/docs/reference/default-theme/extending.md @@ -0,0 +1,113 @@ +# Extending + +VuePress default theme is widely used by users, so it is designed to be extendable, allowing users to make their own customization with ease. + +VuePress provides basic ability to extend a theme, but different themes may have different features to be extended. Thus, if you are using a community theme, you'd better refer to the theme's own documentation for how to extending it. + +## Layout Slots + +Default theme's `Layout` provides some slots: + +- `navbar` +- `navbar-before` +- `navbar-after` +- `sidebar` +- `sidebar-top` +- `sidebar-bottom` +- `page` +- `page-top` +- `page-bottom` +- `page-content-top` +- `page-content-bottom` + +With the help of them, you can add or replace content easily. Here comes an example to introduce how to extend default theme with layout slots. + +Firstly, create a client config file `.vuepress/client.ts`: + +```ts +import { defineClientConfig } from '@vuepress/client' +import Layout from './layouts/Layout.vue' + +export default defineClientConfig({ + layouts: { + Layout, + }, +}) +``` + +Next, create the `.vuepress/layouts/Layout.vue`, and make use of the slots that provided by the `Layout` of default theme: + +```vue + + + + + + + + + +``` + +Then the default `Layout` layout has been overridden by your own local layout, which will add a custom footer to every normal pages in default theme (excluding homepage): + +![extending-a-theme](/images/cookbook/extending-a-theme-01.png) + +## Components Replacement + +The layout slots are useful, but sometimes you might find it's not flexible enough. Default theme also provides the ability to replace a single component. + +Default theme has registered [alias](../plugin-api.md#alias) for every [non-global components](https://github.com/vuepress/ecosystem/tree/main/themes/theme-default/src/client/components) with a `@theme` prefix. For example, the alias of `HomeFooter.vue` is `@theme/HomeFooter.vue`. + +Then, if you want to replace the `HomeFooter.vue` component, just override the alias in your config file `.vuepress/config.ts`: + +```ts +import { getDirname, path } from '@vuepress/utils' +import { defaultTheme, defineUserConfig } from 'vuepress' + +const __dirname = getDirname(import.meta.url) + +export default defineUserConfig({ + theme: defaultTheme(), + alias: { + '@theme/HomeFooter.vue': path.resolve(__dirname, './components/MyHomeFooter.vue'), + }, +}) +``` + +## Developing a Child Theme + +Instead of extending the default theme directly in `.vuepress/config.ts` and `.vuepress/client.ts`, you can also develop your own theme extending the default theme: + +```ts +import type { Theme } from '@vuepress/core' +import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default' +import { getDirname, path } from '@vuepress/utils' + +const __dirname = getDirname(import.meta.url) + +export const childTheme = (options: DefaultThemeOptions): Theme => { + return { + name: 'vuepress-theme-child', + extends: defaultTheme(options), + + // override layouts in child theme's client config file + // notice that you would build ts to js before publishing to npm, + // so this should be the path to the js file + clientConfigFile: path.resolve(__dirname, './client.js'), + + // override component alias + alias: { + '@theme/HomeFooter.vue': path.resolve(__dirname, './components/MyHomeFooter.vue'), + }, + } +} +``` diff --git a/docs/reference/default-theme/frontmatter.md b/docs/reference/default-theme/frontmatter.md new file mode 100644 index 00000000..addc6cca --- /dev/null +++ b/docs/reference/default-theme/frontmatter.md @@ -0,0 +1,360 @@ +# Frontmatter + + + ++ +## All Pages + +Frontmatter in this section will take effect in all types of pages. + +### externalLinkIcon + +- Type: `boolean` + +- Details: + + Provided by [@vuepress/plugin-external-link-icon](../plugin/external-link-icon.md#externallinkicon). + +- Also see: + - [Default Theme > Config Reference > themePlugins.externalLinkIcon](./config.md#themeplugins-externallinkicon) + +### navbar + +- Type: `boolean` + +- Details: + + Show navbar on this page or not. + + If you disable navbar in theme config, this frontmatter will not take effect. + +- Also see: + - [Default Theme > Config > navbar](./config.md#navbar) + +### pageClass + +- Type: `string` + +- Details: + + Add extra class name to this page. + +- Example: + +```md +--- +pageClass: custom-page-class +--- +``` + +Then you can customize styles of this page in `.vuepress/styles/index.scss` file: + +```scss +.theme-container.custom-page-class { + /* page styles */ +} +``` + +- Also see: + - [Default Theme > Styles > Style File](./styles.md#style-file) + +## Home Page + +Frontmatter in this section will only take effect in home pages. + +### home + +- Type: `boolean` + +- Details: + + Specify whether the page is homepage or a normal page. + + If you don't set this frontmatter or set it to `false`, the page would be a [normal page](#normal-page). + +- Example: + +```md +--- +home: true +--- +``` + +### heroImage + +- Type: `string` + +- Details: + + Specify the url of the hero image. + +- Example: + +```md +--- +# public file path +heroImage: /images/hero.png +# url +heroImage: https://vuejs.org/images/logo.png +--- +``` + +- Also see: + - [Guide > Assets > Public Files](../../guide/assets.md#public-files) + +### heroImageDark + +- Type: `string` + +- Details: + + Specify the url of hero image to be used in dark mode. + + You can make use of this option if you want to use different heroImage config in dark mode. + +- Also see: + - [Default Theme > Frontmatter > heroImage](#heroimage) + - [Default Theme > Config > colorMode](./config.md#colormode) + +### heroAlt + +- Type: `string` + +- Details: + + Specify the `alt` attribute of the hero image. + + This will fallback to the [heroText](#herotext). + +### heroHeight + +- Type: `number` + +- Default: `280` + +- Details: + + Specify the `height` attribute of the hero `` tag. + + You may need to reduce this value if the height of your hero image is less than the default value. + + Notice that the height is also constrained by CSS. This attribute is to reduce [Cumulative Layout Shift (CLS)](https://web.dev/cls/) that caused by the loading of the hero image. + +### heroText + +- Type: `string | null` + +- Details: + + Specify the the hero text. + + This will fallback to the site [title](../config.md#title). + + Set to `null` to disable hero text. + +### tagline + +- Type: `string | null` + +- Details: + + Specify the the tagline. + + This will fallback to the site [description](../config.md#description). + + Set to `null` to disable tagline. + +### actions + +- Type: + +```ts +Array<{ + text: string + link: string + type?: 'primary' | 'secondary' +}> +``` + +- Details: + + Configuration of the action buttons. + +- Example: + +```md +--- +actions: + - text: Get Started + link: /guide/getting-started.html + type: primary + - text: Introduction + link: /guide/ + type: secondary +--- +``` + +### features + +- Type: + +```ts +Array<{ + title: string + details: string +}> +``` + +- Details: + + Configuration of the features list. + +- Example: + +```md +--- +features: + - title: Simplicity First + details: Minimal setup with markdown-centered project structure helps you focus on writing. + - title: Vue-Powered + details: Enjoy the dev experience of Vue, use Vue components in markdown, and develop custom themes with Vue. + - title: Performant + details: VuePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded. +--- +``` + +### footer + +- Type: `string` + +- Details: + + Specify the content of the footer. + +### footerHtml + +- Type: `boolean` + +- Details: + + Allow HTML in footer or not. + + If you set it to `true`, the [footer](#footer) will be treated as HTML code. + +## Normal Page + +Frontmatter in this section will only take effect in normal pages. + +### editLink + +- Type: `boolean` + +- Details: + + Enable the _edit this page_ link in this page or not. + +- Also see: + - [Default Theme > Config > editLink](./config.md#editlink) + +### editLinkPattern + +- Type: `string` + +- Details: + + Specify the pattern of the _edit this page_ link of this page. + +- Also see: + - [Default Theme > Config > editLinkPattern](./config.md#editlinkpattern) + +### lastUpdated + +- Type: `boolean` + +- Details: + + Enable the _last updated timestamp_ in this page or not. + +- Also see: + - [Default Theme > Config > lastUpdated](./config.md#lastupdated) + +### contributors + +- Type: `boolean` + +- Details: + + Enable the _contributors list_ in this page or not. + +- Also see: + - [Default Theme > Config > contributors](./config.md#contributors) + +### sidebar + +- Type: `false | 'auto' | SidebarConfigArray | SidebarConfigObject` + +- Details: + + Configure the sidebar of this page. + +- Also see: + - [Default Theme > Config > sidebar](./config.md#sidebar) + +### sidebarDepth + +- Type: `number` + +- Details: + + Configure the sidebar depth of this page. + +- Also see: + - [Default Theme > Config > sidebarDepth](./config.md#sidebardepth) + +### prev + +- Type: `NavLink | string` + +- Details: + + Specify the link of the previous page. + + If you don't set this frontmatter, the link will be inferred from the sidebar config. + + To configure the prev link manually, you can set this frontmatter to a `NavLink` object or a string: + + - A `NavLink` object should have a `text` field and a `link` field. + - A string should be the path to the target page file. It will be converted to a `NavLink` object, whose `text` is the page title, and `link` is the page route path. + +- Example: + +```md +--- +# NavLink +prev: + text: Get Started + link: /guide/getting-started.html + +# NavLink - external url +prev: + text: GitHub + link: https://github.com + +# string - page file path +prev: /guide/getting-started.md + +# string - page file relative path +prev: ../../guide/getting-started.md +--- +``` + +### next + +- Type: `NavLink | string` + +- Details: + + Specify the link of the next page. + + If you don't set this frontmatter, the link will be inferred from the sidebar config. + + The type is the same as [prev](#prev) frontmatter. diff --git a/docs/reference/default-theme/markdown.md b/docs/reference/default-theme/markdown.md new file mode 100644 index 00000000..09c31d66 --- /dev/null +++ b/docs/reference/default-theme/markdown.md @@ -0,0 +1,126 @@ +# Markdown + + + +## Custom Containers + +- Usage: + + ```md + ::: [title] + [content] + ::: + ``` + + The `type` is required, and the `title` and `content` are optional. + + Supported `type` : + - `tip` + - `warning` + - `danger` + - `details` + - Alias of [CodeGroup](./components.md#codegroup) and [CodeGroupItem](./components.md#codegroupitem): + - `code-group` + - `code-group-item` + +- Example 1 (default title): + +**Input** + +```md +::: tip +This is a tip +::: + +::: warning +This is a warning +::: + +::: danger +This is a dangerous warning +::: + +::: details +This is a details block +::: +``` + +**Output** + +::: tip +This is a tip +::: + +::: warning +This is a warning +::: + +::: danger +This is a dangerous warning +::: + +::: details +This is a details block +::: + +- Example 2 (custom title): + +**Input** + +````md +::: danger STOP +Danger zone, do not proceed +::: + +::: details Click me to view the code +```ts +console.log('Hello, VuePress!') +``` +::: +```` + +**Output** + +::: danger STOP +Danger zone, do not proceed +::: + +::: details Click me to view the code +```ts +console.log('Hello, VuePress!') +``` +::: + +- Example 3 (code group alias): + +**Input** + +````md +:::: code-group +::: code-group-item FOO +```ts +const foo = 'foo' +``` +::: +::: code-group-item BAR +```ts +const bar = 'bar' +``` +::: +:::: +```` + +**Output** + +:::: code-group +::: code-group-item FOO +```ts +const foo = 'foo' +``` +::: +::: code-group-item BAR +```ts +const bar = 'bar' +``` +::: +:::: diff --git a/docs/reference/default-theme/styles.md b/docs/reference/default-theme/styles.md new file mode 100644 index 00000000..02cce493 --- /dev/null +++ b/docs/reference/default-theme/styles.md @@ -0,0 +1,40 @@ +# Styles + + + +The default theme uses [SASS](https://sass-lang.com/) as the CSS pre-processor. + +Users can customize style variables via a [palette file](#palette-file), +and add extra styles via a [style file](#style-file). + +## Palette File + +The path of the palette file is `.vuepress/styles/palette.scss`. + +You can make use of it to override predefined SASS variables of the default theme. + +::: details Click to expand SASS variables +@[code{3-} scss](@vuepress/theme-default/src/client/styles/_variables.scss) +::: + +## Style File + +The path of the style file is `.vuepress/styles/index.scss`. + +You can add extra styles here, or override the default styles: + +```scss +:root { + scroll-behavior: smooth; +} +``` + +You can also make use of it to override predefined CSS variables of the default theme. + +::: details Click to expand CSS variables +@[code scss](@vuepress/theme-default/src/client/styles/vars.scss) +::: + +::: details Click to expand dark mode CSS variables +@[code scss](@vuepress/theme-default/src/client/styles/vars-dark.scss) +::: diff --git a/docs/reference/frontmatter.md b/docs/reference/frontmatter.md new file mode 100644 index 00000000..36020eb8 --- /dev/null +++ b/docs/reference/frontmatter.md @@ -0,0 +1,214 @@ +# Frontmatter + + + + +## date + +- Type: `string` + +- Details: + + Created date for the page. + + You should specify the date in the form of `yyyy-MM-dd`, or follow the [YAML Timestamp Type](https://yaml.org/type/timestamp.html). + +- Also see: + - [Node API > Page Properties > date](./node-api.md#date) + +## description + +- Type: `string` + +- Details: + + Description for the page. + + This will override the `description` option in your site config. + +- Also see: + - [Config > description](./config.md#description) + +## head + +- Type: `HeadConfig[]` + +- Details: + + Extra tags in `` tag for the page. + +- Example: + +```md +--- +head: + - - meta + - name: foo + content: yaml array syntax + - [meta, { name: bar , content: square brackets syntax }] +--- +``` + + Rendered as: + +```html + + + + +``` + +- Also see: + - [Config > head](./config.md#head) + +## lang + +- Type: `string` + +- Details: + + Language for the page. + + This will override the `lang` option in your site config. + +- Also see: + - [Config > lang](./config.md#lang) + - [Node API > Page Properties > lang](./node-api.md#lang) + +## layout + +- Type: `string` + +- Details: + + Layout for the page. + + Layouts are provided by theme. If you don't specify this frontmatter, the default layout will be used. You should refer to the theme's own documentation to find what layouts it provides. + + If the theme layouts cannot meet your needs, you can use a custom layout component. + +- Example: + +Register a layout component in `.vuepress/client.ts` file: + +```ts +import { defineClientConfig } from '@vuepress/client' +import CustomLayout from './CustomLayout.vue' + +export default defineClientConfig({ + layouts: { + CustomLayout, + }, +}) +``` + +Set custom layout in frontmatter: + +```md +--- +layout: CustomLayout +--- +``` + +## permalink + +- Type: `string` + +- Details: + + Permalink for the page. + + This will override the default route path that determined by the file path of the page. + +- Also see: + - [Frontmatter > permalinkPattern](#permalinkpattern) + - [Guide > Page > Routing](../guide/page.md#routing) + - [Node API > Page Properties > permalink](./node-api.md#permalink) + +## permalinkPattern + +- Type: `string | null` + +- Details: + + Pattern to generate permalink for the page. + + This will override the `permalinkPattern` option in your site config. + + This won't take effect if the `permalink` frontmatter has been set. + +- Usage: + + | Pattern | Description | + |-----------|-----------------------------| + | `:year` | Year part of created date | + | `:month` | Month part of created date | + | `:day` | Day part of created date | + | `:slug` | Slug of page filename | + | `:raw` | Raw route path | + + The `:year`, `:month` and `:day` patterns are resolved according to the following priority: + + - The `date` frontmatter. + - The filename that matches the date pattern `yyyy-MM-dd-foobar.md` or `yyyy-MM-foobar.md`. + - The dirname that matches the date pattern `yyyy/MM/dd/foobar.md` or `yyyy/MM/foobar.md`. + - Fallback to `0000-00-00`. + +- Example 1: + + The page filename is `foo-bar.md`. + + The page frontmatter is: + +```md +--- +date: 2021-01-03 +permalinkPattern: :year/:month/:day/:slug.html +--- +``` + + Then the permalink of the page would be `2021/01/03/foo-bar.html`. + +- Example 2: + + The page filename is `2021-01-03-bar-baz.md`. + + The page frontmatter is: + +```md +--- +permalinkPattern: :year/:month/:day/:slug.html +--- +``` + + Then the permalink of the page would be `2021/01/03/bar-baz.html`. + +- Also see: + - [Config > permalinkPattern](./config.md#permalinkpattern) + - [Frontmatter > date](#date) + - [Frontmatter > permalink](#permalink) + - [Node API > Page Properties > permalink](./node-api.md#permalink) + +## routeMeta + +- Type: `Record ` + +- Details: + + Custom data to be attached to the page route. + +- Also see: + - [Node API > Page Properties > routeMeta](./node-api.md#routeMeta) + +## title + +- Type: `string` + +- Details: + + Title for the page. + + If you don't specify `title` in frontmatter, content of the first level-one header (i.e. `# title`) will be used as the title. + +- Also see: + - [Node API > Page Properties > title](./node-api.md#title) diff --git a/docs/reference/node-api.md b/docs/reference/node-api.md new file mode 100644 index 00000000..5fb978a9 --- /dev/null +++ b/docs/reference/node-api.md @@ -0,0 +1,665 @@ +# Node API + + + +Node API is provided by [@vuepress/core](https://www.npmjs.com/package/@vuepress/core) package. It is a dependency of the [vuepress](https://www.npmjs.com/package/vuepress) package, and you can also install it separately: + +```bash +npm i -D @vuepress/core@next +``` + +## App + +The app instance is available in all hooks of [Plugin API](./plugin-api.md). + +The `BuildApp` and `DevApp` share almost the same properties and methods, except [build](#build) and [dev](#dev) method. + +### createBuildApp + +- Signature: + +```ts +const createBuildApp: (config: AppConfig) => BuildApp +``` + +- Parameters: + +| Parameter | Type | Description | +| --------- | ----------- | -------------------------------- | +| config | `AppConfig` | Config to create a VuePress app. | + +- Details: + + Create a build mode app instance, which is used for building static files. + +- Example: + +```ts +const build = async () => { + const app = createBuildApp({ + // ...options + }) + + // initialize and prepare + await app.init() + await app.prepare() + + // build + await app.build() + + // process onGenerated hook + await app.pluginApi.hooks.onGenerated.process(app) +} +``` + +- Also see: + - [Node API > App Methods > build](#build) + +### createDevApp + +- Signature: + +```ts +const createDevApp: (config: AppConfig) => DevApp +``` + +- Parameters: + +| Parameter | Type | Description | +| --------- | ----------- | -------------------------------- | +| config | `AppConfig` | Config to create a VuePress app. | + +- Details: + + Create a dev mode app instance, which is used for starting a dev server. + +- Example: + +```ts +const dev = async () => { + const app = createDevApp({ + // ...options + }) + + // initialize and prepare + await app.init() + await app.prepare() + + // start dev server + const closeDevServer = await app.dev() + + // set up file watchers + const watchers = [] + + // restart dev server + const restart = async () => { + await Promise.all([ + // close all watchers + ...watchers.map((item) => item.close()), + // close current dev server + closeDevServer(), + ]) + await dev() + } + + // process onWatched hook + await app.pluginApi.hooks.onWatched.process(app, watchers, restart) +} +``` + +- Also see: + - [Node API > App Methods > dev](#dev) + +## App Properties + +### options + +- Type: `AppOptions` + +- Details: + + Options of VuePress app. + + The options come from the `config` argument in [createBuildApp](#createbuildapp) / [createDevApp](#createdevapp), while all optional fields will be filled with a default value. + +### siteData + +- Type: `SiteData` + +- Details: + + Site data that set by user, including all the [site config](./config.md#site-config), which will be used in client side. + +### version + +- Type: `string` + +- Details: + + Version of VuePress app, i.e. version of `@vuepress/core` package. + +### env.isBuild + +- Type: `boolean` + +- Details: + + Environment flag used to identify whether the app is running in build mode, i.e. whether a [BuildApp](#createbuildapp) instance. + +### env.isDev + +- Type: `boolean` + +- Details: + + Environment flag used to identify whether the app is running in dev mode, i.e. whether a [DevApp](#createdevapp) instance. + +### env.isDebug + +- Type: `boolean` + +- Details: + + Environment flag used to identify whether the debug mode is enabled. + +### markdown + +- Type: `MarkdownIt` + +- Details: + + The [markdown-it](https://github.com/markdown-it/markdown-it) instance used for parsing markdown content. + + It is only available in and after [onInitialized](./plugin-api.md#oninitialized) hook. + +### pages + +- Type: `Page[]` + +- Details: + + The [Page](#page) object array. + + It is only available in and after [onInitialized](./plugin-api.md#oninitialized) hook. + +## App Methods + +### dir + +- Utils: + + - `dir.cache()`: resolve to cache directory + - `dir.temp()`: resolve to temp directory + - `dir.source()`: resolve to source directory + - `dir.dest()`: resolve to dest directory + - `dir.client()`: resolve to `@vuepress/client` directory + - `dir.public()`: resolve to public directory + +- Signature: + +```ts +type AppDirFunction = (...args: string[]) => string +``` + +- Details: + + Utils to resolve the absolute file path in corresponding directory. + + If you don't provide any arguments, it will return the absolute path of the directory. + +- Example: + +```ts +// resolve the absolute file path of the `${sourceDir}/README.md` +const homeSourceFile = app.dir.source('README.md') +``` + +### writeTemp + +- Signature: + +```ts +writeTemp(file: string, content: string): Promise +``` + +- Parameters: + +| Parameter | Type | Description | +| --------- | -------- | ------------------------------------------------------------------------------- | +| file | `string` | Filepath of the temp file that going to be written. Relative to temp directory. | +| content | `string` | Content of the temp file that going to be written. | + +- Details: + + A method to write temp file. + + The written file could be imported via `@temp` alias in client files. + +- Example: + +```ts +export default { + // write temp file in onPrepared hook + async onPrepared() { + await app.writeTemp('foo.js', "export const foo = 'bar'") + }, +} +``` + +```ts +// import temp file in client code +import { foo } from '@temp/foo' +``` + +### init + +- Signature: + +```ts +init(): Promise +``` + +- Details: + + Initialize VuePress app. + +- Also see: + - [Advanced > Architecture > Core Process and Hooks](../advanced/architecture.md#core-process-and-hooks) + +### prepare + +- Signature: + +```ts +prepare(): Promise +``` + +- Details: + + Prepare client temp files. + +- Also see: + - [Advanced > Architecture > Core Process and Hooks](../advanced/architecture.md#core-process-and-hooks) + +### build + +- Signature: + +```ts +build(): Promise +``` + +- Details: + + Generate static site files. + + This method is only available in `BuildApp`. + +- Also see: + - [Node API > App > createBuildApp](#createbuildapp) + - [Advanced > Architecture > Core Process and Hooks](../advanced/architecture.md#core-process-and-hooks) + +### dev + +- Signature: + +```ts +dev(): Promise<() => Promise > +``` + +- Details: + + Start dev server. + + This method is only available in `DevApp`. + +- Also see: + - [Node API > App > createDevApp](#createdevapp) + - [Advanced > Architecture > Core Process and Hooks](../advanced/architecture.md#core-process-and-hooks) + +## Page + +### createPage + +- Signature: + +```ts +const createPage: (app: App, options: PageOptions) => Promise +``` + +- Parameters: + +| Parameter | Type | Description | +| --------- | ------------- | -------------------------------- | +| app | `App` | The VuePress app instance. | +| options | `PageOptions` | Options to create VuePress page. | + +- Details: + + Create a VuePress page object. + +- Example: + +```ts +import { createPage } from '@vuepress/core' + +export default { + // create an extra page in onInitialized hook + async onInitialized(app) { + app.pages.push( + await createPage(app, { + path: '/foo.html', + frontmatter: { + layout: 'Layout', + }, + content: `\ +# Foo Page + +Hello, world. +`, + }) + ) + }, +} +``` + +- Also see: + - [Node API > App Properties > pages](#pages) + - [Cookbook > Adding Extra Pages](../advanced/cookbook/adding-extra-pages.md) + +## Page Properties + +### key + +- Type: `string` + +- Details: + + Identifier of the page. + + The page key would be used as the [name](https://router.vuejs.org/api/#name-2) of the page route. + +- Also see: + - [Built-in Components > Content](./components.md#content) + +### path + +- Type: `string` + +- Details: + + Route path of the page. + +- Also see: + - [Guide > Page > Routing](../guide/page.md#routing) + - [Node API > Page Properties > pathInferred](#pathinferred) + +### title + +- Type: `string` + +- Details: + + Title of the page. + +- Also see: + - [Frontmatter > title](./frontmatter.md#title) + +### lang + +- Type: `string` + +- Details: + + Language of the page. + +- Example: + + - `'en-US'` + - `'zh-CN'` + +- Also see: + - [Frontmatter > lang](./frontmatter.md#title) + +### frontmatter + +- Type: `PageFrontmatter` + +- Details: + + Frontmatter of the page. + +- Also see: + - [Frontmatter](./frontmatter.md) + +### headers + +- Type: `PageHeader[]` + +```ts +interface PageHeader { + level: number + title: string + slug: string + children: PageHeader[] +} +``` + +- Details: + + Headers of the page. + +- Also see: + - [Config > markdown.headers](./config.md#markdown-headers) + +### data + +- Type: `PageData` + +```ts +interface PageData { + key: string + path: string + title: string + lang: string + frontmatter: PageFrontmatter + headers: PageHeader[] +} +``` + +- Details: + + Data of the page. + + Page data would be available in client side. + +- Also see: + - [Client API > usePageData](./client-api.md#usepagedata) + - [Plugin API > extendsPage](./plugin-api.md#extendspage) + +### content + +- Type: `string` + +- Details: + + Raw content of the page. + +### contentRendered + +- Type: `string` + +- Details: + + Rendered content of the page. + +### date + +- Type: `string` + +- Details: + + Date of the page, in 'yyyy-MM-dd' format. + +- Example: + + - `'0000-00-00'` + - `'2021-08-16`' + +- Also see: + - [Frontmatter > date](./frontmatter.md#date) + +### deps + +- Type: `string[]` + +- Details: + + Dependencies of the page. + + For example, if users import code snippet in the page, the absolute file path of the imported file would be added to `deps`. + +- Also see: + - [Config > markdown.importCode](./config.md#markdown-importcode) + +### links + +- Type: `MarkdownLink[]` + +```ts +interface MarkdownLink { + raw: string + relative: string + absolute: string +} +``` + +- Details: + + Links included in the page content. + +### markdownEnv + +- Type: `Record ` + +- Details: + + The `env` object when parsing markdown content with markdown-it. + + Some markdown-it plugins may store extra information inside this object, and you can make use of them for advanced customization. + + Notice that some other page properties are also extracted from the original `env` object. Those properties have already been removed from `page.markdownEnv`. + +- Also see: + - [markdown-it > API Documentation > MarkdownIt > parse](https://markdown-it.github.io/markdown-it/#MarkdownIt.parse) + +### pathInferred + +- Type: `string | null` + +- Details: + + Route path of the page that inferred from file path. + + By default, the route path is inferred from the relative file path of the Markdown source file. However, users may explicitly set the route path, e.g. [permalink](#permalink), which would be used as the final route path of the page. So we keep the inferred path as a page property in case you may need it. + + It would be `null` if the page does not come from a Markdown source file. + +- Example: + + - `'/'` + - `'/foo.html'` + +- Also see: + - [Guide > Page > Routing](../guide/page.md#routing) + - [Node API > Page Properties > path](#path) + +### pathLocale + +- Type: `string` + +- Details: + + Locale prefix of the page route path. + + It is inferred from the relative file path of the Markdown source file and the key of `locales` option in user config. + +- Example: + + - `'/'` + - `'/en/'` + - `'/zh/'` + +- Also see: + - [Config > locales](./config.md#locales) + +### permalink + +- Type: `string | null` + +- Details: + + Permalink of the page. + +- Also see: + - [Frontmatter > permalink](./frontmatter.md#permalink) + - [Frontmatter > permalinkPattern](./frontmatter.md#permalinkpattern) + +### routeMeta + +- Type: `Record ` + +- Details: + + Custom data to be attached to the route record of vue-router. + +- Also see: + - [Frontmatter > routeMeta](./frontmatter.md#routemeta) + - [vue-router > API Reference > RouteRecordRaw > meta](https://router.vuejs.org/api/#meta) + +::: tip What's the difference between route meta and page data? +Both [route meta](#routemeta) and [page data](#data) is available in client side. However, route meta is attached to the route record, so the route meta of all pages would be loaded at once when users enter your site. In the contrast, page data is saved in separated files, which would be loaded only when users enter the corresponding page. + +Therefore, it's not recommended to store large amounts of info into route meta, otherwise the initial loading speed will be affected a lot when your site has a large number of pages. +::: + +### sfcBlocks + +- Type: `MarkdownSfcBlocks` + +- Details: + + Extracted vue SFC blocks of the page. + +- Also see: + - [Config > markdown.sfc](./config.md#markdown-sfc) + +### slug + +- Type: `string` + +- Details: + + Slug of the page. + + It is inferred from the filename of the Markdown source file. + +### filePath + +- Type: `string | null` + +- Details: + + Absolute path of the Markdown source file of the page. + + It would be `null` if the page does not come from a Markdown source file. + +### filePathRelative + +- Type: `string | null` + +- Details: + + Relative path of the Markdown source file of the page. + + It would be `null` if the page does not come from a Markdown source file. diff --git a/docs/reference/plugin-api.md b/docs/reference/plugin-api.md new file mode 100644 index 00000000..7771d196 --- /dev/null +++ b/docs/reference/plugin-api.md @@ -0,0 +1,366 @@ +# Plugin API + + + +Plugin API is supported by [@vuepress/core](https://www.npmjs.com/package/@vuepress/core) package. You could check out [Node API](./node-api.md) for how to use the VuePress app instance in plugin hooks. + +## Overview + +Plugins should be used before initialization. The basic options will be handled once the plugin is used: + +- [name](#name) +- [multiple](#multiple) + +The following hooks will be processed when initializing app: + +- [extendsMarkdownOptions](#extendsmarkdownoptions) +- [extendsMarkdown](#extendsmarkdown) +- [extendsPageOptions](#extendspageoptions) +- [extendsPage](#extendspage) +- [onInitialized](#oninitialized) + +The following hooks will be processed when preparing files: + +- [clientConfigFile](#clientconfigfile) +- [onPrepared](#onprepared) + +The following hooks will be processed in dev / build: + +- [extendsBundlerOptions](#extendsbundleroptions) +- [alias](#alias) +- [define](#define) +- [onWatched](#onwatched) +- [onGenerated](#ongenerated) + +> Check out [Advanced > Architecture > Core Process and Hooks](../advanced/architecture.md#core-process-and-hooks) to understand the process better. + +## Basic Options + +### name + +- Type: `string` + +- Details: + + Name of the plugin. + + It will be used for identifying plugins to avoid using a same plugin multiple times, so make sure to use a unique plugin name. + + It should follow the naming convention: + + - Non-scoped: `vuepress-plugin-foo` + - Scoped: `@org/vuepress-plugin-foo` + +- Also see: + - [Plugin API > multiple](#multiple) + +### multiple + +- Type: `boolean` + +- Default: `false` + +- Details: + + Declare whether the plugin can be used multiple times. + + If set to `false`, when using plugins with the same name, the one used previously will be replaced by the one used later. + + If set to `true`, plugins with the same name could be used multiple times and won't be replaced. + +- Also see: + - [Plugin API > name](#name) + +## Development Hooks + +### alias + +- Type: `Record | ((app: App, isServer: boolean) => Record )` + +- Details: + + Path aliases definition. + + This hook accepts an object or a function that returns an object. + +- Example: + +```ts +import { getDirname, path } from '@vuepress/utils' + +const __dirname = getDirname(import.meta.url) + +export default { + alias: { + '@alias': path.resolve(__dirname, './path/to/alias'), + }, +} +``` + +### clientConfigFile + +- Type: `string | ((app: App) => string | Promise )` + +- Details: + + Path of client config file. + + This hook accepts an absolute file path, or a function that returns the path. + +- Example: + +```ts +import { getDirname, path } from '@vuepress/utils' + +const __dirname = getDirname(import.meta.url) + +export default { + clientConfigFile: path.resolve( + __dirname, + './path/to/clientConfig.js' + ), +} +``` + +- Also see: + - [Client API > defineClientConfig](./client-api.md#defineclientconfig) + - [Advanced > Cookbook > Usage of Client Config](../advanced/cookbook/usage-of-client-config.md) + +### define + +- Type: `Record | ((app: App, isServer: boolean) => Record )` + +- Details: + + Define global constants replacements. + + This hook accepts an object or a function that returns an object. + + This can be useful for passing variables to client files. Note that the values will be automatically processed by `JSON.stringify()`. + +- Example: + +```ts +export default { + define: { + __GLOBAL_BOOLEAN__: true, + __GLOBAL_STRING__: 'foobar', + __GLOBAL_OBJECT__: { foo: 'bar' }, + }, +} +``` + +### extendsBundlerOptions + +- Type: `(options: BundlerOptions, app: App) => void | Promise ` + +- Details: + + Bundler options extension. + + This hook accepts a function that will receive the bundler options. + + This hook can be used for modifying bundler options. + + You could determine which bundler the user is using by `app.options.bundler.name`. + +- Example: + +Adding default [app.compilerOptions.isCustomElement](https://vuejs.org/api/application.html#app-config-compileroptions) option: + +```ts +export default { + extendsBundlerOptions: (bundlerOptions, app) => { + // extends options of @vuepress/bundler-vite + if (app.options.bundler.name === '@vuepress/bundler-vite') { + bundlerOptions.vuePluginOptions ??= {} + bundlerOptions.vuePluginOptions.template ??= {} + bundlerOptions.vuePluginOptions.template.compilerOptions ??= {} + const isCustomElement = bundlerOptions.vuePluginOptions.template.compilerOptions.isCustomElement + bundlerOptions.vuePluginOptions.template.compilerOptions.isCustomElement = (tag) => { + if (isCustomElement?.(tag)) return true + if (tag === 'my-custom-element') return true + } + } + + // extends options of @vuepress/bundler-webpack + if (app.options.bundler.name === '@vuepress/bundler-webpack') { + bundlerOptions.vue ??= {} + bundlerOptions.vue.compilerOptions ??= {} + const isCustomElement = bundlerOptions.vue.compilerOptions.isCustomElement + bundlerOptions.vue.compilerOptions.isCustomElement = (tag) => { + if (isCustomElement?.(tag)) return true + if (tag === 'my-custom-element') return true + } + } + }, +} +``` + +- Also see: + - [Bundlers > Vite](./bundler/vite.md) + - [Bundlers > Webpack](./bundler/webpack.md) + +### extendsMarkdownOptions + +- Type: `(options: MarkdownOptions, app: App) => void | Promise ` + +- Details: + + Markdown options extension. + + This hook accepts a function that will receive the markdown options. + + This hook can be used for modifying markdown options. + +- Example: + +Modifying the default header levels that going to be extracted: + +```ts +export default { + extendsMarkdownOptions: (markdownOptions, app) => { + if (markdownOptions.headers === false) return + markdownOptions.headers ??= {} + if (markdownOptions.headers.level) return + markdownOptions.headers.level = [2, 3, 4, 5, 6] + }, +} +``` + +- Also see: + - [Config > markdown](./config.md#markdown) + +### extendsMarkdown + +- Type: `(md: Markdown, app: App) => void | Promise ` + +- Details: + + Markdown enhancement. + + This hook accepts a function that will receive an instance of `Markdown` powered by [markdown-it](https://github.com/markdown-it/markdown-it) in its arguments. + + This hook can be used for using extra markdown-it plugins and implementing customizations. + +- Example: + +```ts +export default { + extendsMarkdown: (md) => { + md.use(plugin1) + md.linkify.set({ fuzzyEmail: false }) + }, +} +``` + +### extendsPageOptions + +- Type: `(options: PageOptions, app: App) => void | Promise ` + +- Details: + + Page options extension. + + This hook accepts a function that will receive the options of `createPage`. + + This hook can be used for modifying page options + +- Example: + +Set permalink pattern for pages in `_posts` directory: + +```ts +export default { + extendsPageOptions: (pageOptions, app) => { + if (pageOptions.filePath?.startsWith(app.dir.source('_posts/'))) { + pageOptions.frontmatter = pageOptions.frontmatter ?? {} + pageOptions.frontmatter.permalinkPattern = '/:year/:month/:day/:slug.html' + } + }, +} +``` + +- Also see: + - [Node API > Page > createPage](./node-api.md#createpage) + +### extendsPage + +- Type: `(page: Page, app: App) => void | Promise ` + +- Details: + + Page extension. + + This hook accepts a function that will receive a `Page` instance. + + This hook can be used for adding extra properties or modifying current properties on `Page` object. + + Notice that changes to `page.data` and `page.routeMeta` can be used in client side code. + +- Example: + +```ts +export default { + extendsPage: (page) => { + page.foo = 'foo' + page.data.bar = 'bar' + }, +} +``` + +In client component: + +```ts +import { usePageData } from '@vuepress/client' + +export default { + setup() { + const page = usePageData() + console.log(page.value.bar) // bar + }, +} +``` + +- Also see: + - [Client API > usePageData](./client-api.md#usepagedata) + - [Node API > Page Properties > data](./node-api.md#data) + - [Node API > Page Properties > routeMeta](./node-api.md#routemeta) + +## Lifecycle Hooks + +### onInitialized + +- Type: `(app: App) => void | Promise ` + +- Details: + + This hook will be invoked once VuePress app has been initialized. + +### onPrepared + +- Type: `(app: App) => void | Promise ` + +- Details: + + This hook will be invoked once VuePress app has finished preparation. + +### onWatched + +- Type: `(app: App, watchers: Closable[], restart: () => Promise ) => void | Promise ` + +- Details: + + This hook will be invoked once VuePress app has started dev-server and watched files change. + + The `watchers` is an array of file watchers. When changing config file, the dev command will be restarted and those watchers will be closed. If you are adding new watchers in this hook, you should push your watchers to the `watchers` array, so that they can be closed correctly when restarting. + + The `restart` is a method to restart the dev command. When calling this method, the `watchers` array will be closed automatically. + +### onGenerated + +- Type: `(app: App) => void | Promise ` + +- Details: + + This hook will be invoked once VuePress app has generated static files. diff --git a/docs/reference/plugin/active-header-links.md b/docs/reference/plugin/active-header-links.md new file mode 100644 index 00000000..a4b0ca0b --- /dev/null +++ b/docs/reference/plugin/active-header-links.md @@ -0,0 +1,74 @@ +# active-header-links + + + +This plugin will listen to page scroll event. When the page scrolls to a certain _header anchor_, this plugin will change the route hash to that _header anchor_ if there is a corresponding _header link_. + +This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases. + +## Usage + +```bash +npm i -D @vuepress/plugin-active-header-links@next +``` + +```ts +import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links' + +export default { + plugins: [ + activeHeaderLinksPlugin({ + // options + }), + ], +} +``` + +## Options + +### headerLinkSelector + +- Type: `string` + +- Default: `'a.sidebar-item'` + +- Details: + + Selector of _header link_. + + If a _header anchor_ does not have a corresponding _header link_, this plugin won't change the route hash to that anchor when scrolling to it. + +### headerAnchorSelector + +- Type: `string` + +- Default: `'.header-anchor'` + +- Details: + + Selector of _header anchor_. + + You don't need to specify this option unless you have changed the `permalinkClass` option of [markdown-it-anchor](https://github.com/valeriangalliat/markdown-it-anchor#readme) via [markdown.anchor](../config.md#markdown-anchor). + +- Also see: + - [Guide > Markdown > Syntax Extensions > Header Anchors](../../guide/markdown.md#header-anchors) + +### delay + +- Type: `number` + +- Default: `200` + +- Details: + + The delay of the debounced scroll event listener. + +### offset + +- Type: `number` + +- Default: `5` + +- Details: + + Even if you click the link of the _header anchor_ directly, the `scrollTop` might not be exactly equal to `offsetTop` of the _header anchor_, so we add an offset to avoid the error. diff --git a/docs/reference/plugin/back-to-top.md b/docs/reference/plugin/back-to-top.md new file mode 100644 index 00000000..edb77284 --- /dev/null +++ b/docs/reference/plugin/back-to-top.md @@ -0,0 +1,29 @@ +# back-to-top + + + +This plugin will add a _back to top_ button to your site. The button will be displayed in the bottom right corner of the page when scrolling down. By clicking the button, the page will scroll to the top. + +This plugin has been integrated into the default theme. + +## Usage + +```bash +npm i -D @vuepress/plugin-back-to-top@next +``` + +```ts +import { backToTopPlugin } from '@vuepress/plugin-back-to-top' + +export default { + plugins: [ + backToTopPlugin(), + ], +} +``` + +## Styles + +You can customize the style of the _back to top_ button via CSS variables: + +@[code css](@vuepress/plugin-back-to-top/src/client/styles/vars.css) diff --git a/docs/reference/plugin/container.md b/docs/reference/plugin/container.md new file mode 100644 index 00000000..960db6a3 --- /dev/null +++ b/docs/reference/plugin/container.md @@ -0,0 +1,161 @@ +# container + + + +Register markdown custom containers in your VuePress site. + +This plugin simplifies the use of [markdown-it-container](https://github.com/markdown-it/markdown-it-container), but also retains its original capabilities. + +The [Custom Containers](../default-theme/markdown.md#custom-containers) of default theme is powered by this plugin. + +## Usage + +```bash +npm i -D @vuepress/plugin-container@next +``` + +```ts +import { containerPlugin } from '@vuepress/plugin-container' + +export default { + plugins: [ + containerPlugin({ + // options + }), + ], +} +``` + +## Container Syntax + +```md +::: [info] +[content] +::: +``` + +- The `type` is required and should be specified via [type](#type) option. +- The `info` is optional, and the default value can be specified via `defaultInfo` in [locales](#locales) option. +- The `content` can be any valid markdown content. + +::: tip +This plugin can be used multiple times to support different types of containers. +::: + +## Options + +### type + +- Type: `string` + +- Details: + + The type of the container. + + It will be used as the `name` param of [markdown-it-container](https://github.com/markdown-it/markdown-it-container#api). + +### locales + +- Type: `Record ` + +- Details: + + The default `info` of the container in different locales. + + If this option is not specified, the default `info` will fallback to the uppercase of the [type](#type) option. + +- Example: + +```ts +export default { + plugins: [ + containerPlugin({ + type: 'tip', + locales: { + '/': { + defaultInfo: 'TIP', + }, + '/zh/': { + defaultInfo: '提示', + }, + }, + }), + ], +} +``` + +- Also see: + - [Guide > I18n](../../guide/i18n.md) + +### before + +- Type: `(info: string) => string` + +- Default: + +```ts +(info: string): string => + ` ${info ? `\n' +``` + +- Details: + + A function to render the ending tag of the container. + + The first param is the `info` part of [container syntax](#container-syntax). + + This option will not take effect if you don't specify the [before](#before) option. + +### render + +- Type: + +```ts +type MarkdownItContainerRenderFunction = ( + tokens: Token[], + index: number, + options: any, + env: MarkdownEnv, + self: Renderer +) => string +``` + +- Details: + + The `render` option of [markdown-it-container](https://github.com/markdown-it/markdown-it-container#api). + + This plugin uses a default `render` function. If you specify this option, the default `render` function will be replaced, and the [locales](#locales), [before](#before) and [after](#after) options will be ignored. + +### validate + +- Type: `(params: string) => boolean` + +- Details: + + The `validate` option of [markdown-it-container](https://github.com/markdown-it/markdown-it-container#api). + +### marker + +- Type: `string` + +- Details: + + The `marker` option of [markdown-it-container](https://github.com/markdown-it/markdown-it-container#api). diff --git a/docs/reference/plugin/docsearch.md b/docs/reference/plugin/docsearch.md new file mode 100644 index 00000000..39762436 --- /dev/null +++ b/docs/reference/plugin/docsearch.md @@ -0,0 +1,411 @@ +# docsearch + +${info}
` : ''}\n` +``` + +- Details: + + A function to render the starting tag of the container. + + The first param is the `info` part of [container syntax](#container-syntax). + + This option will not take effect if you don't specify the [after](#after) option. + +### after + +- Type: `(info: string) => string` + +- Default: + +```ts +(): string => '+ +Integrate [Algolia DocSearch](https://docsearch.algolia.com/) into VuePress, which can provide search to your documentation site. + +::: tip +Default theme will add DocSearch to the navbar once you configure this plugin correctly. + +This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details. +::: + +## Usage + +```bash +npm i -D @vuepress/plugin-docsearch@next +``` + +```ts +import { docsearchPlugin } from '@vuepress/plugin-docsearch' + +export default { + plugins: [ + docsearchPlugin({ + // options + }), + ], +} +``` + +## Get Search Index + +You need to [submit the URL of your site](https://docsearch.algolia.com/apply/) to join the DocSearch program. The DocSearch team will send [apiKey](#apikey) and [indexName](#indexname) to your email once the index is generated. Then you can configure this plugin to enable DocSearch in VuePress. + +Alternatively, you can [run your own crawler](https://docsearch.algolia.com/docs/run-your-own/) to generate the index, and then use your own [appId](#appId), [apiKey](#apikey) and [indexName](#indexname) to configure this plugin. + +::: details Official crawler config + +```js{35-50,59} +new Crawler({ + appId: 'YOUR_APP_ID', + apiKey: 'YOUR_API_KEY', + rateLimit: 8, + startUrls: [ + // These are urls which algolia start to craw + // If your site is divided in to mutiple parts, + // you may want to set mutiple entry links + 'https://YOUR_WEBSITE_URL/', + ], + sitemaps: [ + // if you are using sitemap plugins (e.g.: vuepress-plugin-sitemap2), you may provide one + 'https://YOUR_WEBSITE_URL/sitemap.xml', + ], + ignoreCanonicalTo: false, + exclusionPatterns: [ + // You can use this to stop algolia crawing some paths + ], + discoveryPatterns: [ + // These are urls which algolia looking for, + 'https://YOUR_WEBSITE_URL/**', + ], + // Crawler schedule, set it according to your docs update frequency + schedule: 'at 02:00 every 1 day', + actions: [ + // you may have mutiple actions, especially when you are deploying mutiple docs under one domain + { + // name the index with name you like + indexName: 'YOUR_INDEX_NAME', + // paths where the index take effect + pathsToMatch: ['https://YOUR_WEBSITE_URL/**'], + // controls how algolia extracts records from your site + recordExtractor: ({ $, helpers }) => { + // options for @vuepress/theme-default + return helpers.docsearch({ + recordProps: { + lvl0: { + selectors: '.sidebar-heading.active', + defaultValue: 'Documentation', + }, + lvl1: '.theme-default-content h1', + lvl2: '.theme-default-content h2', + lvl3: '.theme-default-content h3', + lvl4: '.theme-default-content h4', + lvl5: '.theme-default-content h5', + lvl6: '.theme-default-content h6', + content: '.theme-default-content p, .theme-default-content li', + }, + indexHeadings: true, + }) + }, + }, + ], + initialIndexSettings: { + // controls how index are initialized + // only has effects before index are initialize + // you may need to delete your index and recraw after modification + YOUR_INDEX_NAME: { + attributesForFaceting: ['type', 'lang'], + attributesToRetrieve: ['hierarchy', 'content', 'anchor', 'url'], + attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'], + attributesToSnippet: ['content:10'], + camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'], + searchableAttributes: [ + 'unordered(hierarchy_radio_camel.lvl0)', + 'unordered(hierarchy_radio.lvl0)', + 'unordered(hierarchy_radio_camel.lvl1)', + 'unordered(hierarchy_radio.lvl1)', + 'unordered(hierarchy_radio_camel.lvl2)', + 'unordered(hierarchy_radio.lvl2)', + 'unordered(hierarchy_radio_camel.lvl3)', + 'unordered(hierarchy_radio.lvl3)', + 'unordered(hierarchy_radio_camel.lvl4)', + 'unordered(hierarchy_radio.lvl4)', + 'unordered(hierarchy_radio_camel.lvl5)', + 'unordered(hierarchy_radio.lvl5)', + 'unordered(hierarchy_radio_camel.lvl6)', + 'unordered(hierarchy_radio.lvl6)', + 'unordered(hierarchy_camel.lvl0)', + 'unordered(hierarchy.lvl0)', + 'unordered(hierarchy_camel.lvl1)', + 'unordered(hierarchy.lvl1)', + 'unordered(hierarchy_camel.lvl2)', + 'unordered(hierarchy.lvl2)', + 'unordered(hierarchy_camel.lvl3)', + 'unordered(hierarchy.lvl3)', + 'unordered(hierarchy_camel.lvl4)', + 'unordered(hierarchy.lvl4)', + 'unordered(hierarchy_camel.lvl5)', + 'unordered(hierarchy.lvl5)', + 'unordered(hierarchy_camel.lvl6)', + 'unordered(hierarchy.lvl6)', + 'content', + ], + distinct: true, + attributeForDistinct: 'url', + customRanking: [ + 'desc(weight.pageRank)', + 'desc(weight.level)', + 'asc(weight.position)', + ], + ranking: [ + 'words', + 'filters', + 'typo', + 'attribute', + 'proximity', + 'exact', + 'custom', + ], + highlightPreTag: '', + highlightPostTag: '', + minWordSizefor1Typo: 3, + minWordSizefor2Typos: 7, + allowTyposOnNumericTokens: false, + minProximity: 1, + ignorePlurals: true, + advancedSyntax: true, + attributeCriteriaComputedByMinProximity: true, + removeWordsIfNoResults: 'allOptional', + }, + }, +}) +``` + +The above `recordProps` is the configuration used for the default theme. You can modify them according to the theme you are using. + +Notice that the `initialIndexSettings.YOUR_INDEX_NAME.attributesForFaceting` fields must include `'lang'` to make this plugin work properly. +::: + +::: tip +If you are not using default theme, or you meet any problems when using docsearch, you can also check the above example crawler config, and ahead to [Algolia Crawler](https://crawler.algolia.com/admin/crawlers/), and edit your config with 'Editor' panel in project sidebar. +::: + +## Options + +### apiKey + +- Type: `string` + +- Required: `true` + +- Details: + + The `apiKey` that you received from the DocSearch team, or generated by yourself. + +- Also see: + - [DocSearch > Options > apiKey](https://docsearch.algolia.com/docs/api#apikey) + +### indexName + +- Type: `string` + +- Required: `true` + +- Details: + + The `indexName` that you received from the DocSearch team, or generated by yourself. + +- Also see: + - [DocSearch > Options > indexName](https://docsearch.algolia.com/docs/api#indexname) + +### appId + +- Type: `string` + +- Required: `true` + +- Details: + + It defines your own application ID. + +- Also see: + - [DocSearch > Options > appId](https://docsearch.algolia.com/docs/api#appid) + +### searchParameters + +- Type: `SearchParameters` + +- Details: + + Parameters of Algolia Search API. + +- Also see: + - [DocSearch > Options > searchParameters](https://docsearch.algolia.com/docs/api/#searchparameters) + - [Algolia > Search API Parameters](https://www.algolia.com/doc/api-reference/search-api-parameters/) + +### placeholder + +- Type: `string` + +- Default: `'Search docs'` + +- Details: + + The placeholder attribute of the search input. + +- Also see: + - [DocSearch > Options > placeholder](https://docsearch.algolia.com/docs/api/#placeholder) + +### disableUserPersonalization + +- Type: `boolean` + +- Default: `false` + +- Details: + + Whether to disable all personalized features: recent searches, favorite searches, etc. + +- Also see: + - [DocSearch > Options > disableUserPersonalization](https://docsearch.algolia.com/docs/api/#disableuserpersonalization) + +### initialQuery + +- Type: `string` + +- Details: + + The initial query when the modal opens. + +- Also see: + - [DocSearch > Options > initialQuery](https://docsearch.algolia.com/docs/api/#initialquery) + +### translations + +- Type: `Partial ` + +- Details: + + Allow replacing the default text in the DocSearch button or modal. + +- Also see: + - [DocSearch > Options > translations](https://docsearch.algolia.com/docs/api/#translations) + +### locales + +- Type: `Record ` + +- Details: + + Options of this plugin in different locales. + + All other options of this plugin are acceptable in locale config. + +- Example: + +```ts +export default { + plugins: [ + docsearchPlugin({ + appId: ' ', + apiKey: ' ', + indexName: ' ', + locales: { + '/': { + placeholder: 'Search Documentation', + translations: { + button: { + buttonText: 'Search Documentation', + }, + }, + }, + '/zh/': { + placeholder: '搜索文档', + translations: { + button: { + buttonText: '搜索文档', + }, + }, + }, + }, + }), + ], +} +``` + +- Also see: + - [Guide > I18n](../../guide/i18n.md) + +### indexBase + +- Type: `string` + +- Default: [base](../config.md#base) + +- Details: + + The base path of the search index. + + If you are deploying your site to multiple domains, you don't need to submit all of them to DocSearch and generate search index separately. You could choose one of the domains as the _index domain_, and only submit the _index domain_ to Docsearch for crawling search index. Then, you could reuse the search index across all deployments. + + However, if the [base](../config.md#base) of your deployments are different for different domains, you need to set the option to the [base](../config.md#base) of your _index domain_, so that other deployments could reuse the search index correctly. + +### injectStyles + +- Type: `boolean` + +- Default: `true` + +- Details: + + Whether to inject the default styles of DocSearch or not. + + If you think the default styles of DocSearch is not compatible with your site, you can try to override the default styles, or set this option to `false` to totally exclude the default styles. + + When this option is disabled, you need to import your own styles for DocSearch. Also notice that all styles customization in [Styles](#styles) section would be unavailable. + +## Styles + +You can customize styles via CSS variables that provided by [@docsearch/css](https://docsearch.algolia.com/docs/styling): + +```css +:root { + --docsearch-primary-color: rgb(84, 104, 255); + --docsearch-text-color: rgb(28, 30, 33); + --docsearch-spacing: 12px; + --docsearch-icon-stroke-width: 1.4; + --docsearch-highlight-color: var(--docsearch-primary-color); + --docsearch-muted-color: rgb(150, 159, 175); + --docsearch-container-background: rgba(101, 108, 133, 0.8); + --docsearch-logo-color: rgba(84, 104, 255); + + /* modal */ + --docsearch-modal-width: 560px; + --docsearch-modal-height: 600px; + --docsearch-modal-background: rgb(245, 246, 247); + --docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.5), 0 3px + 8px 0 rgba(85, 90, 100, 1); + + /* searchbox */ + --docsearch-searchbox-height: 56px; + --docsearch-searchbox-background: rgb(235, 237, 240); + --docsearch-searchbox-focus-background: #fff; + --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color); + + /* hit */ + --docsearch-hit-height: 56px; + --docsearch-hit-color: rgb(68, 73, 80); + --docsearch-hit-active-color: #fff; + --docsearch-hit-background: #fff; + --docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225); + + /* key */ + --docsearch-key-gradient: linear-gradient( + -225deg, + rgb(213, 219, 228) 0%, + rgb(248, 248, 248) 100% + ); + --docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), inset 0 0 1px 1px + #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4); + + /* footer */ + --docsearch-footer-height: 44px; + --docsearch-footer-background: #fff; + --docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 0 -3px 6px 0 rgba(69, 98, 155, 0.12); +} +``` + +## Components + +### Docsearch + +- Details: + + This plugin will register a ` ` component globally, and you can use it without any props. + + Put this component to where you want to place the docsearch button. For example, default theme puts this component to the end of the navbar. + +::: tip +This component is mainly used for theme development. You don't need to use it directly in most cases. +::: diff --git a/docs/reference/plugin/external-link-icon.md b/docs/reference/plugin/external-link-icon.md new file mode 100644 index 00000000..c57f4374 --- /dev/null +++ b/docs/reference/plugin/external-link-icon.md @@ -0,0 +1,87 @@ +# external-link-icon + + + +This plugin will add an icon to the external link in your markdown content, i.e. + +This plugin has been integrated into the default theme. + +## Usage + +```bash +npm i -D @vuepress/plugin-external-link-icon@next +``` + +```ts +import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon' + +export default { + plugins: [ + externalLinkIconPlugin({ + // options + }), + ], +} +``` + +## Options + +### locales + +- Type: `Record ` + +- Details: + + The a11y text of the external link icon in different locales. + + If this option is not specified, it will fallback to default text. + +- Example: + +```ts +export default { + plugins: [ + externalLinkIconPlugin({ + locales: { + '/': { + openInNewWindow: 'open in new window', + }, + '/zh/': { + openInNewWindow: '在新窗口打开', + }, + }, + }), + ], +} +``` + +- Also see: + - [Guide > I18n](../../guide/i18n.md) + +## Frontmatter + +### externalLinkIcon + +- Type: `boolean` + +- Details: + + Whether to append an external link icon to external links in current page. + +## Styles + +You can customize the style of the external link icon via CSS variables: + +@[code css](@vuepress/plugin-external-link-icon/src/client/styles/vars.css) + +## Components + +### ExternalLinkIcon + +- Details: + + This plugin will register a ` ` component globally, and you can use it without any props. + +::: tip +This component is mainly used for theme development. You don't need to use it directly in most cases. +::: diff --git a/docs/reference/plugin/git.md b/docs/reference/plugin/git.md new file mode 100644 index 00000000..742c314a --- /dev/null +++ b/docs/reference/plugin/git.md @@ -0,0 +1,145 @@ +# git + + + +This plugin will collect git information of your pages, including the created and updated time, the contributors, etc. + +The [lastUpdated](../default-theme/config.md#lastupdated) and [contributors](../default-theme/config.md#contributors) of default theme is powered by this plugin. + +This plugin is mainly used to develop themes. You won't need to use it directly in most cases. + +## Usage + +```bash +npm i -D @vuepress/plugin-git@next +``` + +```ts +import { gitPlugin } from '@vuepress/plugin-git' + +export default { + plugins: [ + gitPlugin({ + // options + }), + ], +} +``` + +## Git Repository + +This plugin requires your project to be inside a [Git Repository](https://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository), so that it can collect information from the commit history. + +You should ensure all commits are available when building your site. For example, CI workflows usually clone your repository with [--depth 1](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt) to avoid fetching all commits, so you should disable the behavior to make this plugin work properly in CI. + +::: warning +This plugin will significantly slow down the speed of data preparation, especially when you have a lot of pages. You can consider disabling this plugin in `dev` mode to get better development experience. +::: + +## Options + +### createdTime + +- Type: `boolean` + +- Default: `true` + +- Details: + + Whether to collect page created time or not. + +### updatedTime + +- Type: `boolean` + +- Default: `true` + +- Details: + + Whether to collect page updated time or not. + +### contributors + +- Type: `boolean` + +- Default: `true` + +- Details: + + Whether to collect page contributors or not. + +## Frontmatter + +### gitInclude + +- Type: `string[]` + +- Details: + + An array of relative paths to be included when calculating page data. + +- Example: + +```md +--- +gitInclude: + - relative/path/to/file1 + - relative/path/to/file2 +--- +``` + +## Page Data + +This plugin will add a `git` field to page data. + +After using this plugin, you can get the collected git information in page data: + +```ts +import { usePageData } from '@vuepress/client' +import type { GitPluginPageData } from '@vuepress/plugin-git' + +export default { + setup() { + const page = usePageData () + console.log(page.value.git) + }, +} +``` + +### git.createdTime + +- Type: `number` + +- Details: + + Unix timestamp in milliseconds of the first commit of the page. + + This attribute would take the minimum of the first commit timestamps of the current page and the files listed in [gitInclude](#gitinclude). + +### git.updatedTime + +- Type: `number` + +- Details: + + Unix timestamp in milliseconds of the last commit of the page. + + This attribute would take the maximum of the last commit timestamps of the current page and the files listed in [gitInclude](#gitinclude). + +### git.contributors + +- Type: `GitContributor[]` + +```ts +interface GitContributor { + name: string + email: string + commits: number +} +``` + +- Details: + + The contributors information of the page. + + This attribute would also include contributors to the files listed in [gitInclude](#gitinclude). diff --git a/docs/reference/plugin/google-analytics.md b/docs/reference/plugin/google-analytics.md new file mode 100644 index 00000000..c35b267d --- /dev/null +++ b/docs/reference/plugin/google-analytics.md @@ -0,0 +1,78 @@ +# google-analytics + + + +Integrate [Google Analytics](https://analytics.google.com/) into VuePress. + +This plugin will import [gtag.js](https://developers.google.com/analytics/devguides/collection/gtagjs) for [Google Analytics 4](https://support.google.com/analytics/answer/10089681). + +## Usage + +```bash +npm i -D @vuepress/plugin-google-analytics@next +``` + +```ts +import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics' + +export default { + plugins: [ + googleAnalyticsPlugin({ + // options + }), + ], +} +``` + +## Reporting Events + +Google Analytics will [automatically collect some events](https://support.google.com/analytics/answer/9234069), such as `page_view`, `first_visit`, etc. + +So if you only want to collect some basic data of your site, you don't need to do anything else except setting the [Measurement ID](#id) correctly. + +After using this plugin, the global `gtag()` function is available on the `window` object, and you can use it for [custom events reporting](https://developers.google.com/analytics/devguides/collection/ga4/events). + +## Options + +### id + +- Type: `string` + +- Details: + + The Measurement ID of Google Analytics 4, which should start with `'G-'`. + + You can follow the instructions [here](https://support.google.com/analytics/answer/9539598) to find your Measurement ID. Notice the difference between Google Analytics 4 Measurement ID (i.e. "G-" ID) and Universal Analytics Tracking ID (i.e. "UA-" ID). + +- Example: + +```ts +export default { + plugins: [ + googleAnalyticsPlugin({ + id: 'G-XXXXXXXXXX', + }), + ], +} +``` + +### debug + +- Type: `boolean` + +- Details: + + Set to `true` to enable sending events to DebugView. [See more information on DebugView](https://support.google.com/analytics/answer/7201382). + +- Example: + +```ts +export default { + plugins: [ + googleAnalyticsPlugin({ + id: 'G-XXXXXXXXXX', + debug: true, + }), + ], +} +``` diff --git a/docs/reference/plugin/medium-zoom.md b/docs/reference/plugin/medium-zoom.md new file mode 100644 index 00000000..1edce76d --- /dev/null +++ b/docs/reference/plugin/medium-zoom.md @@ -0,0 +1,100 @@ +# medium-zoom + + + +Integrate [medium-zoom](https://github.com/francoischalifour/medium-zoom#readme) into VuePress, which can provide the ability to zoom images. + +This plugin has been integrated into the default theme. + +## Usage + +```bash +npm i -D @vuepress/plugin-medium-zoom@next +``` + +```ts +import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom' + +export default { + plugins: [ + mediumZoomPlugin({ + // options + }), + ], +} +``` + +## Options + +### selector + +- Type: `string` + +- Default: `':not(a) > img'` + +- Details: + + Selector of zoomable images. + + By default this plugin will make all images zoomable except those inside `` tags. + +### delay + +- Type: `number` + +- Default: `500` + +- Details: + + Delay in milliseconds. + + After navigating to a new page, this plugin will make images zoomable with a delay. + +### zoomOptions + +- Type: `Object` + +- Details: + + Options for medium-zoom. + +- Also see: + - [medium-zoom > Options](https://github.com/francoischalifour/medium-zoom#options) + +## Styles + +You can customize most of the zoom styles via [zoomOptions](#zoomoptions), while this plugin also provides some CSS variables for additional customization: + +@[code css](@vuepress/plugin-medium-zoom/src/client/styles/vars.css) + +## Composition API + +### useMediumZoom + +- Details: + + Returns the `Zoom` instance that used by this plugin, so that you can use the instance [methods](https://github.com/francoischalifour/medium-zoom#methods) directly. + + This plugin will make images zoomable after navigating to current page. But if you are going to add new images dynamically, you may need this method to make those new images zoomable, too. + + This plugin adds an extra `refresh` method on the `Zoom` instance, which will call `zoom.detach()` then `zoom.attach()` with the [selector](#selector) as the default parameter. It will help you to refresh the zoomable images for current page. + +- Example: + +```ts +import { nextTick } from 'vue' +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client' + +export default { + setup() { + const zoom = useMediumZoom() + + // ... do something to add new images in current page + + // then you may need to call `refresh` manually to make those new images zoomable + nextTick(() => { + zoom.refresh() + }) + }, +} +``` diff --git a/docs/reference/plugin/nprogress.md b/docs/reference/plugin/nprogress.md new file mode 100644 index 00000000..6671502f --- /dev/null +++ b/docs/reference/plugin/nprogress.md @@ -0,0 +1,37 @@ +--- +title: nprogress +--- + + + + + +# nprogress Plugin + + + +Integrate [nprogress](https://github.com/rstacruz/nprogress) into VuePress, which can provide a progress bar when navigating to another page. + +This plugin has been integrated into the default theme. + +## Usage + +```bash +npm i -D @vuepress/plugin-nprogress@next +``` + +```ts +import { nprogressPlugin } from '@vuepress/plugin-nprogress' + +export default { + plugins: [ + nprogressPlugin(), + ], +} +``` + +## Styles + +You can customize the style of the progress bar via CSS variables: + +@[code css](@vuepress/plugin-nprogress/src/client/styles/vars.css) diff --git a/docs/reference/plugin/palette.md b/docs/reference/plugin/palette.md new file mode 100644 index 00000000..5bb01d10 --- /dev/null +++ b/docs/reference/plugin/palette.md @@ -0,0 +1,200 @@ +# palette + + + +Provide palette support for your theme. + +This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases. + +For theme authors, this plugin will help you to provide styles customization for users. + +## Usage + +```bash +npm i -D @vuepress/plugin-palette@next +``` + +```ts +import { palettePlugin } from '@vuepress/plugin-palette' + +export default { + plugins: [ + palettePlugin({ + // options + }), + ], +} +``` + +## Palette and Style + +This plugin will provide a `@vuepress/plugin-palette/palette` (palette file) and a `@vuepress/plugin-palette/style` (style file) to be imported in your theme styles. + +The palette file is used for defining style variables, so it's likely to be imported at the beginning of your theme styles. For example, users can define [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties), [SASS variables](https://sass-lang.com/documentation/variables), [LESS variables](http://lesscss.org/features/#variables-feature) or [Stylus variables](https://stylus-lang.com/docs/variables.html) in the palette, and then you can use those variables in your theme styles. + +The style file is used for overriding the default styles or adding extra styles, so it's likely to be imported at the end of your theme styles. + +## Usage + +Use this plugin in your theme, assuming you are using SASS: + +```ts +export default { + // ... + plugins: [ + palettePlugin({ preset: 'sass' }), + ], +} +``` + +### Usage of Palette + +Import the plugin's palette file where your theme needs to use the corresponding variables, such as in the `Layout.vue` file: + +```vue + + Hello, Palette!
+ + + +``` + +Then users can customize variables in `.vuepress/styles/palette.scss`: + +```scss +$color: green; +``` + +### Usage of Style + +Import the plugin's style file after your theme's styles, for example, in the `clientConfigFile`: + +```ts +// import your theme's style file +import 'path/to/your/theme/style' +// import the plugin's style file +import '@vuepress/plugin-palette/style' +``` + +Then users can add extra styles in `.vuepress/styles/index.scss` and override the default styles of your theme: + +```scss +h1 { + font-size: 2.5rem; +} +``` + +## Options + +### preset + +- Type: `'css' | 'sass' | 'less' | 'stylus'` + +- Default: `'css'` + +- Details: + + Set preset for other options. + + If you don't need advanced customization of the plugin, it's recommended to only set this option and omit other options. + +### userPaletteFile + +- Type: `string` + +- Default: + - css: `'.vuepress/styles/palette.css'` + - sass: `'.vuepress/styles/palette.scss'` + - less: `'.vuepress/styles/palette.less'` + - stylus: `'.vuepress/styles/palette.styl'` + +- Details: + + File path of the user palette file, relative to source directory. + + The default value depends on the [preset](#preset) option. + + The file is where users define style variables, and it's recommended to keep the default file path as a convention. + +### tempPaletteFile + +- Type: `string` + +- Default: + - css: `'styles/palette.css'` + - sass: `'styles/palette.scss'` + - less: `'styles/palette.less'` + - stylus: `'styles/palette.styl'` + +- Details: + + File path of the generated palette temp file, relative to temp directory. + + The default value depends on the [preset](#preset) option. + + You should import the palette file via `'@vuepress/plugin-palette/palette'` alias, so you don't need to change this option in most cases. + +### userStyleFile + +- Type: `string` + +- Default: + - css: `'.vuepress/styles/index.css'` + - sass: `'.vuepress/styles/index.scss'` + - less: `'.vuepress/styles/index.less'` + - stylus: `'.vuepress/styles/index.styl'` + +- Details: + + File path of the user style file, relative to source directory. + + The default value depends on the [preset](#preset) option. + + The file is where users override default styles or add extra styles, and it's recommended to keep the default file path as a convention. + +### tempStyleFile + +- Type: `string` + +- Default: + - css: `'styles/index.css'` + - sass: `'styles/index.scss'` + - less: `'styles/index.less'` + - stylus: `'styles/index.styl'` + +- Details: + + File path of the generated style temp file, relative to temp directory. + + The default value depends on the [preset](#preset) option. + + You should import the style file via `'@vuepress/plugin-palette/style'` alias, so you don't need to change this option in most cases. + +### importCode + +- Type: `(filePath: string) => string` + +- Default: + - css: `` (filePath) => `@import '${filePath}';\n` `` + - sass: `` (filePath) => `@forward 'file:///${filePath}';\n` `` + - less: `` (filePath) => `@import '${filePath}';\n` `` + - stylus: `` (filePath) => `@require '${filePath}';\n` `` + +- Details: + + Function to generate import code. + + The default value depends on the [preset](#preset) option. + + This option is used for generating [tempPaletteFile](#temppalettefile) and [tempStyleFile](#tempstylefile), and you don't need to change this option in most cases. diff --git a/docs/reference/plugin/prismjs.md b/docs/reference/plugin/prismjs.md new file mode 100644 index 00000000..d72625f2 --- /dev/null +++ b/docs/reference/plugin/prismjs.md @@ -0,0 +1,43 @@ +# prismjs + ++ +This plugin will enable syntax highlighting for markdown code fence with [Prism.js](https://prismjs.com/). + +This plugin has been integrated into the default theme. + +Notice that this plugin would only tokenize the code fence without adding styles. When using it with a custom theme, you may need to choose and import Prism.js style theme yourself. + +## Usage + +```bash +npm i -D @vuepress/plugin-prismjs@next +``` + +```ts +import { prismjsPlugin } from '@vuepress/plugin-prismjs' + +export default { + plugins: [ + prismjsPlugin({ + // options + }), + ], +} +``` + +## Options + +### preloadLanguages + +- Type: `string[]` + +- Default: `['markdown', 'jsdoc', 'yaml']` + +- Details: + + Languages to preload. + + By default, languages will be loaded on demand when parsing markdown files. + + However, Prism.js has [some potential issues](https://github.com/PrismJS/prism/issues/2716) about loading languages dynamically. To avoid them, you can preload languages via this option. diff --git a/docs/reference/plugin/pwa-popup.md b/docs/reference/plugin/pwa-popup.md new file mode 100644 index 00000000..f305e526 --- /dev/null +++ b/docs/reference/plugin/pwa-popup.md @@ -0,0 +1,72 @@ +# pwa-popup + + + +Provide a popup component for users to activate the new PWA service worker manually. + +This plugin must be used together with [pwa plugin](./pwa.md), and the `skipWaiting` option must not be set to `true`. + +When the new service worker is ready, a popup will appear in the right bottom of the page to ask users to activate the waiting service worker. + +## Usage + +```bash +npm i -D @vuepress/plugin-pwa-popup@next +``` + +```ts +import { pwaPlugin } from '@vuepress/plugin-pwa' +import { pwaPopupPlugin } from '@vuepress/plugin-pwa-popup' + +export default { + plugins: [ + pwaPlugin(), + pwaPopupPlugin({ + // options + }), + ], +} +``` + +## Options + +### locales + +- Type: `Record ` + +- Details: + + The messages of the popup in different locales. + + If this option is not specified, it will fallback to default messages. + +- Example: + +```ts +export default { + plugins: [ + pwaPlugin(), + pwaPopupPlugin({ + locales: { + '/': { + message: 'New content is available.', + buttonText: 'Refresh', + }, + '/zh/': { + message: '发现新内容可用', + buttonText: '刷新', + }, + }, + }), + ], +} +``` + +- Also see: + - [Guide > I18n](../../guide/i18n.md) + +## Styles + +You can customize the style of the popup via CSS variables: + +@[code css](@vuepress/plugin-pwa-popup/src/client/styles/vars.css) diff --git a/docs/reference/plugin/pwa.md b/docs/reference/plugin/pwa.md new file mode 100644 index 00000000..12c3dd3c --- /dev/null +++ b/docs/reference/plugin/pwa.md @@ -0,0 +1,176 @@ +# pwa + + + +Make your VuePress site a [Progressive Web Application (PWA)](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps). + +This plugin uses [workbox-build](https://developers.google.com/web/tools/workbox/modules/workbox-build) to generate service worker file, and uses [register-service-worker](https://github.com/yyx990803/register-service-worker) to register service worker. + +## Usage + +```bash +npm i -D @vuepress/plugin-pwa@next +``` + +```ts +import { pwaPlugin } from '@vuepress/plugin-pwa' + +export default { + plugins: [ + pwaPlugin({ + // options + }), + ], +} +``` + +## Web App Manifests + +To make your website fully compliant with PWA, you need to create a [Web app manifests](https://developer.mozilla.org/en-US/docs/Web/Manifest) file and set the icons, colors, etc. for your PWA. + +You need to put your manifest file and icons into the [public files directory](../../guide/assets.md#public-files). In the following example, we assume that you are using the default public directory `.vuepress/public`. + +1. Create manifest file + +Typically `.vuepress/public/manifest.webmanifest`: + +```json +{ + "name": "VuePress", + "short_name": "VuePress", + "description": "Vue-powered Static Site Generator", + "start_url": "/index.html", + "display": "standalone", + "background_color": "#fff", + "theme_color": "#3eaf7c", + "icons": [ + { + "src": "/images/icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/images/icons/android-chrome-384x384.png", + "sizes": "384x384", + "type": "image/png" + } + ] +} +``` + +2. Generate PWA icons + +To make your PWA more accessible, you need to generate some icons, and put them inside the public directory. + +Make sure the path of icons matches the `icons` field in your manifest file: + +- `.vuepress/public/images/icons/android-chrome-192x192.png` +- `.vuepress/public/images/icons/android-chrome-384x384.png` + +::: tip +Some tools can help to do that. For example, [Favicon Generator](https://realfavicongenerator.net/) would help you to generate icons together with a sample manifest file. +::: + +3. Set tags in head + +You also need to set some tags via [head](../config.md#head) option to [deploy the manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest#deploying_a_manifest_with_the_link_tag): + +```ts +export default { + head: [ + ['link', { rel: 'manifest', href: '/manifest.webmanifest' }], + ['meta', { name: 'theme-color', content: '#3eaf7c' }], + // ...other tags + ], +} +``` + +## Options + +This plugin accepts all parameters of workbox-build's [generateSW method](https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW) in its options, except `globDirectory` and `swDest`. + +For example, you can set `skipWaiting: true` to auto activate the new service worker once it is ready: + +```ts +export default { + plugins: [ + pwaPlugin({ + skipWaiting: true, + }), + ], +} +``` + +But if you omit `skipWaiting` or set it to `false`, you have to activate the new service worker manually: + +- For users, you can use our [pwa-popup](./pwa-popup.md) plugin together. +- For developers, you can use our [composition API](#composition-api) to take control of the service worker behavior. + +### serviceWorkerFilename + +- Type: `string` + +- Default: `'service-worker.js'` + +- Details: + + File path of the generated service worker file, which is relative to the [dest](../config.md#dest) directory. + + The service worker file will only be generated in `build` mode. + +## Composition API + +### usePwaEvent + +- Details: + + Returns the event emitter of this plugin. + + You can add listener function to events that provided by [register-service-worker](https://github.com/yyx990803/register-service-worker). + +- Example: + +```ts +import { usePwaEvent } from '@vuepress/plugin-pwa/client' + +export default { + setup() { + const event = usePwaEvent() + event.on('ready', (registration) => { + console.log('Service worker is active.') + }) + }, +} +``` + +### useSkipWaiting + +- Parameters: + +| Parameter | Type | Description | +| ------------ | --------------------------- | -------------------------------------------------------- | +| registration | `ServiceWorkerRegistration` | The registration of the service worker you want activate | + +- Details: + + Call [skipWaiting()](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting) to activate the waiting service worker. + +- Example: + +```ts +import { + usePwaEvent, + useSkipWaiting, +} from '@vuepress/plugin-pwa/client' + +export default { + setup() { + const event = usePwaEvent() + event.on('updated', (registration) => { + console.log('The waiting service worker is available.') + // activate the waiting service worker + useSkipWaiting(registration) + }) + }, +} +``` diff --git a/docs/reference/plugin/register-components.md b/docs/reference/plugin/register-components.md new file mode 100644 index 00000000..8a9c87ca --- /dev/null +++ b/docs/reference/plugin/register-components.md @@ -0,0 +1,135 @@ +# register-components + + + +Register Vue components from component files or directory automatically. + +## Usage + +```bash +npm i -D @vuepress/plugin-register-components@next +``` + +```ts +import { registerComponentsPlugin } from '@vuepress/plugin-register-components' + +export default { + plugins: [ + registerComponentsPlugin({ + // options + }), + ], +} +``` + +## Options + +### components + +- Type: `Record ` + +- Default: `{}` + +- Details: + + An object that defines name of components and their corresponding file path. + + The key will be used as the component name, and the value is an absolute path of the component file. + + If the component name from this option conflicts with [componentsDir](#componentsdir) option, this option will have a higher priority. + +- Example: + +```ts +import { getDirname, path } from '@vuepress/utils' + +const __dirname = getDirname(import.meta.url) + +export default { + plugins: [ + registerComponentsPlugin({ + components: { + FooBar: path.resolve(__dirname, './components/FooBar.vue'), + }, + }), + ], +} +``` + +### componentsDir + +- Type: `string | null` + +- Default: `null` + +- Details: + + Absolute path to the components directory. + + Files in this directory which are matched with [componentsPatterns](#componentspatterns) will be registered as Vue components automatically. + +- Example: + +```ts +import { getDirname, path } from '@vuepress/utils' + +const __dirname = getDirname(import.meta.url) + +export default { + plugins: [ + registerComponentsPlugin({ + componentsDir: path.resolve(__dirname, './components'), + }), + ], +} +``` + +Components directory: + +```bash +components +├─ FooBar.vue +└─ Baz.vue +``` + +Components will be registered like this: + +```ts +import { defineAsyncComponent } from 'vue' + +app.component( + 'FooBar', + defineAsyncComponent(() => import('/path/to/components/FooBar.vue')) +) + +app.component( + 'Baz', + defineAsyncComponent(() => import('/path/to/components/Baz.vue')) +) +``` + +### componentsPatterns + +- Type: `string[]` + +- Default: `['**/*.vue']` + +- Details: + + Patterns to match component files using [globby](https://github.com/sindresorhus/globby). + + The patterns are relative to [componentsDir](#componentsdir). + +### getComponentName + +- Type: `(filename: string) => string` + +- Default: `(filename) => path.trimExt(filename.replace(/\/|\\/g, '-'))` + +- Details: + + A function to get component name from the filename. + + It will only take effect on the files in the [componentsDir](#componentsdir) which are matched with the [componentsPatterns](#componentspatterns). + + Notice that the `filename` is a filepath relative to [componentsDir](#componentsdir). diff --git a/docs/reference/plugin/search.md b/docs/reference/plugin/search.md new file mode 100644 index 00000000..f563b8a7 --- /dev/null +++ b/docs/reference/plugin/search.md @@ -0,0 +1,166 @@ +# search + + + +Provide local search to your documentation site. + +::: tip +Default theme will add search box to the navbar once you configure this plugin correctly. + +This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details. +::: + +## Usage + +```bash +npm i -D @vuepress/plugin-search@next +``` + +```ts +import { searchPlugin } from '@vuepress/plugin-search' + +export default { + plugins: [ + searchPlugin({ + // options + }), + ], +} +``` + +## Local Search Index + +This plugin will generate search index from your pages locally, and load the search index file when users enter your site. In other words, this is a lightweight built-in search which does not require any external requests. + +However, when your site has a large number of pages, the size of search index file would be very large, which could slow down the page loading speed. In this case, we recommend you to use a more professional solution - [docsearch](./docsearch.md). + +## Options + +### locales + +- Type: `Record ` + +- Details: + + The text of the search box in different locales. + + If this option is not specified, it will fallback to default text. + +- Example: + +```ts +export default { + plugins: [ + searchPlugin({ + locales: { + '/': { + placeholder: 'Search', + }, + '/zh/': { + placeholder: '搜索', + }, + }, + }), + ], +} +``` + +- Also see: + - [Guide > I18n](../../guide/i18n.md) + +### hotKeys + +- Type: `(string | HotKeyOptions)[]` + +@[code ts](@vuepress/plugin-search/src/shared/hotKey.ts) + +- Default: `['s', '/']` + +- Details: + + Specify the [event.key](http://keycode.info/) of the hotkeys. + + When hotkeys are pressed, the search box input will be focused. + + Set to an empty array to disable hotkeys. + +### maxSuggestions + +- Type: `number` + +- Default: `5` + +- Details: + + Specify the maximum number of search results. + +### isSearchable + +- Type: `(page: Page) => boolean` + +- Default: `() => true` + +- Details: + + A function to determine whether a page should be included in the search index. + + - Return `true` to include the page. + - Return `false` to exclude the page. + +- Example: + +```ts +export default { + plugins: [ + searchPlugin({ + // exclude the homepage + isSearchable: (page) => page.path !== '/', + }), + ], +} +``` + +### getExtraFields + +- Type: `(page: Page) => string[]` + +- Default: `() => []` + +- Details: + + A function to add extra fields to the search index of a page. + + By default, this plugin will use page title and headers as the search index. This option could help you to add more searchable fields. + +- Example: + +```ts +export default { + plugins: [ + searchPlugin({ + // allow searching the `tags` frontmatter + getExtraFields: (page) => page.frontmatter.tags ?? [], + }), + ], +} +``` + +## Styles + +You can customize the style of the search box via CSS variables: + +@[code css](@vuepress/plugin-search/src/client/styles/vars.css) + +## Components + +### SearchBox + +- Details: + + This plugin will register a ` ` component globally, and you can use it without any props. + + Put this component to where you want to place the search box. For example, default theme puts this component to the end of the navbar. + +::: tip +This component is mainly used for theme development. You don't need to use it directly in most cases. +::: diff --git a/docs/reference/plugin/shiki.md b/docs/reference/plugin/shiki.md new file mode 100644 index 00000000..44bd5132 --- /dev/null +++ b/docs/reference/plugin/shiki.md @@ -0,0 +1,63 @@ +# shiki + + + +This plugin will enable syntax highlighting for markdown code fence with [Shiki](https://shiki.matsu.io/). + +::: tip +[Shiki](https://shiki.matsu.io/) is the syntax highlighter being used by VSCode. It has higher fidelity, but it is slower than [Prism.js](https://prismjs.com/). + +You could consider disabling this plugin in `dev` mode to get better development experience. +::: + +## Usage + +```bash +npm i -D @vuepress/plugin-shiki@next +``` + +```ts +import { shikiPlugin } from '@vuepress/plugin-shiki' + +export default { + plugins: [ + shikiPlugin({ + // options + }), + ], +} +``` + +## Options + +### theme + +- Type: `IThemeRegistration` + +- Default: `'nord'` + +- Details: + + Theme of shiki. + + This option will be forwarded to `getHighlighter()` method of shiki. + +- Also see: + - [shiki > themes](https://github.com/shikijs/shiki/blob/master/docs/themes.md) + +### langs + +- Type: `(Lang | ILanguageRegistration)[]` + +- Default: `[]` + +- Details: + + Languages of shiki. + + This option will be forwarded to `getHighlighter()` method of shiki. + + If no languages are provided, shiki will load all available languages automatically. + +- Also see: + - [shiki > languages](https://github.com/shikijs/shiki/blob/master/docs/languages.md) diff --git a/docs/reference/plugin/theme-data.md b/docs/reference/plugin/theme-data.md new file mode 100644 index 00000000..211216af --- /dev/null +++ b/docs/reference/plugin/theme-data.md @@ -0,0 +1,118 @@ +# theme-data + + + +Provide client data for your theme, with VuePress [i18n](../../guide/i18n.md) support. + +This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases. + +For theme authors, this plugin will help you to use the same i18n mechanism as VuePress and the default theme. But if you don't want to provide i18n support, or you want to implement in your own way, you don't need this plugin. + +## Usage + +```bash +npm i -D @vuepress/plugin-theme-data@next +``` + +```ts +import { themeDataPlugin } from '@vuepress/plugin-theme-data' + +export default { + plugins: [ + themeDataPlugin({ + // options + }), + ], +} +``` + +## Options + +### themeData + +- Type: `ThemeData` + +- Default: `{}` + +- Details: + + The theme data object that you want to use in client side. + + You can provide theme data in Node side via this option, and use it in client side via [useThemeData](#useThemeData) and [useThemeLocaleData](#useThemeLocaleData). + +- Example: + +```ts +export default { + plugins: [ + themeDataPlugin({ + themeData: { + foo: 'foo', + locales: { + '/zh/': { + foo: 'zh-foo', + }, + }, + }, + }), + ], +} +``` + +::: warning +The theme data object will be processed by `JSON.stringify()` before forwarding to client side, so you should ensure that you are providing a JSON-friendly object. +::: + +## Composition API + +### useThemeData + +- Details: + + Returns the theme data ref object. + + The value is provided by [themeData](#themeData) option. + +- Example: + +```ts +import { useThemeData } from '@vuepress/plugin-theme-data/client' +import type { ThemeData } from '@vuepress/plugin-theme-data/client' + +type MyThemeData = ThemeData<{ + foo: string +}> + +export default { + setup() { + const themeData = useThemeData () + console.log(themeData.value) + }, +} +``` + +### useThemeLocaleData + +- Details: + + Returns the theme data ref object in current locale. + + The properties of current locale has been merged into the root-level properties. + +- Example: + +```ts +import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client' +import type { ThemeData } from '@vuepress/plugin-theme-data/client' + +type MyThemeData = ThemeData<{ + foo: string +}> + +export default { + setup() { + const themeLocaleData = useThemeLocaleData () + console.log(themeLocaleData.value) + }, +} +``` diff --git a/docs/reference/plugin/toc.md b/docs/reference/plugin/toc.md new file mode 100644 index 00000000..21f65f2a --- /dev/null +++ b/docs/reference/plugin/toc.md @@ -0,0 +1,169 @@ +# toc + + + +This plugin will provide a table-of-contents (TOC) component. + +## Usage + +```bash +npm i -D @vuepress/plugin-toc@next +``` + +```ts +import { tocPlugin } from '@vuepress/plugin-toc' + +export default { + plugins: [ + tocPlugin({ + // options + }), + ], +} +``` + +## Differences with Markdown TOC Syntax + +Similar to the [Table of Contents Markdown Syntax](../../guide/markdown.md#table-of-contents), the TOC component that provided by this plugin could be used in your markdown content directly: + +```md + +[[toc]] + + + +``` + +Both of them can be pre-rendered correctly in build mode. However, there are some differences between them. + +The markdown syntax `[[toc]]` could only be used in markdown files. It is parsed by markdown-it, and the generated TOC is static content. + +The component ` ` could be used in both markdown files and vue files. It is loaded by vue, and the generated TOC is a vue component. + +This plugin could work together with [@vuepress/plugin-active-header-links](./active-header-links.md) by setting the [headerLinkSelector](./active-header-links.md#headerlinkselector) to match the `linkClass` option. When the page scroll to a certain header anchor, this corresponding link will be added `linkActiveClass` class name. + +Therefore, this plugin is more useful for theme developers. + +## Options + +### componentName + +- Type: `string` + +- Default: `'Toc'` + +- Details: + + Specify the name of the TOC component. + +### defaultPropsOptions + +- Type: `Partial ` + +- Default: `{}` + +- Details: + + Override the default values of the component [options](#options-1) prop. + +## Component Props + +The TOC component also accepts props for customization. + +```vue + + + +``` + +### headers + +- Type: `PageHeader[]` + +```ts +interface PageHeader { + level: number + title: string + slug: string + children: PageHeader[] +} +``` + +- Details: + + Specify the headers array to render. + + If this prop is not specified, the headers of current page will be used. + +### options + +- Type: `Partial ` + +```ts +interface TocPropsOptions { + containerTag: string + containerClass: string + listClass: string + itemClass: string + linkTag: 'a' | 'RouterLink' + linkClass: string + linkActiveClass: string + linkChildrenActiveClass: string +} +``` + +- Default: + + Following default values can be overridden by [defaultPropsOptions](#defaultpropsoptions). + +```ts +const defaultOptions = { + containerTag: 'nav', + containerClass: 'vuepress-toc', + listClass: 'vuepress-toc-list', + itemClass: 'vuepress-toc-item', + linkTag: 'RouterLink', + linkClass: 'vuepress-toc-link', + linkActiveClass: 'active', + linkChildrenActiveClass: 'active', +} +``` + +- Details: + + Customize the TOC component. + + If the `containerTag` is set to an empty string `''`, the `