Merge branch 'main' into main

pull/4393/head
yuxi-ovo 2 months ago committed by GitHub
commit 4911bb6fa8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -23,7 +23,7 @@ Hi! We're really excited that you are interested in contributing to VitePress. B
## Development Setup
You will need [pnpm](https://pnpm.io)
You will need [Node.js](https://nodejs.org) v20 or higher and [pnpm](https://pnpm.io).
After cloning the repo, run:

@ -13,4 +13,4 @@
---
> [!TIP]
> The author of this PR can publish a _preview release_ by commenting `/publish` below.
> The author can publish a _preview release_ by commenting `/publish` after creating the PR.

@ -9,6 +9,7 @@ on:
types: [opened, synchronize, labeled, ready_for_review]
paths-ignore:
- '.github/**'
- '!.github/workflows/cr.yml'
- '__tests__/**'
- 'art/**'
- 'docs/**'
@ -17,6 +18,7 @@ on:
branches: [main]
paths-ignore:
- '.github/**'
- '!.github/workflows/cr.yml'
- '__tests__/**'
- 'art/**'
- 'docs/**'
@ -36,11 +38,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- uses: actions/setup-node@v4
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: pnpm
- run: pnpm install
- run: pnpm build

@ -12,7 +12,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Create Release for Tag
id: release_tag

@ -1,12 +1,14 @@
name: Close stale issues and PRs
on:
schedule:
- cron: '0 12 1,15 * *'
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
- uses: actions/stale@v10
with:
days-before-stale: 30
days-before-close: -1

@ -18,22 +18,22 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
node_version: [18, 20, 22]
node_version: [20, 22, 24, latest]
include:
- os: windows-latest
node_version: 22
node_version: 24
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
- name: Set node version to ${{ matrix.node_version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node_version }}
cache: pnpm

1
.gitignore vendored

@ -16,3 +16,4 @@ node_modules
pnpm-global
TODOs.md
*.timestamp-*.mjs
.claude

@ -1,2 +0,0 @@
shell-emulator=true
auto-install-peers=false

@ -1,3 +1,351 @@
## [2.0.0-alpha.17](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.16...v2.0.0-alpha.17) (2026-03-19)
### Bug Fixes
- **build:** `processIncludes` no longer swallows errors ([fb21fdf](https://github.com/vuejs/vitepress/commit/fb21fdf6759b7c88ac98456820b75286936fbf87))
- **build:** deterministic local search indexing and non-blocking initial scan (closes [#4081](https://github.com/vuejs/vitepress/issues/4081)) ([fc0f203](https://github.com/vuejs/vitepress/commit/fc0f203f0ac451ec039850b920f0684c1695d161))
- **client:** await `clipboard.writeText` so that fallback works in non-secure contexts ([#5119](https://github.com/vuejs/vitepress/issues/5119)) ([b544df8](https://github.com/vuejs/vitepress/commit/b544df802d256aef10d4da94631967b0fcab68d2))
- disable brotli compression in preview server ([ca1e875](https://github.com/vuejs/vitepress/commit/ca1e87512924acdd8d39ca0f530a424ad933b701))
- **theme:** add `rel="alternate"` and `hreflang` to links in language menu ([#5108](https://github.com/vuejs/vitepress/issues/5108)) ([f09593b](https://github.com/vuejs/vitepress/commit/f09593b073e42fa9d8de1e744a9f2a0e5c8170fc))
- **theme:** add margin to images in vp-doc (closes [#5136](https://github.com/vuejs/vitepress/issues/5136)) ([896e49a](https://github.com/vuejs/vitepress/commit/896e49a777bb983de2cbef82c1c295db2dba3dbe))
- **theme:** close sidebar on resize (closes [#5145](https://github.com/vuejs/vitepress/issues/5145)) ([17696c3](https://github.com/vuejs/vitepress/commit/17696c358b88335c4f6bb516bbcdf774aaf7deee))
- **theme:** disable text autospace in `<pre>` (closes [#5115](https://github.com/vuejs/vitepress/issues/5115)) ([21a5fb6](https://github.com/vuejs/vitepress/commit/21a5fb6a96549564fcf698d0e7256181eac3919b))
- **theme:** enable `line-break: strict` for `<li>` too ([f811d58](https://github.com/vuejs/vitepress/commit/f811d581a164fa330b17cdb85270effa39bdb3fd))
- **theme:** keep pnum,tnum features in Inter ([f1bef95](https://github.com/vuejs/vitepress/commit/f1bef95eee453bb18cc160741a11513678015cd6)), closes [#5096](https://github.com/vuejs/vitepress/issues/5096)
- **theme:** navbar menu group is incorrectly highlighted ([#5113](https://github.com/vuejs/vitepress/issues/5113)) ([b28667b](https://github.com/vuejs/vitepress/commit/b28667b6b03d50c4be0d509ec5e7d780f29f8042))
- **theme:** strip system-ui from font-family-base ([#4988](https://github.com/vuejs/vitepress/issues/4988)) ([8096eac](https://github.com/vuejs/vitepress/commit/8096eaca4f1d9eef00101e42f7159da1e12ca3de))
### Features
- **theme:** add `home-hero-actions-before-actions` slot ([#5151](https://github.com/vuejs/vitepress/issues/5151)) ([74a73d8](https://github.com/vuejs/vitepress/commit/74a73d869ca61f2c64f9dcbd9912a0461e8d4bfe))
- support text-fragments ([#5140](https://github.com/vuejs/vitepress/issues/5140)) ([44e2675](https://github.com/vuejs/vitepress/commit/44e2675889588b54dea771a0c82e2db64739364c))
- **theme:** add Japanese translation for "copied" label ([#5106](https://github.com/vuejs/vitepress/issues/5106)) ([a01bba0](https://github.com/vuejs/vitepress/commit/a01bba0cad70bb089918adab105f0a5b5ff85918))
- **theme:** enable text-autospace and text-spacing-trim (closes [#4996](https://github.com/vuejs/vitepress/issues/4996)) ([d7effff](https://github.com/vuejs/vitepress/commit/d7effffdbb9ea6bdeda31e26dea0f1ed32ddd51e))
- **theme:** support specifying list of details in home page features (closes [#5101](https://github.com/vuejs/vitepress/issues/5101)) ([3146ce4](https://github.com/vuejs/vitepress/commit/3146ce48ed3ad684ddfbf1194de3a2200082eb92))
### BREAKING CHANGES
Previously, the `<!-- @include: ./path/to/file -->` syntax silently ignored errors when files did not exist. This behavior was originally intended as an escape hatch while documenting includes, but better solutions now exist using Shiki transformers.
For most users, no code changes are required. If you now see errors, it means your includes are broken and were earlier not being reported.
Users who intentionally reference non-existent files or want to document includes without resolving them can configure `markdown.codeTransformers` with a `postprocess` hook. See `docs/.vitepress/config.ts` in this repo for an example.
## [2.0.0-alpha.16](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.15...v2.0.0-alpha.16) (2026-01-31)
### Bug Fixes
- always log error when failed to fetch page ([66cf64e](https://github.com/vuejs/vitepress/commit/66cf64e6d127dd8473e582d11e1133acda6c3bc8))
- **theme:** add fallback for `heroImageSlotExists` ([#5076](https://github.com/vuejs/vitepress/issues/5076)) ([f119b18](https://github.com/vuejs/vitepress/commit/f119b18e39b545f39e29358913fe9ed1fd69bc55))
- **theme:** align badges in h1 and h2 ([#5087](https://github.com/vuejs/vitepress/issues/5087)) (closes [#5063](https://github.com/vuejs/vitepress/issues/5063)) ([b200865](https://github.com/vuejs/vitepress/commit/b2008654ffaf0d8f8c425e88592e9ed2f8791506))
- **theme:** highlight active link in mobile nav menu ([#5086](https://github.com/vuejs/vitepress/issues/5086)) (closes [#5068](https://github.com/vuejs/vitepress/issues/5068), closes [#5074](https://github.com/vuejs/vitepress/issues/5074)) ([923aa90](https://github.com/vuejs/vitepress/commit/923aa902523739bfb9d77aed376ebc73c32eeb33))
- **theme:** overflow clip is buggy on safari ([8ed6ea0](https://github.com/vuejs/vitepress/commit/8ed6ea048cb49256e3302de2de0edfbe635afd32)), closes [#5050](https://github.com/vuejs/vitepress/issues/5050) [#5039](https://github.com/vuejs/vitepress/issues/5039) [#5027](https://github.com/vuejs/vitepress/issues/5027)
- **theme:** remove margin between code groups and blocks in markdown containers ([a28e171](https://github.com/vuejs/vitepress/commit/a28e171604605713a221d0eb2bbdce211930d94f)), closes [#5099](https://github.com/vuejs/vitepress/issues/5099)
### Features
- **theme:** upgrade DocSearch to 4.5 with sidepanel ([#5092](https://github.com/vuejs/vitepress/issues/5092)) ([0d646a6](https://github.com/vuejs/vitepress/commit/0d646a66cd44e97adef516a6a36e03365f179906))
- **theme:** use `@layer __vitepress_base` to wrap the styles in base.css ([#4905](https://github.com/vuejs/vitepress/issues/4905)) ([f8d8c0d](https://github.com/vuejs/vitepress/commit/f8d8c0d712fba4728c750e1f44c5ba9596979ba1))
## [2.0.0-alpha.15](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.14...v2.0.0-alpha.15) (2025-11-22)
### Bug Fixes
- **theme:** navbar overflowing on mobile devices ([06f0e1a](https://github.com/vuejs/vitepress/commit/06f0e1a5c92e36d86fd2e037c335af04d75384e7)), closes [#5039](https://github.com/vuejs/vitepress/issues/5039)
## [2.0.0-alpha.14](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.13...v2.0.0-alpha.14) (2025-11-21)
### Bug Fixes
- log dead links in dev mode too ([179ee62](https://github.com/vuejs/vitepress/commit/179ee621d99b3c14e2e098e3b786465cbeaeab9a)), closes [#4419](https://github.com/vuejs/vitepress/issues/4419)
- **theme:** sidebar alignment when scrollbar is there on page ([0ee7158](https://github.com/vuejs/vitepress/commit/0ee71588de2b1691b1a9287aa1daa729197fd3ca)), closes [#5027](https://github.com/vuejs/vitepress/issues/5027)
### Features
- **client:** emit `vitepress:codeGroupTabActivate` custom event when a code group tab is activated ([dfb02a4](https://github.com/vuejs/vitepress/commit/dfb02a479f19afbee9e292b15c3c2beef271e57f)), closes [#5023](https://github.com/vuejs/vitepress/issues/5023)
## [2.0.0-alpha.13](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.12...v2.0.0-alpha.13) (2025-11-13)
### Bug Fixes
- **client,a11y:** improve focus handling and scrolling behavior in router ([#4943](https://github.com/vuejs/vitepress/issues/4943)) ([d46107f](https://github.com/vuejs/vitepress/commit/d46107fa254d662d297b1362aa0d3b898ef96e2c))
- disable markdown-it-attrs for fenced code blocks ([0899618](https://github.com/vuejs/vitepress/commit/089961855653f862b71747e8179ef2647e06d626))
- git log parsing when there are empty commits in history ([#4965](https://github.com/vuejs/vitepress/issues/4965)) ([612c458](https://github.com/vuejs/vitepress/commit/612c45895df79a0c0e87ca040564bfe88ce04f62))
- print full path in dead links check ([2b77fb3](https://github.com/vuejs/vitepress/commit/2b77fb3a72058129edbaddd3c6f0f6ee24f983d5)), closes [#4919](https://github.com/vuejs/vitepress/issues/4919)
- rename `markdown.cjkFriendly` to `markdown.cjkFriendlyEmphasis` ([bce0b53](https://github.com/vuejs/vitepress/commit/bce0b53659fa3a57b2ed8431a0861939dadd118a)), closes [#4952](https://github.com/vuejs/vitepress/issues/4952)
- respect markdown.cache = false on build too ([6d7422f](https://github.com/vuejs/vitepress/commit/6d7422f8fa321c641b1d5be3fa0c382400a2b78f))
- simplify lang extraction logic; use markdown-it plugins in type-safe manner; bump deps ([4e548f5](https://github.com/vuejs/vitepress/commit/4e548f542469a366f327cdef1530bdb1a31542ad))
- **theme:** add lang and dir attributes to language picker ([f0b29d7](https://github.com/vuejs/vitepress/commit/f0b29d7ef32a33f61c355d19561176411ede4b48))
- **theme:** adjust margin of code blocks inside containers ([82fac5d](https://github.com/vuejs/vitepress/commit/82fac5d22c9e2b28d18dafcd458741a4b4d7a86b)), closes [#4921](https://github.com/vuejs/vitepress/issues/4921)
- **theme:** avoid use of `:where` in selector list for now ([c2eaccd](https://github.com/vuejs/vitepress/commit/c2eaccd0d2109a6c64cee9fe615e48daaf4eda0e)), closes [#4923](https://github.com/vuejs/vitepress/issues/4923)
- **theme:** disable whitespace wrapping for VPBadge ([#4968](https://github.com/vuejs/vitepress/issues/4968)) ([113d230](https://github.com/vuejs/vitepress/commit/113d2304784586028d9733036ccb585374731397))
- **theme:** use nav height css var for curtain top in sidebar ([#4993](https://github.com/vuejs/vitepress/issues/4993)) ([be260fd](https://github.com/vuejs/vitepress/commit/be260fda6efc1d6c4b56219d7a17a19ab7a4ba76))
### Features
- export cacheAllGitTimestamps and getGitTimestamp ([31d87e2](https://github.com/vuejs/vitepress/commit/31d87e27387ebdceb22c047cc5f821761276d5f7))
- **i18n,a11y:** change last update logic ([#4935](https://github.com/vuejs/vitepress/issues/4935)) ([187bf25](https://github.com/vuejs/vitepress/commit/187bf250e6496554fca0b070a5aba55484f7fc0b))
- **markdown:** support custom display-name for fenced code blocks ([#4960](https://github.com/vuejs/vitepress/issues/4960)) ([3d61619](https://github.com/vuejs/vitepress/commit/3d61619ec0f0458c7ae04e7954b72a8e2ff399c0))
- prevent `$` symbol selection in shell code ([#5025](https://github.com/vuejs/vitepress/issues/5025)) ([bf2715e](https://github.com/vuejs/vitepress/commit/bf2715ed67f290726fc6d4c85c203ca8f74cc907))
- **theme:** allow passing functions for nav links ([#4963](https://github.com/vuejs/vitepress/issues/4963)) ([34cfa91](https://github.com/vuejs/vitepress/commit/34cfa91b6f14d8adfaa2d3c9f3eb6ad8b889ef1c))
### Performance Improvements
- make a single git call for timestamps instead of calling it for each file ([#4958](https://github.com/vuejs/vitepress/issues/4958)) ([6dfcdd3](https://github.com/vuejs/vitepress/commit/6dfcdd3fe8dc73e7b4ad7783df9530dedac1f6bd))
### BREAKING CHANGES
- `markdown-it-attrs` is disabled for fenced code blocks. For most users no change is required. If you want to add classes to code blocks, do it using shiki transformers instead.
- Rename `cjkFriendly` to `cjkFriendlyEmphasis` in your vitepress config. Most people should be unaffected unless they want to disable the CJK emphasis behavior added v2.0.0-alpha.12.
## [2.0.0-alpha.12](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.11...v2.0.0-alpha.12) (2025-08-20)
### Bug Fixes
- **hmr:** don't load config twice on server restart ([d1a8061](https://github.com/vuejs/vitepress/commit/d1a8061eb438c730ccc62ce2d7158dbe89cc5292))
- **hmr:** no need for server restart on theme change ([d3a1567](https://github.com/vuejs/vitepress/commit/d3a15673bd0846c7837bcc4ff5a2e3239a02f1f9))
- **hmr:** hmr not working for snippet imports in dynamic routes ([914467e](https://github.com/vuejs/vitepress/commit/914467e17fb759a9722951a3fd7568eb3bc4d4e6))
- **theme:** fix local nav alignment and increase touch area ([43b36c0](https://github.com/vuejs/vitepress/commit/43b36c0c19c2b4696f8c38fdaf4318786ea7ae8e))
- **theme:** nav background doesn't extend fully and gap after sidebar with non-overlay scrollbars ([7df3052](https://github.com/vuejs/vitepress/commit/7df30525121a28a46cc6c802f3155ccff8effaca)), closes [#4653](https://github.com/vuejs/vitepress/issues/4653)
- **theme:** use clipboard-check instead of clipboard-copy for code copied icon ([1c8815d](https://github.com/vuejs/vitepress/commit/1c8815d53ed2d56b07938260df6566f1514f4bfc))
### Features
- add markdown-it-cjk-friendly ([9fc8462](https://github.com/vuejs/vitepress/commit/9fc8462726ccf1cdb78b6171c9f1f5964e79ca22)), closes [#3762](https://github.com/vuejs/vitepress/issues/3762) [#4752](https://github.com/vuejs/vitepress/issues/4752)
- make postcssIsolateStyles idempotent ([0944777](https://github.com/vuejs/vitepress/commit/094477789328b80cff45cd973efa16b6a4db0a27))
### BREAKING CHANGES
- [markdown-it-cjk-friendly](https://www.npmjs.com/package/markdown-it-cjk-friendly) is enabled by default. This intentionally deviates from the official commonmark spec for the benefit of CJK users. **For most users, no change is required.** If you were using hacks to patch `scanDelims`, you can remove those. To disable the plugin, set `markdown: { cjkFriendly: false }` in your vitepress config.
- `includeFiles` option in `postcssIsolateStyles` now defaults to `[/vp-doc\.css/, /base\.css/]`. You can remove explicit `includeFiles` if you were using it just to run it on `vp-doc.css`. To revert back to older behavior pass `includeFiles: [/base\.css/]`. The underlying implementation is changed and `transform` and `exclude` options are no longer supported. Use `postcss-prefix-selector` directly if you've advanced use cases.
## [2.0.0-alpha.11](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.10...v2.0.0-alpha.11) (2025-08-14)
### Bug Fixes
- hmr working only once for markdown files ([8d8a5ac](https://github.com/vuejs/vitepress/commit/8d8a5ac281f090cd097bece792d9dd3ef00e5545)), closes [#4909](https://github.com/vuejs/vitepress/issues/4909)
- html entities encoded twice in toc plugin ([8abbe29](https://github.com/vuejs/vitepress/commit/8abbe298d545de17d34a9bc1eb72af4c5a4b41b8)), closes [#4908](https://github.com/vuejs/vitepress/issues/4908)
## [2.0.0-alpha.10](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.9...v2.0.0-alpha.10) (2025-08-11)
### Bug Fixes
- **client:** base not stripped from relativePath in 404 pages ([b840877](https://github.com/vuejs/vitepress/commit/b840877aa83a5a24ffc1222e8a5a3dbf3e5105e8)), closes [#4850](https://github.com/vuejs/vitepress/issues/4850)
- hmr of style blocks in dynamic routes ([#4903](https://github.com/vuejs/vitepress/issues/4903)) ([3d0fafb](https://github.com/vuejs/vitepress/commit/3d0fafba545f4b5028cf43d86027dd44dab14421))
- make paths in `watchedFiles` absolute as mentioned in the docs ([318c14f](https://github.com/vuejs/vitepress/commit/318c14fa7c9fb949d74b7d9fae416e917766cf05))
- module graph causing unnecessary route regeneration on every update ([fc267ae](https://github.com/vuejs/vitepress/commit/fc267ae6b787e163d41666e090089821377ead43))
- preserve externally added dynamic routes and pages ([fc267ae](https://github.com/vuejs/vitepress/commit/fc267ae6b787e163d41666e090089821377ead43))
- **search:** input placeholder being cut off in smaller viewports ([162c6a6](https://github.com/vuejs/vitepress/commit/162c6a69bf56945daa20d126aa034c59ee0c8a2e))
- **search:** style tweaks for when searches are empty ([8b23217](https://github.com/vuejs/vitepress/commit/8b232171cc321bd3dc86b4357622815269f0b6f4))
- **types:** externalize markdown-it types ([5bf835b](https://github.com/vuejs/vitepress/commit/5bf835b5074e9567852d552bfb5115c6456026e8))
- **types:** pass generics deeply to user config ([777e2ca](https://github.com/vuejs/vitepress/commit/777e2caaacd93ce41b046f6c9d5ba80cc43ba37c))
### Features
- add source param to the deadlink check fn ([#4870](https://github.com/vuejs/vitepress/issues/4870)) ([8c027c2](https://github.com/vuejs/vitepress/commit/8c027c2a7c443074fd0d4890f7736b444f9254aa))
- **theme:** add `rel="me"` to social links by default ([#4873](https://github.com/vuejs/vitepress/issues/4873)) ([34886c6](https://github.com/vuejs/vitepress/commit/34886c667d1305a79d64c957f8c52931ea122f47))
## [2.0.0-alpha.9](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.8...v2.0.0-alpha.9) (2025-07-26)
### Bug Fixes
- **md:** pass container option to gitHubAlertsPlugin ([#4848](https://github.com/vuejs/vitepress/issues/4848)) ([52f0eaa](https://github.com/vuejs/vitepress/commit/52f0eaa0849344aa45efbf7258a6287597e55a9a))
- **theme:** remove duplicate text in sponsors grid ([3c51b22](https://github.com/vuejs/vitepress/commit/3c51b22ac98a12f193081d23799cb9f3f2ecf682)), closes [#4854](https://github.com/vuejs/vitepress/issues/4854)
### Features
- **search:** upgrade search to DocSearch v4-beta ([#4843](https://github.com/vuejs/vitepress/issues/4843)) ([ac61abe](https://github.com/vuejs/vitepress/commit/ac61abe7d7be5ef8b6939f18192896538eba1b8c))
### BREAKING CHANGES
- **search:** Uses DocSearch v4 beta. No change is required if you're not customizing the styles of navbar search button or modal. DocSearch AI features are in private beta, you can apply for them at https://forms.gle/iyfb5pC2CiiwszUKA
## [2.0.0-alpha.8](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.7...v2.0.0-alpha.8) (2025-07-08)
### Bug Fixes
- adjust glob logic to always resolve glob relative to base ([5d41785](https://github.com/vuejs/vitepress/commit/5d41785ff7b016b08f587f1ef3318fc18d58f6ab)), closes [#4822](https://github.com/vuejs/vitepress/issues/4822)
- **build:** ignore escaped `:` when splitting selector in `postcssIsolateStyles` ([#4830](https://github.com/vuejs/vitepress/issues/4830)) ([a629b03](https://github.com/vuejs/vitepress/commit/a629b03f0ee8a29d73a18481399d7de1c992faf2))
- font preload not being generated in rolldown-vite ([ed387e8](https://github.com/vuejs/vitepress/commit/ed387e89d42a08c15a9f45c9c5e11c6750245490))
- **theme:** remove extra slash when concatenating base with sidebar links ([c8fc80e](https://github.com/vuejs/vitepress/commit/c8fc80e438fffd98feaf7c72263bc3077792c4a2))
## [2.0.0-alpha.7](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.6...v2.0.0-alpha.7) (2025-06-24)
### Bug Fixes
- **local-search:** parse headings with non-anchor `a` tags as titles properly ([#4809](https://github.com/vuejs/vitepress/issues/4809)) ([5359903](https://github.com/vuejs/vitepress/commit/53599039a01af6d8e17a6a6e9cea5c222cc5948c))
- resolve pages after setting global vitepress config ([56ba65e](https://github.com/vuejs/vitepress/commit/56ba65e1301454df88f9a3856fa1a70dc052d314)), closes [#4803](https://github.com/vuejs/vitepress/issues/4803)
### Features
- **router:** add `replace` option to `useRouter` for history management ([#4788](https://github.com/vuejs/vitepress/issues/4788)) ([23541b4](https://github.com/vuejs/vitepress/commit/23541b4f83726cdac09ffcaf9141bba871cda690)), closes [#4787](https://github.com/vuejs/vitepress/issues/4787)
- consistent glob options across content, data, and path loaders ([#4808](https://github.com/vuejs/vitepress/issues/4808)) ([7619521](https://github.com/vuejs/vitepress/commit/76195212596cd54095240246b7e78075ac3cbc27)), closes [#4807](https://github.com/vuejs/vitepress/issues/4807)
- bump to vite 7 ([2ecd607](https://github.com/vuejs/vitepress/commit/2ecd607af15222eeddf0b888a72d0f913f5a3cd2))
### Performance Improvements
- render pages in contentLoader asynchronously ([36148a0](https://github.com/vuejs/vitepress/commit/36148a0bcf3a73d1fe3f0c5f33337b679f700053))
### BREAKING CHANGES
- Only `cwd`, `ignore`, `dot` and `debug` are supported in `globOptions` of `createContentLoader`. If you want to pass other options, you still can but you might need to suppress type errors.
- Uses vite 7. See [vite migration guide](https://vite.dev/guide/migration.html) for more info. For most of the users no change is required. VitePress should work same as earlier, except for maybe some type mismatches if you're using third-party plugins. You can suppress them using `@ts-expect-error` or `as any` and report the issues at respective repositories.
## [2.0.0-alpha.6](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.5...v2.0.0-alpha.6) (2025-06-12)
### Bug Fixes
- allow AdditionalConfigLoader to return void ([906a44a](https://github.com/vuejs/vitepress/commit/906a44a3ad488a46804757326af95cfb8cac6b75))
- **build:** avoiding creating separate chunks for vite public assets ([21f24b9](https://github.com/vuejs/vitepress/commit/21f24b9994ea4807ac7e0be38408e9aaa3abe8a9))
- **build:** emit lean chunks after vite has done processing ([26cb685](https://github.com/vuejs/vitepress/commit/26cb685adf54f07fe3e9fd7bfd49a0ff79956923)), closes [#4737](https://github.com/vuejs/vitepress/issues/4737)
- **client:** properly skip removed lines when copying code blocks ([c128baf](https://github.com/vuejs/vitepress/commit/c128baf0c41d5113c1b876f691e0185201b1f500))
- disable appearance scripts in zero-js mode ([e7f9d05](https://github.com/vuejs/vitepress/commit/e7f9d05c3e2ef4f4c1db3b2c17e586f0fc26a6f6)), closes [#4766](https://github.com/vuejs/vitepress/issues/4766)
- don't preload dynamic imports ([801648a](https://github.com/vuejs/vitepress/commit/801648a4c9d91e7f96302932ac9247d5bdd64ef7)), closes [#4770](https://github.com/vuejs/vitepress/issues/4770)
- gather additional config files even if root .vitepress/config is not present ([26f178c](https://github.com/vuejs/vitepress/commit/26f178cfaa330a017bb69b1ec6bd482d63a100a9))
- set `preserveEntrySignatures` for rolldown-vite ([#4784](https://github.com/vuejs/vitepress/issues/4784)) ([4351bc0](https://github.com/vuejs/vitepress/commit/4351bc0b831277401e08b350d7d7c0ab9ea0c9ed))
- skip fields not supported by rolldown for rolldown-vite ([#4747](https://github.com/vuejs/vitepress/issues/4747)) ([4e3fce4](https://github.com/vuejs/vitepress/commit/4e3fce40c9bab261f3c5e31833475c3e2c6ba0cf))
- **theme/regression:** code blocks not aligned properly in rtl layouts ([a643347](https://github.com/vuejs/vitepress/commit/a64334753079a5b874a482508d9ee255d2a0ea38))
- **theme:** hide native search input cancel button ([#4723](https://github.com/vuejs/vitepress/issues/4723)) ([2c4944f](https://github.com/vuejs/vitepress/commit/2c4944f06ccf46fcf58fb18a1819fd167c9533cc))
- **theme:** prevent error in handleSearchHotKey method ([#4782](https://github.com/vuejs/vitepress/issues/4782)) ([21fcecc](https://github.com/vuejs/vitepress/commit/21fcecce0581d0c461bc15e03429f61ff444a655))
- use v-pre for mathjax instead of isCustomElement ([c9b8928](https://github.com/vuejs/vitepress/commit/c9b89282f3573998cfc4103bbddbd73d2529cb66))
### Features
- use `oxc-minify` instead of `transformWithEsbuild` when rolldown-vite is used ([#4748](https://github.com/vuejs/vitepress/issues/4748)) ([7c1dc48](https://github.com/vuejs/vitepress/commit/7c1dc48b2fd08e128f7bbe26690fb6534dfb4b95))
## [2.0.0-alpha.5](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.4...v2.0.0-alpha.5) (2025-04-21)
### Bug Fixes
- don't remove shiki styles from `pre` and remove unnecessary transformers (#4652) ([db58af5](https://github.com/vuejs/vitepress/commit/db58af5c66e563e7663084057a9853d8f2da984c)), closes [#4652](https://github.com/vuejs/vitepress/issues/4652)
- normalize url fragments in internal links to correctly resolve to anchors ([#4628](https://github.com/vuejs/vitepress/issues/4628)) ([e25d080](https://github.com/vuejs/vitepress/commit/e25d0805505db2f1116e99d38a488d5cb39ed426)), closes [#4605](https://github.com/vuejs/vitepress/issues/4605)
- **theme-default:** ensure proper sizing of SVG hero images ([#4639](https://github.com/vuejs/vitepress/issues/4639)) ([7d94481](https://github.com/vuejs/vitepress/commit/7d9448192079e59493aa5c1e86cdf6d6deae8e36))
### Features
- add `isHome` frontmatter option (#4673) ([544cd81](https://github.com/vuejs/vitepress/commit/544cd8125985b9e3af7fee68ea9592d159799e01)), closes [#4673](https://github.com/vuejs/vitepress/issues/4673)
- add `custom-block-title-default` class when default title is used for containers ([#4643](https://github.com/vuejs/vitepress/issues/4643)) ([63079bf](https://github.com/vuejs/vitepress/commit/63079bff03b15861d174199f7361a2aff84380e0))
- add `dir=ltr` by default on code block pre elements instead of relying on css ([19faa16](https://github.com/vuejs/vitepress/commit/19faa16169b44f52bedf1401b4a97b2a8ffdeacb))
- **default-theme:** make VPButton slottable ([#4689](https://github.com/vuejs/vitepress/issues/4689)) ([0b70397](https://github.com/vuejs/vitepress/commit/0b7039719782e85119ad22be5c89ef3d233ffaae))
- support distributed config files ([#4660](https://github.com/vuejs/vitepress/issues/4660)) ([c5e2e4d](https://github.com/vuejs/vitepress/commit/c5e2e4db818c06f3c1b458753f22fb6ec1609628))
- **theme:** make "Take me home" button's link customizable ([#4658](https://github.com/vuejs/vitepress/issues/4658)) ([0267dca](https://github.com/vuejs/vitepress/commit/0267dcafa20beea24ef359d24bb1fa99e1ffda49))
### Performance Improvements
- call `module.enableCompileCache()` ([70de34c](https://github.com/vuejs/vitepress/commit/70de34c0387d9668ada3ea9a795f9ebee3535f5b))
- hoist expensive operations in useLayout ([e5ab067](https://github.com/vuejs/vitepress/commit/e5ab0676a9a8dc607e213eb691439b2e4ee472b7))
### BREAKING CHANGES
- `useLocalNav` and `useSidebar` are removed in favor of `useLayout`. To migrate, just do find and replace. Sidebar controls are no longer exported, but we didn't find any usage on GitHub. If there is demand, we can export respective composables later. `DefaultTheme.DocSidebar` and `DefaultTheme.DocLocalNav` types are also removed.
- `vp-adaptive-theme` class is no longer added to code blocks when there is single theme. Theme authors supporting single code theme can use `.shiki:not(.shiki-themes)` as selector. Alternatively, it might be better to use the bg/fg variables set on the `.shiki` block to keep things generic.
- `vp-code` class is no longer added to code blocks. Use `.shiki` or `pre.shiki` or `[class*='language-'] pre` instead. People not customizing their themes are not affected.
## [2.0.0-alpha.4](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.3...v2.0.0-alpha.4) (2025-03-09)
### Bug Fixes
- **build/regression:** langAlias not working ([06ae2bf](https://github.com/vuejs/vitepress/commit/06ae2bf3a4ee02351530b0bd055e577ca6509d62)), closes [#4581](https://github.com/vuejs/vitepress/issues/4581)
- don't hardcode `tabindex` attr in table renderer ([#4082](https://github.com/vuejs/vitepress/issues/4082)) ([aadc517](https://github.com/vuejs/vitepress/commit/aadc517c69fb239bdda99173bbc123ace567484b))
- hmr not working for watched files in path loaders ([e271695](https://github.com/vuejs/vitepress/commit/e271695d716247455ca620948f814e6c8ca0e3c4)), closes [#4525](https://github.com/vuejs/vitepress/issues/4525)
- ignore non-text content in permalink generation and fix types of markdown.config ([a8a1800](https://github.com/vuejs/vitepress/commit/a8a1800ae578be88027aa4ec7561ada4d055b888))
- prevent reload on first server start in fresh installations ([d8a884e](https://github.com/vuejs/vitepress/commit/d8a884ed0f754523765058a70149cdbaf6942341))
- properly merge classes in custom containers ([#4128](https://github.com/vuejs/vitepress/issues/4128)) ([8aad617](https://github.com/vuejs/vitepress/commit/8aad617446c03d39a65a0b21e9fce43bc484af1e))
- rebuild dynamic routes cache on server restart ([9f54714](https://github.com/vuejs/vitepress/commit/9f54714e7db69fd4902f1917f927456c71b5a292)), closes [#4525](https://github.com/vuejs/vitepress/issues/4525)
### Features
- allow matching region end in snippets without tag ([#4287](https://github.com/vuejs/vitepress/issues/4287)) ([1a2f81d](https://github.com/vuejs/vitepress/commit/1a2f81de4d6549dd1adf86ae131d1a861158bd2d))
- improve region regexes for snippet plugin ([1a6684c](https://github.com/vuejs/vitepress/commit/1a6684cf1054d326bc1dd6eeb9fb78b150ac2b2a))
- support using header anchors in markdown file inclusion ([#4608](https://github.com/vuejs/vitepress/issues/4608)) ([b99d512](https://github.com/vuejs/vitepress/commit/b99d5123c9b2afdc7461089e03476c34d7816faf)), closes [#4375](https://github.com/vuejs/vitepress/issues/4375) [#4382](https://github.com/vuejs/vitepress/issues/4382)
## [2.0.0-alpha.3](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.2...v2.0.0-alpha.3) (2025-02-24)
### Bug Fixes
- **build:** `--minify` not working as documented ([9b5c037](https://github.com/vuejs/vitepress/commit/9b5c0377cd3474447c84b2901801287f3caf3d82)), closes [#4523](https://github.com/vuejs/vitepress/issues/4523)
- **build:** deterministic code group ids ([#4565](https://github.com/vuejs/vitepress/issues/4565)) ([b930b8d](https://github.com/vuejs/vitepress/commit/b930b8d5310f1691d8d9f009f45b70122e4ce800))
- **markdown:** include content of all tokens in heading ids ([68dff2a](https://github.com/vuejs/vitepress/commit/68dff2af8547ae70f6622ac826affd76f2f6378e)), closes [#4561](https://github.com/vuejs/vitepress/issues/4561)
- **client:** set correct oldURL and newURL for hashchange ([#4573](https://github.com/vuejs/vitepress/issues/4573)) ([d1f2afd](https://github.com/vuejs/vitepress/commit/d1f2afdf0fbb022f12cc12295723b3b7c7ef5cb1))
- **theme:** allow interactions behind scroll shadow ([#4537](https://github.com/vuejs/vitepress/issues/4537)) ([091d584](https://github.com/vuejs/vitepress/commit/091d5840ae15b64e04e8c07fbc0263a2749571bd))
- **theme:** code block contrast ratio ([#4487](https://github.com/vuejs/vitepress/issues/4487)) ([5dccaee](https://github.com/vuejs/vitepress/commit/5dccaeef055beb109919f8990032975a0d630384))
- **build:** fix flaky embedded languages highlighting ([#4566](https://github.com/vuejs/vitepress/issues/4566)) ([1969cf4](https://github.com/vuejs/vitepress/commit/1969cf4f3b93ad105595e4e2f8b030b04eb1c975))
### Features
- **cli:** support custom `srcDir` ([#4270](https://github.com/vuejs/vitepress/issues/4270)) ([518c094](https://github.com/vuejs/vitepress/commit/518c0945f159aae679ef710bb48ae3ab3891cc9f))
- **cli:** support custom npm scripts prefix ([#4271](https://github.com/vuejs/vitepress/issues/4271)) ([e5a0ee8](https://github.com/vuejs/vitepress/commit/e5a0ee8161752a77c5bb9546245a940cb5f28fb8))
- **build:** dynamic routes plugin overhaul ([#4525](https://github.com/vuejs/vitepress/issues/4525)) ([a62ea6a](https://github.com/vuejs/vitepress/commit/a62ea6a832a33b756642b24ad5d38c248e08b554))
- **build:** update to shiki v3 ([#4571](https://github.com/vuejs/vitepress/issues/4571)) ([52c2aa1](https://github.com/vuejs/vitepress/commit/52c2aa178d4b3fa98b863cf28f0ccf6d2aabcd93))
- **build:** use `markdown-it-async`, remove `synckit` ([#4507](https://github.com/vuejs/vitepress/issues/4507)) ([8062235](https://github.com/vuejs/vitepress/commit/80622356f1d648577ee47ee3a44b04bb015ee462))
### BREAKING CHANGES
- markdown-it-async is used instead of markdown-it. If you're using custom content renderer for local search, you'll need to do `await md.renderAsync` instead of `md.render`.
- Internals are modified a bit to better support vite 6 and handle HMR more correctly. For most users this won't need any change on their side.
- shiki is upgraded to v3. There shouldn't be any breaking change but if you see any issue, please report it.
## [2.0.0-alpha.2](https://github.com/vuejs/vitepress/compare/v2.0.0-alpha.1...v2.0.0-alpha.2) (2025-01-23)
### Bug Fixes
- fix docsearch navigation and rendering ([e035027](https://github.com/vuejs/vitepress/commit/e0350275b39258a61ee867840ce1c6f5b2cecf2a))
- **types:** support preload built-in shiki languages as string ([#4513](https://github.com/vuejs/vitepress/issues/4513)) ([4f77b4f](https://github.com/vuejs/vitepress/commit/4f77b4fdfdbe945e482348a57731bff5fb4672fc))
### Features
- allow `markdown.config` and `markdown.preConfig` to accept async function ([#4512](https://github.com/vuejs/vitepress/issues/4512)) ([b88ae8d](https://github.com/vuejs/vitepress/commit/b88ae8d4a11a20104b2007c2631eb7aeb123d965))
- support same page navigation in `router.go` and expose decoded hash and query from the `route` object ([#4511](https://github.com/vuejs/vitepress/issues/4511)) ([23d3281](https://github.com/vuejs/vitepress/commit/23d3281ed6f1111ab15708ca1fd86202674f8ef7))
## [2.0.0-alpha.1](https://github.com/vuejs/vitepress/compare/v1.6.2...v2.0.0-alpha.1) (2025-01-22)
### Features
- upgrade vite to v6 ([#4504](https://github.com/vuejs/vitepress/issues/4504)) ([6a2efc3](https://github.com/vuejs/vitepress/commit/6a2efc385c90b088241db05f5263b2f3e1f757cf))
## [1.6.3](https://github.com/vuejs/vitepress/compare/v1.6.2...v1.6.3) (2025-01-22)
### Bug Fixes
- docsearch not rendering properly ([3e4120e](https://github.com/vuejs/vitepress/commit/3e4120e94805156bf63587fd633162433dbaf260))
## [1.6.2](https://github.com/vuejs/vitepress/compare/v1.6.1...v1.6.2) (2025-01-22)
### Bug Fixes
- fix static content removal for lean chunks due to Vue 3.5 changes ([#4508](https://github.com/vuejs/vitepress/issues/4508)) ([8214cae](https://github.com/vuejs/vitepress/commit/8214cae21bb16842d8870d5867e974146c51fd61))
## [1.6.1](https://github.com/vuejs/vitepress/compare/v1.6.0...v1.6.1) (2025-01-20)
### Bug Fixes
- **build:** escape `$` in replace pattern in dynamic routes plugin ([e812916](https://github.com/vuejs/vitepress/commit/e8129167c76104d59d31a77b16dff3458e6af5eb)), closes [#4499](https://github.com/vuejs/vitepress/issues/4499)
- **theme/regression:** broken hero heading at certain viewports ([37dbe89](https://github.com/vuejs/vitepress/commit/37dbe895d4cf813e6eb1289f24c637945eec0d1f))
# [1.6.0](https://github.com/vuejs/vitepress/compare/v1.5.0...v1.6.0) (2025-01-20)
### Bug Fixes
- **build:** out of order css in prod builds ([241d17d](https://github.com/vuejs/vitepress/commit/241d17d9839f06b17c3898b1a8ba0f9fa12da0d1)), closes [#4098](https://github.com/vuejs/vitepress/issues/4098)
- **build:** properly strip vpi-social css declaration in debug mode ([c61182a](https://github.com/vuejs/vitepress/commit/c61182ab278350699b5d50461788478a340790aa))
- **build:** respect `vite.clearScreen` in build ([8ea776a](https://github.com/vuejs/vitepress/commit/8ea776addc2c3bcabf3c707a9a81d6e0080a8fcb)), closes [#4468](https://github.com/vuejs/vitepress/issues/4468)
- **build:** specify mode for iconify ([8a5e8ea](https://github.com/vuejs/vitepress/commit/8a5e8ea4f5b7cba0a6c909d8949f0c20426104a6))
- **theme:** apply `externalLinkIcon` option on `VPHome` ([#4492](https://github.com/vuejs/vitepress/issues/4492)) ([fe48943](https://github.com/vuejs/vitepress/commit/fe48943640895d859811b81f86d78c3e510dbe54))
- **theme:** don't show external link icon for images ([096bba1](https://github.com/vuejs/vitepress/commit/096bba19fb61c4b2f8f527046b4b0fe2e91c6bd6))
- **theme:** ignore footnote-ref for outline ([1832617](https://github.com/vuejs/vitepress/commit/183261753b04c2c96ddb8c10e520c748c6d3e613)), closes [#4402](https://github.com/vuejs/vitepress/issues/4402)
- **theme:** includes text to h1 tag for hero page ([#4472](https://github.com/vuejs/vitepress/issues/4472)) ([bd896c6](https://github.com/vuejs/vitepress/commit/bd896c638f8046f6546b5b32e8f98f3707aa8d05)), closes [#4453](https://github.com/vuejs/vitepress/issues/4453)
### Features
- **build:** export normalize function from shared chunk ([616f63f](https://github.com/vuejs/vitepress/commit/616f63f5f08a57347f2800e2d147d5bcd1cd072d)), closes [#4401](https://github.com/vuejs/vitepress/issues/4401)
- **theme:** allow customizing skip to content label ([ff254dc](https://github.com/vuejs/vitepress/commit/ff254dcbe6f2bcc89c34d2d2f4050229dc094400)), closes [#4288](https://github.com/vuejs/vitepress/issues/4288)
- **theme:** export VPNavBarSearch ([23522ab](https://github.com/vuejs/vitepress/commit/23522ab83ff33802d382fa066578dd87eb06789d)), closes [#4476](https://github.com/vuejs/vitepress/issues/4476)
- **theme:** export VPFeatures ([#4356](https://github.com/vuejs/vitepress/issues/4356)) ([6442e17](https://github.com/vuejs/vitepress/commit/6442e174838aec9668325bb1199419908e7dd728))
### Miscellaneous
- **build:** shiki transformers now use v3 [matching algorithm](https://shiki.style/packages/transformers#matching-algorithm) ([373f9b9](https://github.com/vuejs/vitepress/commit/373f9b933ee44f33a15ebdcfcb6db6dfac52f739))
# [1.5.0](https://github.com/vuejs/vitepress/compare/v1.4.5...v1.5.0) (2024-11-04)
### Features
@ -2093,7 +2441,6 @@ This version uses Vue 3.2.0.
### BREAKING CHANGES
- Some config options have changed.
- `vueOptions` renamed to `vue`
- `alias` option has been removed. Use `vite.resovle.alias` instead.
@ -2111,7 +2458,6 @@ This version uses Vue 3.2.0.
### BREAKING CHANGES
- The following methods are removed.
- `useSiteData`
- `useSiteDataByRoute`
- `usePageData`

@ -1,7 +1,7 @@
# VitePress 📝💨
[![test](https://github.com/vuejs/vitepress/workflows/Test/badge.svg)](https://github.com/vuejs/vitepress/actions)
[![npm](https://img.shields.io/npm/v/vitepress)](https://www.npmjs.com/package/vitepress)
[![test](https://github.com/vuejs/vitepress/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/vuejs/vitepress/actions/workflows/test.yml)
[![npm](https://img.shields.io/npm/v/vitepress/next)](https://www.npmjs.com/package/vitepress/v/next)
[![nightly releases](https://img.shields.io/badge/nightly-releases-orange)](https://nightly.akryum.dev/vuejs/vitepress)
[![chat](https://img.shields.io/badge/chat-discord-blue?logo=discord)](https://chat.vuejs.org)
@ -26,3 +26,14 @@ Please make sure to read the [Contributing Guide](https://github.com/vuejs/vitep
[MIT](https://github.com/vuejs/vitepress/blob/main/LICENSE)
Copyright (c) 2019-present, Yuxi (Evan) You
## Special Thanks
This project would not be possible without the support of these amazing companies and tools.
| | | |
| ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <a href="https://voidzero.dev" target="_blank"><img alt="VoidZero's logo" src="https://github.com/voidzero-dev.png" width="80" /></a> | **[VoidZero](https://voidzero.dev)** | The company behind Vite, Vitest, Rolldown, and Oxc. VoidZero has been a long-time sponsor of VitePress. VitePress itself is built on top of this ecosystem of high-performance JavaScript tooling. |
| <a href="https://stackblitz.com" target="_blank"><img alt="StackBlitz's logo" src="https://github.com/stackblitz.png" width="80" /></a> | **[StackBlitz](https://stackblitz.com)** | Creators of WebContainers and a browser-based development platform. Their technology powers the VitePress playground and provides instant preview releases for our pull requests and commits. |
| <a href="https://algolia.com" target="_blank"><img alt="Algolia's logo" src="https://github.com/algolia.png" width="80" /></a> | **[Algolia](https://algolia.com)** | An AI-powered search platform that provides fast, scalable search. Algolia powers DocSearch for VitePress. Their team has been a long-time partner in building and maintaining our search integration. |
| <a href="https://browserstack.com" target="_blank"><img alt="BrowserStack's logo" src="https://github.com/browserstack.png" width="80" /></a> | **[BrowserStack](https://browserstack.com)** | A cloud-based testing platform for websites and mobile apps. BrowserStack provides the cross-browser and real-device infrastructure used to ensure VitePress works consistently across all browsers. |

@ -165,13 +165,21 @@ export default defineConfig({
search: {
provider: 'local',
options: {
_render(src, env, md) {
const html = md.render(src, env)
async _render(src, env, md) {
const html = await md.renderAsync(src, env)
if (env.frontmatter?.search === false) return ''
if (env.relativePath.startsWith('local-search/excluded')) return ''
return html
}
}
}
},
vite: {
server: {
watch: {
usePolling: true,
interval: 100
}
}
}
})

@ -30,7 +30,7 @@ function removeSpaces(str: string) {
<input
type="radio"
:id="option.key"
:name="name"
:name
:value="option.value"
v-model="selected"
/>

@ -0,0 +1,14 @@
<script setup lang="ts">
import DefaultTheme from 'vitepress/theme'
import HomeHeroCopy from './HomeHeroCopy.vue'
const INSTALL_COMMAND = 'npx vitepress init'
</script>
<template>
<DefaultTheme.Layout>
<template #home-hero-actions-before-actions>
<HomeHeroCopy :command="INSTALL_COMMAND" />
</template>
</DefaultTheme.Layout>
</template>

@ -0,0 +1,210 @@
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps<{
command: string
}>()
const copied = ref(false)
async function copy () {
try {
await navigator.clipboard.writeText(props.command)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
} catch (error) {
console.error('Failed to copy', error)
}
}
</script>
<template>
<div
class="hero-copy-btn"
:class="{ copied }"
role="button"
tabindex="0"
@click="copy"
@keydown.enter="copy"
@keydown.space.prevent="copy"
>
<div class="content">
<span class="prompt">$</span>
<span class="command">{{ command }}</span>
</div>
<div class="icon-wrapper">
<transition mode="out-in" name="fade">
<svg
v-if="!copied"
class="lucide lucide-copy"
fill="none"
height="18"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<rect
height="14"
rx="2"
ry="2"
width="14"
x="8"
y="8"
/>
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
</svg>
<svg
v-else
class="lucide lucide-check"
fill="none"
height="18"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M20 6 9 17l-5-5" />
</svg>
</transition>
</div>
<transition name="slide-up">
<div v-if="copied" class="tooltip">Copied!</div>
</transition>
</div>
</template>
<style scoped>
.hero-copy-btn {
margin-bottom: 32px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
background: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 0 16px;
height: 48px;
cursor: pointer;
transition: all 0.25s ease;
position: relative;
user-select: none;
max-width: calc(100vw - 2 * 24px);
width: 100%;
}
.hero-copy-btn:hover {
border-color: var(--vp-c-brand-1);
background: var(--vp-c-bg-mute);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-1px);
}
.hero-copy-btn:active {
transform: translateY(0);
}
.hero-copy-btn.copied {
border-color: var(--vp-c-green-1);
background: var(--vp-c-green-dimm-1);
}
.content {
display: flex;
align-items: center;
gap: 10px;
font-family: var(--vp-font-family-mono);
font-size: 14px;
color: var(--vp-c-text-2);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.prompt {
color: var(--vp-c-text-3);
user-select: none;
font-weight: 600;
}
.command {
color: var(--vp-c-text-1);
font-weight: 500;
}
.icon-wrapper {
display: flex;
align-items: center;
justify-content: center;
color: var(--vp-c-text-3);
transition: color 0.2s;
flex-shrink: 0;
}
.hero-copy-btn:hover .icon-wrapper {
color: var(--vp-c-text-1);
}
.hero-copy-btn.copied .icon-wrapper {
color: var(--vp-c-green-1);
}
.tooltip {
position: absolute;
top: -36px;
right: 0;
background: var(--vp-c-text-1);
color: var(--vp-c-bg);
padding: 6px 10px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
pointer-events: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10;
}
/* Tooltip arrow */
.tooltip::after {
content: '';
position: absolute;
bottom: -4px;
right: 12px;
width: 8px;
height: 8px;
background: var(--vp-c-text-1);
transform: rotate(45deg);
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: scale(0.8);
}
.slide-up-enter-active,
.slide-up-leave-active {
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.slide-up-enter-from,
.slide-up-leave-to {
opacity: 0;
transform: translateY(10px);
}
</style>

@ -1,10 +1,12 @@
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import CustomLayout from './components/CustomLayout.vue'
import ApiPreference from './components/ApiPreference.vue'
import NavVersion from './components/NavVersion.vue'
export default {
extends: DefaultTheme,
Layout: CustomLayout,
enhanceApp({ app }) {
app.component('ApiPreference', ApiPreference)
app.component('NavVersion', NavVersion)

@ -43,8 +43,7 @@ describe('static data file support in vite 3', () => {
`)
})
// TODO: make it `.runIf(!process.env.VITE_TEST_BUILD)` -- it currently works, but is skipped to avoid vite's ecosystem-ci from failing (https://github.com/vitejs/vite/pull/16471#issuecomment-2308437187)
test.skip('hmr works', async () => {
test.runIf(!process.env.VITE_TEST_BUILD)('hmr works', async () => {
const a = fileURLToPath(new URL('./data/a.json', import.meta.url))
const b = fileURLToPath(new URL('./data/b.json', import.meta.url))
@ -53,9 +52,7 @@ describe('static data file support in vite 3', () => {
await page.waitForFunction(
() =>
document.querySelector('pre#basic')?.textContent ===
JSON.stringify([{ a: false }, { b: true }], null, 2),
undefined,
{ timeout: 3000 }
JSON.stringify([{ a: false }, { b: true }], null, 2)
)
} finally {
await fs.writeFile(a, JSON.stringify({ a: true }, null, 2) + '\n')
@ -68,9 +65,7 @@ describe('static data file support in vite 3', () => {
await page.waitForFunction(
() =>
document.querySelector('pre#basic')?.textContent ===
JSON.stringify([{ a: true }], null, 2),
undefined,
{ timeout: 3000 }
JSON.stringify([{ a: true }], null, 2)
)
err = false
} finally {
@ -84,9 +79,7 @@ describe('static data file support in vite 3', () => {
await page.waitForFunction(
() =>
document.querySelector('pre#basic')?.textContent ===
JSON.stringify([{ a: true }, { b: false }], null, 2),
undefined,
{ timeout: 3000 }
JSON.stringify([{ a: true }, { b: false }], null, 2)
)
} finally {
await fs.writeFile(b, JSON.stringify({ b: true }, null, 2) + '\n')

@ -1,8 +1,14 @@
export default {
async paths() {
return [
{ params: { id: 'foo' }, content: `# Foo` },
{ params: { id: 'bar' }, content: `# Bar` }
]
import { defineRoutes } from 'vitepress'
import paths from './paths'
export default defineRoutes({
async paths(watchedFiles: string[]) {
// console.log('watchedFiles', watchedFiles)
return paths
},
watch: ['../data-loading/**/*.json'],
async transformPageData(pageData) {
// console.log('transformPageData', pageData.filePath)
pageData.title += ' - transformed'
}
}
})

@ -0,0 +1,4 @@
export default [
{ params: { id: 'foo' }, content: `# Foo` },
{ params: { id: 'bar' }, content: `# Bar` }
]

@ -6,6 +6,10 @@ title: VitePress
hero:
name: VitePress
text: Vite & Vue Powered Static Site Generator
image:
src: /vitepress-logo.svg
alt: VitePress
actions:
- theme: brand
text: Examples

@ -4,7 +4,7 @@ describe('local search', () => {
})
test('exclude content from search results', async () => {
await page.locator('#local-search button').click()
await page.locator('.VPNavBarSearchButton').click()
const input = await page.waitForSelector('input#localsearch-input')
await input.type('local')

@ -0,0 +1,27 @@
# header 1
header 1 content
## header 1.1
header 1.1 content
### header 1.1.1
header 1.1.1 content
### header 1.1.2
header 1.1.2 content
## header 1.2
header 1.2 content
### header 1.2.1
header 1.2.1 content
### header 1.2.2
header 1.2.2 content

@ -213,6 +213,10 @@ export default config
<!--@include: ./region-include.md#range-region{5,}-->
## Markdown File Inclusion with Header
<!--@include: ./header-include.md#header-1-1-->
## Image Lazy Loading
![vitepress logo](/vitepress.png)
![vitepress logo](/vitepress.png)

@ -64,8 +64,61 @@ describe('Emoji', () => {
describe('Table of Contents', () => {
test('render toc', async () => {
const items = page.locator('#table-of-contents + nav ul li')
const count = await items.count()
expect(count).toBe(44)
expect(
await items.evaluateAll((elements) =>
elements.map((el) => el.childNodes[0].textContent)
)
).toMatchInlineSnapshot(`
[
"Links",
"Internal Links",
"External Links",
"GitHub-Style Tables",
"Emoji",
"Table of Contents",
"Custom Containers",
"Default Title",
"Custom Title",
"Line Highlighting in Code Blocks",
"Single Line",
"Multiple single lines, ranges",
"Comment Highlight",
"Line Numbers",
"Import Code Snippets",
"Basic Code Snippet",
"Specify Region",
"With Other Features",
"Code Groups",
"Basic Code Group",
"With Other Features",
"Markdown File Inclusion",
"Region",
"Markdown At File Inclusion",
"Markdown Nested File Inclusion",
"Region",
"After Foo",
"Sub sub",
"Sub sub sub",
"Markdown File Inclusion with Range",
"Region",
"Markdown File Inclusion with Range without Start",
"Region",
"Markdown File Inclusion with Range without End",
"Region",
"Markdown At File Region Snippet",
"Region Snippet",
"Markdown At File Range Region Snippet",
"Range Region Line 2",
"Markdown At File Range Region Snippet without start",
"Range Region Line 1",
"Markdown At File Range Region Snippet without end",
"Range Region Line 3",
"Markdown File Inclusion with Header",
"header 1.1.1",
"header 1.1.2",
"Image Lazy Loading",
]
`)
})
})

@ -0,0 +1 @@
../../../art/vitepress-logo.svg

@ -2,11 +2,11 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"isolatedModules": false,
"baseUrl": ".",
"types": ["node", "vitest/globals"],
"paths": {
"client/*": ["../src/client/*"],
"node/*": ["../src/node/*"],
"client/*": ["../src/client/*"]
"shared/*": ["../src/shared/*"]
}
}
}

@ -0,0 +1,196 @@
import {
buildAskAiConfig,
hasAskAi,
hasKeywordSearch,
mergeLangFacetFilters,
validateCredentials
} from 'client/theme-default/support/docsearch'
describe('client/theme-default/support/docsearch', () => {
describe('mergeLangFacetFilters', () => {
test('adds a lang facet filter when none is provided', () => {
expect(mergeLangFacetFilters(undefined, 'en')).toEqual(['lang:en'])
})
test('replaces existing lang facet filters', () => {
expect(mergeLangFacetFilters('lang:fr', 'en')).toEqual(['lang:en'])
expect(mergeLangFacetFilters(['foo', 'lang:fr'], 'en')).toEqual([
'foo',
'lang:en'
])
})
test('handles nested facet filters (OR conditions)', () => {
expect(
mergeLangFacetFilters([['tag:foo', 'tag:bar'], 'lang:fr'], 'en')
).toEqual([['tag:foo', 'tag:bar'], 'lang:en'])
})
test('removes empty nested arrays', () => {
expect(mergeLangFacetFilters([['lang:fr'], 'other'], 'en')).toEqual([
'other',
'lang:en'
])
})
test('handles multiple lang filters in nested arrays', () => {
expect(
mergeLangFacetFilters([['lang:fr', 'tag:foo'], 'bar'], 'en')
).toEqual([['tag:foo'], 'bar', 'lang:en'])
})
})
describe('hasKeywordSearch', () => {
test('returns true when all credentials are provided', () => {
expect(
hasKeywordSearch({
appId: 'app',
apiKey: 'key',
indexName: 'index'
})
).toBe(true)
})
test('returns false when any credential is missing', () => {
expect(
hasKeywordSearch({
appId: undefined,
apiKey: 'key',
indexName: 'index'
})
).toBe(false)
expect(
hasKeywordSearch({
appId: 'app',
apiKey: undefined,
indexName: 'index'
})
).toBe(false)
expect(
hasKeywordSearch({
appId: 'app',
apiKey: 'key',
indexName: undefined
})
).toBe(false)
})
})
describe('hasAskAi', () => {
test('returns true for valid string assistantId', () => {
expect(hasAskAi('assistant123')).toBe(true)
})
test('returns false for empty string assistantId', () => {
expect(hasAskAi('')).toBe(false)
})
test('returns true for object with assistantId', () => {
expect(hasAskAi({ assistantId: 'assistant123' } as any)).toBe(true)
})
test('returns false for object without assistantId', () => {
expect(hasAskAi({ assistantId: null } as any)).toBe(false)
expect(hasAskAi({} as any)).toBe(false)
})
test('returns false for undefined', () => {
expect(hasAskAi(undefined)).toBe(false)
})
})
describe('validateCredentials', () => {
test('validates complete credentials', () => {
const result = validateCredentials({
appId: 'app',
apiKey: 'key',
indexName: 'index'
})
expect(result.valid).toBe(true)
expect(result.appId).toBe('app')
expect(result.apiKey).toBe('key')
expect(result.indexName).toBe('index')
})
test('invalidates incomplete credentials', () => {
expect(
validateCredentials({
appId: undefined,
apiKey: 'key',
indexName: 'index'
}).valid
).toBe(false)
})
})
describe('buildAskAiConfig', () => {
test('builds config from string assistantId', () => {
const result = buildAskAiConfig(
'assistant123',
{
appId: 'app',
apiKey: 'key',
indexName: 'index'
} as any,
'en'
)
expect(result.assistantId).toBe('assistant123')
expect(result.appId).toBe('app')
expect(result.apiKey).toBe('key')
expect(result.indexName).toBe('index')
})
test('builds config from object with overrides', () => {
const result = buildAskAiConfig(
{
assistantId: 'assistant123',
appId: 'custom-app',
apiKey: 'custom-key',
indexName: 'custom-index'
} as any,
{
appId: 'default-app',
apiKey: 'default-key',
indexName: 'default-index'
} as any,
'en'
)
expect(result.assistantId).toBe('assistant123')
expect(result.appId).toBe('custom-app')
expect(result.apiKey).toBe('custom-key')
expect(result.indexName).toBe('custom-index')
})
test('merges facet filters with lang', () => {
const result = buildAskAiConfig(
{
assistantId: 'assistant123',
searchParameters: {
facetFilters: ['tag:docs']
}
} as any,
{
appId: 'app',
apiKey: 'key',
indexName: 'index'
} as any,
'en'
)
expect(result.searchParameters?.facetFilters).toContain('tag:docs')
expect(result.searchParameters?.facetFilters).toContain('lang:en')
})
test('always adds lang facet filter to searchParameters', () => {
const result = buildAskAiConfig(
'assistant123',
{
appId: 'app',
apiKey: 'key',
indexName: 'index'
} as any,
'en'
)
expect(result.searchParameters?.facetFilters).toEqual(['lang:en'])
})
})
})

@ -1,4 +1,7 @@
import { ensureStartingSlash } from 'client/theme-default/support/utils'
import {
ensureStartingSlash,
isLinkExternal
} from 'client/theme-default/support/utils'
describe('client/theme-default/utils', () => {
describe('ensureStartingSlash', () => {
@ -9,4 +12,22 @@ describe('client/theme-default/utils', () => {
expect(ensureStartingSlash('/path/nested')).toBe('/path/nested')
})
})
describe('isLinkExternal', () => {
test('it detects external links by default', () => {
expect(isLinkExternal('https://vite.dev')).toBe(true)
expect(isLinkExternal('/guide/')).toBe(false)
})
test('it treats _blank targets as external by default', () => {
expect(isLinkExternal('/guide/', '_blank')).toBe(true)
})
test('it allows callers to override external detection', () => {
expect(isLinkExternal('https://cn.vite.dev', undefined, false)).toBe(
false
)
expect(isLinkExternal('/guide/', undefined, true)).toBe(true)
})
})
})

@ -0,0 +1,26 @@
import { highlight } from 'node/markdown/plugins/highlight'
describe('node/markdown/plugins/highlight', () => {
test('passes color replacements through markdown options', async () => {
const [render, dispose] = await highlight(
{ light: 'github-light', dark: 'github-dark' },
{
colorReplacements: {
'github-light': {
'#005cc5': '#000000'
}
}
}
)
try {
const html = await render('const a = 1', 'js', '')
expect(html).toContain('--shiki-light:#000000')
expect(html).toContain('--shiki-dark:#79B8FF')
expect(html).not.toContain('--shiki-light:#005CC5')
} finally {
dispose()
}
})
})

@ -0,0 +1,51 @@
import { slugify } from '@mdit-vue/shared'
import { MarkdownItAsync } from 'markdown-it-async'
import { linkPlugin } from 'node/markdown/plugins/link'
describe('node/markdown/plugins/link', () => {
const md = new MarkdownItAsync()
linkPlugin(md, {}, '/', slugify)
test('preserves text-fragment hashes on markdown links', async () => {
const html = await md.renderAsync(
'[58-61](/resources/server/user#:~:text=58*,time%20authentication%20token)',
{ cleanUrls: false }
)
expect(html).toContain(
'href="/resources/server/user.html#:~:text=58*,time%20authentication%20token"'
)
})
// https://web.dev/articles/text-fragments#mixing_element_and_text_fragments
test('preserves mixed element and text-fragment hashes', async () => {
const html = await md.renderAsync(
'[Section](/guide/getting-started#Hello%20World:~:text=Hello%20World)',
{ cleanUrls: false }
)
expect(html).toContain(
'href="/guide/getting-started.html#hello-world:~:text=Hello%20World"'
)
})
test('continues to normalize regular heading hashes', async () => {
const html = await md.renderAsync(
'[Section](/guide/getting-started#Hello%20World)',
{ cleanUrls: false }
)
expect(html).toContain('href="/guide/getting-started.html#hello-world"')
})
test('does not break encoding for text-fragments', async () => {
const html = await md.renderAsync(
'[Section](/foo?title=Cat&oldid=916388819#:~:text=Claws-,Like%20almost,the%20Felidae%2C,-cats)',
{ cleanUrls: false }
)
expect(html).toContain(
'href="/foo.html?title=Cat&amp;oldid=916388819#:~:text=Claws-,Like%20almost,the%20Felidae%2C,-cats"'
)
})
})

@ -1,4 +1,8 @@
import { dedent, rawPathToToken } from 'node/markdown/plugins/snippet'
import {
dedent,
findRegion,
rawPathToToken
} from 'node/markdown/plugins/snippet'
const removeEmptyKeys = <T extends Record<string, unknown>>(obj: T) => {
return Object.fromEntries(
@ -94,9 +98,228 @@ describe('node/markdown/plugins/snippet', () => {
})
})
test('rawPathToToken', () => {
rawPathTokenMap.forEach(([rawPath, token]) => {
describe('rawPathToToken', () => {
test.each(rawPathTokenMap)('%s', (rawPath, token) => {
expect(removeEmptyKeys(rawPathToToken(rawPath))).toEqual(token)
})
})
describe('findRegion', () => {
it('returns null when no region markers are present', () => {
const lines = ['function foo() {', ' console.log("hello");', '}']
expect(findRegion(lines, 'foo')).toBeNull()
})
it('ignores non-matching region names', () => {
const lines = [
'// #region regionA',
'some code here',
'// #endregion regionA'
]
expect(findRegion(lines, 'regionC')).toBeNull()
})
it('returns null if a region start marker exists without a matching end marker', () => {
const lines = [
'// #region missingEnd',
'console.log("inside region");',
'console.log("still inside");'
]
expect(findRegion(lines, 'missingEnd')).toBeNull()
})
it('returns null if an end marker exists without a preceding start marker', () => {
const lines = [
'// #endregion ghostRegion',
'console.log("stray end marker");'
]
expect(findRegion(lines, 'ghostRegion')).toBeNull()
})
it('detects C#/JavaScript style region markers with matching tags', () => {
const lines = [
'Console.WriteLine("Before region");',
'#region hello',
'Console.WriteLine("Hello, World!");',
'#endregion hello',
'Console.WriteLine("After region");'
]
const result = findRegion(lines, 'hello')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
'Console.WriteLine("Hello, World!");'
)
}
})
it('detects region markers even when the end marker omits the region name', () => {
const lines = [
'Console.WriteLine("Before region");',
'#region hello',
'Console.WriteLine("Hello, World!");',
'#endregion',
'Console.WriteLine("After region");'
]
const result = findRegion(lines, 'hello')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
'Console.WriteLine("Hello, World!");'
)
}
})
it('handles indented region markers correctly', () => {
const lines = [
' Console.WriteLine("Before region");',
' #region hello',
' Console.WriteLine("Hello, World!");',
' #endregion hello',
' Console.WriteLine("After region");'
]
const result = findRegion(lines, 'hello')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
' Console.WriteLine("Hello, World!");'
)
}
})
it('detects TypeScript style region markers', () => {
const lines = [
'let regexp: RegExp[] = [];',
'// #region foo',
'let start = -1;',
'// #endregion foo'
]
const result = findRegion(lines, 'foo')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
'let start = -1;'
)
}
})
it('detects CSS style region markers', () => {
const lines = [
'.body-content {',
'/* #region foo */',
' padding-left: 15px;',
'/* #endregion foo */',
' padding-right: 15px;',
'}'
]
const result = findRegion(lines, 'foo')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
' padding-left: 15px;'
)
}
})
it('detects HTML style region markers', () => {
const lines = [
'<div>Some content</div>',
'<!-- #region foo -->',
' <h1>Hello world</h1>',
'<!-- #endregion foo -->',
'<div>Other content</div>'
]
const result = findRegion(lines, 'foo')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
' <h1>Hello world</h1>'
)
}
})
it('detects Visual Basic style region markers (with case-insensitive "End")', () => {
const lines = [
'Console.WriteLine("VB")',
'#Region VBRegion',
' Console.WriteLine("Inside region")',
'#End Region VBRegion',
'Console.WriteLine("Done")'
]
const result = findRegion(lines, 'VBRegion')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
' Console.WriteLine("Inside region")'
)
}
})
it('detects Bat style region markers', () => {
const lines = ['::#region foo', 'echo off', '::#endregion foo']
const result = findRegion(lines, 'foo')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
'echo off'
)
}
})
it('detects C/C++ style region markers using #pragma', () => {
const lines = [
'#pragma region foo',
'int a = 1;',
'#pragma endregion foo'
]
const result = findRegion(lines, 'foo')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
'int a = 1;'
)
}
})
it('returns the first complete region when multiple regions exist', () => {
const lines = [
'// #region foo',
'first region content',
'// #endregion foo',
'// #region foo',
'second region content',
'// #endregion foo'
]
const result = findRegion(lines, 'foo')
expect(result).not.toBeNull()
if (result) {
expect(lines.slice(result.start, result.end).join('\n')).toBe(
'first region content'
)
}
})
it('handles nested regions with different names properly', () => {
const lines = [
'// #region foo',
"console.log('line before nested');",
'// #region bar',
"console.log('nested content');",
'// #endregion bar',
'// #endregion foo'
]
const result = findRegion(lines, 'foo')
expect(result).not.toBeNull()
if (result) {
const extracted = lines.slice(result.start, result.end).join('\n')
const expected = [
"console.log('line before nested');",
'// #region bar',
"console.log('nested content');",
'// #endregion bar'
].join('\n')
expect(extracted).toBe(expected)
}
})
})
})

@ -0,0 +1,77 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`node/postcss/isolateStyles > transforms selectors and skips keyframes 1`] = `
"
/* simple classes */
.example:not(:where(.vp-raw, .vp-raw *)) { color: red; }
.class-a:not(:where(.vp-raw, .vp-raw *)) { color: coral; }
.class-b:not(:where(.vp-raw, .vp-raw *)) { color: deepskyblue; }
/* escaped colon in class */
.baz\\:not\\(.bar\\):not(:where(.vp-raw, .vp-raw *)) { display: block; }
.disabled\\:opacity-50:not(:where(.vp-raw, .vp-raw *)):disabled { opacity: .5; }
/* pseudos (class + element) */
.button:not(:where(.vp-raw, .vp-raw *)):hover { color: pink; }
.button:not(:where(.vp-raw, .vp-raw *)):focus:hover { color: hotpink; }
.item:not(:where(.vp-raw, .vp-raw *))::before { content: '•'; }
:not(:where(.vp-raw, .vp-raw *))::first-letter { color: pink; }
:not(:where(.vp-raw, .vp-raw *))::before { content: ''; }
/* universal + :not */
*:not(:where(.vp-raw, .vp-raw *)) { background-color: red; }
*:not(:where(.vp-raw, .vp-raw *)):not(.b) { text-transform: uppercase; }
/* combinators */
.foo:hover .bar:not(:where(.vp-raw, .vp-raw *)) { background: blue; }
ul > li.active:not(:where(.vp-raw, .vp-raw *)) { color: green; }
a + b ~ c:not(:where(.vp-raw, .vp-raw *)) { color: orange; }
/* ids + attribute selectors */
#wow:not(:where(.vp-raw, .vp-raw *)) { color: yellow; }
[data-world] .d:not(:where(.vp-raw, .vp-raw *)) { padding: 10px 20px; }
/* :root and chained tags */
:not(:where(.vp-raw, .vp-raw *)):root { --bs-blue: #0d6efd; }
:root .a:not(:where(.vp-raw, .vp-raw *)) { --bs-green: #bada55; }
html:not(:where(.vp-raw, .vp-raw *)) { margin: 0; }
body:not(:where(.vp-raw, .vp-raw *)) { padding: 0; }
html body div:not(:where(.vp-raw, .vp-raw *)) { color: blue; }
/* grouping with commas */
.a:not(:where(.vp-raw, .vp-raw *)), .b:not(:where(.vp-raw, .vp-raw *)) { color: red; }
/* multiple repeated groups to ensure stability */
.a:not(:where(.vp-raw, .vp-raw *)), .b:not(:where(.vp-raw, .vp-raw *)) { color: coral; }
.a:not(:where(.vp-raw, .vp-raw *)) { animation: glow 1s linear infinite alternate; }
/* nested blocks */
.foo:not(:where(.vp-raw, .vp-raw *)) {
svg:not(:where(.vp-raw, .vp-raw *)) { display: none; }
.bar:not(:where(.vp-raw, .vp-raw *)) { display: inline; }
}
/* standalone pseudos */
:not(:where(.vp-raw, .vp-raw *)):first-child { color: pink; }
:not(:where(.vp-raw, .vp-raw *)):hover { color: blue; }
:not(:where(.vp-raw, .vp-raw *)):active { color: red; }
/* keyframes (should be ignored) */
@keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}
@-webkit-keyframes glow {
from { color: coral; }
to { color: red; }
}
@-moz-keyframes glow {
from { color: coral; }
to { color: red; }
}
@-o-keyframes glow {
from { color: coral; }
to { color: red; }
}
"
`;

@ -0,0 +1,93 @@
import { postcssIsolateStyles } from 'node/postcss/isolateStyles'
import postcss from 'postcss'
const INPUT_CSS = `
/* simple classes */
.example { color: red; }
.class-a { color: coral; }
.class-b { color: deepskyblue; }
/* escaped colon in class */
.baz\\:not\\(.bar\\) { display: block; }
.disabled\\:opacity-50:disabled { opacity: .5; }
/* pseudos (class + element) */
.button:hover { color: pink; }
.button:focus:hover { color: hotpink; }
.item::before { content: '•'; }
::first-letter { color: pink; }
::before { content: ''; }
/* universal + :not */
* { background-color: red; }
*:not(.b) { text-transform: uppercase; }
/* combinators */
.foo:hover .bar { background: blue; }
ul > li.active { color: green; }
a + b ~ c { color: orange; }
/* ids + attribute selectors */
#wow { color: yellow; }
[data-world] .d { padding: 10px 20px; }
/* :root and chained tags */
:root { --bs-blue: #0d6efd; }
:root .a { --bs-green: #bada55; }
html { margin: 0; }
body { padding: 0; }
html body div { color: blue; }
/* grouping with commas */
.a, .b { color: red; }
/* multiple repeated groups to ensure stability */
.a, .b { color: coral; }
.a { animation: glow 1s linear infinite alternate; }
/* nested blocks */
.foo {
svg { display: none; }
.bar { display: inline; }
}
/* standalone pseudos */
:first-child { color: pink; }
:hover { color: blue; }
:active { color: red; }
/* keyframes (should be ignored) */
@keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}
@-webkit-keyframes glow {
from { color: coral; }
to { color: red; }
}
@-moz-keyframes glow {
from { color: coral; }
to { color: red; }
}
@-o-keyframes glow {
from { color: coral; }
to { color: red; }
}
`
describe('node/postcss/isolateStyles', () => {
test('transforms selectors and skips keyframes', () => {
const out = run(INPUT_CSS)
expect(out.css).toMatchSnapshot()
})
test('idempotent (running twice produces identical CSS)', () => {
const first = run(INPUT_CSS).css
const second = run(first).css
expect(second).toBe(first)
})
})
function run(css: string, from = 'src/styles/vp-doc.css') {
return postcss([postcssIsolateStyles()]).process(css, { from })
}

@ -0,0 +1,72 @@
import { ModuleGraph } from 'node/utils/moduleGraph'
describe('node/utils/moduleGraph', () => {
let graph: ModuleGraph
beforeEach(() => {
graph = new ModuleGraph()
})
it('should correctly delete a module and its dependents', () => {
graph.add('A', ['B', 'C'])
graph.add('B', ['D'])
graph.add('C', [])
graph.add('D', [])
expect(graph.delete('D')).toEqual(new Set(['D', 'B', 'A']))
})
it('should handle shared dependencies correctly', () => {
graph.add('A', ['B', 'C'])
graph.add('B', ['D'])
graph.add('C', ['D']) // Shared dependency
graph.add('D', [])
expect(graph.delete('D')).toEqual(new Set(['A', 'B', 'C', 'D']))
})
it('merges dependencies correctly', () => {
// Add module A with dependency B
graph.add('A', ['B'])
// Merge new dependency C into module A (B should remain)
graph.add('A', ['C'])
// Deleting B should remove A as well, since A depends on B.
expect(graph.delete('B')).toEqual(new Set(['B', 'A']))
})
it('handles cycles gracefully', () => {
// Create a cycle: A -> B, B -> C, C -> A.
graph.add('A', ['B'])
graph.add('B', ['C'])
graph.add('C', ['A'])
// Deleting any module in the cycle should delete all modules in the cycle.
expect(graph.delete('A')).toEqual(new Set(['A', 'B', 'C']))
})
it('cleans up dependencies when deletion', () => {
// Setup A -> B relationship.
graph.add('A', ['B'])
graph.add('B', [])
// Deleting B should remove both B and A from the graph.
expect(graph.delete('B')).toEqual(new Set(['B', 'A']))
// After deletion, add modules again.
graph.add('C', [])
graph.add('A', ['C']) // Now A depends only on C.
expect(graph.delete('C')).toEqual(new Set(['C', 'A']))
})
it('handles independent modules', () => {
// Modules with no dependencies.
graph.add('X', [])
graph.add('Y', [])
// Deletion of one should only remove that module.
expect(graph.delete('X')).toEqual(new Set(['X']))
expect(graph.delete('Y')).toEqual(new Set(['Y']))
})
})

@ -12,6 +12,7 @@ export default defineConfig({
{ find: '@siteData', replacement: resolve(dir, './shims.ts') },
{ find: 'client', replacement: resolve(dir, '../../src/client') },
{ find: 'node', replacement: resolve(dir, '../../src/node') },
{ find: 'shared', replacement: resolve(dir, '../../src/shared') },
{
find: /^vitepress$/,
replacement: resolve(dir, '../../src/client/index.js')

@ -1,4 +1,4 @@
<svg width="160" height="192" viewBox="0 0 160 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 160 192" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1288_1252)">
<path d="M0.12699 43.8989C-0.863275 37.1255 4.04575 30.863 11.0915 29.911L113.152 16.1221C120.197 15.1702 126.712 19.8893 127.702 26.6627L147.873 164.635C148.863 171.409 143.954 177.671 136.908 178.623L39.1006 191.837C29.7063 193.107 21.0203 186.814 19.7001 177.784L0.12699 43.8989Z" fill="url(#paint0_linear_1288_1252)"/>
<path d="M10.1376 30.5728C9.06487 22.8787 14.3827 15.7647 22.0152 14.6834L124.678 0.138664C132.31 -0.942678 139.367 4.41806 140.44 12.1122L159.862 151.427C160.935 159.121 155.617 166.235 147.985 167.317L45.3224 181.861C37.6899 182.943 30.6329 177.582 29.5602 169.888L10.1376 30.5728Z" fill="url(#paint1_linear_1288_1252)"/>

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

@ -1,2 +1,16 @@
#!/usr/bin/env node
// @ts-check
import module from 'node:module'
// https://github.com/vitejs/vite/blob/6c8a5a27e645a182f5b03a4ed6aa726eab85993f/packages/vite/bin/vite.js#L48-L63
try {
module.enableCompileCache?.()
setTimeout(() => {
try {
module.flushCompileCache?.()
} catch {}
}, 10 * 1000).unref()
} catch {}
import('../dist/node/cli.js')

9
client.d.ts vendored

@ -3,3 +3,12 @@
/// <reference types="vite/client" />
export * from './dist/client/index.js'
declare global {
interface WindowEventMap {
'vitepress:codeGroupTabActivate': Event & {
/** code block element that was activated */
detail: Element
}
}
}

@ -0,0 +1,178 @@
import {
defineConfig,
resolveSiteDataByRoute,
type HeadConfig
} from 'vitepress'
import {
groupIconMdPlugin,
groupIconVitePlugin,
localIconLoader
} from 'vitepress-plugin-group-icons'
import llmstxt from 'vitepress-plugin-llms'
const prod = !!process.env.NETLIFY
const siteUrl = 'https://vitepress.dev'
const ogImage = new URL('/vitepress-og.jpg', siteUrl).href
const localeToOgLocaleMap: Record<string, string> = {
root: 'en_US',
zh: 'zh_CN',
pt: 'pt_BR',
ru: 'ru_RU',
es: 'es_ES',
ko: 'ko_KR',
fa: 'fa_IR',
ja: 'ja_JP'
}
export default defineConfig({
title: 'VitePress',
rewrites: {
'en/:rest*': ':rest*'
},
lastUpdated: true,
cleanUrls: true,
metaChunk: true,
markdown: {
math: true,
codeTransformers: [
// We use `[!!code` and `@@include` in demo to prevent transformation,
// here we revert it back.
{
postprocess(code) {
return code
.replaceAll('[!!code', '[!code')
.replaceAll('@@include', '@include')
}
}
],
config(md) {
// TODO: remove when https://github.com/vuejs/vitepress/issues/4431 is fixed
const fence = md.renderer.rules.fence!
md.renderer.rules.fence = function (tokens, idx, options, env, self) {
const { localeIndex = 'root' } = env
const codeCopyButtonTitle = (() => {
switch (localeIndex) {
case 'es':
return 'Copiar código'
case 'fa':
return 'کپی کد'
case 'ko':
return '코드 복사'
case 'pt':
return 'Copiar código'
case 'ru':
return 'Скопировать код'
case 'zh':
return '复制代码'
case 'ja':
return 'コードをコピー'
default:
return 'Copy code'
}
})()
return fence(tokens, idx, options, env, self).replace(
'<button title="Copy Code" class="copy"></button>',
`<button title="${codeCopyButtonTitle}" class="copy"></button>`
)
}
md.use(groupIconMdPlugin)
}
},
sitemap: {
hostname: siteUrl,
transformItems(items) {
return items.filter((item) => !item.url.includes('migration'))
}
},
// prettier-ignore
head: [
['link', { rel: 'icon', type: 'image/svg+xml', href: '/vitepress-logo-mini.svg' }],
['link', { rel: 'icon', type: 'image/png', href: '/vitepress-logo-mini.png' }],
['meta', { name: 'theme-color', content: '#5f67ee' }],
['script', { src: 'https://cdn.usefathom.com/script.js', 'data-site': 'AZBRSFGG', 'data-spa': 'auto', defer: '' }]
],
themeConfig: {
logo: { src: '/vitepress-logo-mini.svg', width: 24, height: 24 },
socialLinks: [
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }
],
search: {
provider: 'algolia',
options: {
appId: '8J64VVRP8K',
apiKey: '52f578a92b88ad6abde815aae2b0ad7c',
indexName: 'vitepress',
askAi: {
assistantId: 'YaVSonfX5bS8',
sidePanel: true
}
}
},
carbonAds: { code: 'CEBDT27Y', placement: 'vuejsorg' }
},
locales: {
root: { label: 'English', lang: 'en-US', dir: 'ltr' },
zh: { label: '简体中文', lang: 'zh-Hans', dir: 'ltr' },
pt: { label: 'Português', lang: 'pt-BR', dir: 'ltr' },
ru: { label: 'Русский', lang: 'ru-RU', dir: 'ltr' },
es: { label: 'Español', lang: 'es', dir: 'ltr' },
ko: { label: '한국어', lang: 'ko-KR', dir: 'ltr' },
fa: { label: 'فارسی', lang: 'fa-IR', dir: 'rtl' },
ja: { label: '日本語', lang: 'ja', dir: 'ltr' }
},
vite: {
plugins: [
groupIconVitePlugin({
customIcon: {
vitepress: localIconLoader(
import.meta.url,
'../public/vitepress-logo-mini.svg'
),
firebase: 'logos:firebase'
}
}),
prod && llmstxt({ workDir: 'en', ignoreFiles: ['index.md'] })
],
experimental: {
enableNativePlugin: true
}
},
// prettier-ignore
transformPageData: prod ? (pageData, ctx) => {
const url = new URL(pageData.relativePath.replace(/(?:(^|\/)index)?\.md$/, '$1'), siteUrl).href
const site = resolveSiteDataByRoute(ctx.siteConfig.site, pageData.relativePath)
const title = pageData.title ? `${pageData.title} | VitePress` : site.title
const description = pageData.description || site.description
const locale = localeToOgLocaleMap[site.localeIndex || 'root']
;((pageData.frontmatter.head ??= []) as HeadConfig[]).push(
['meta', { property: 'og:url', content: url }],
['meta', { property: 'og:title', content: title }],
['meta', { property: 'og:description', content: description }],
['meta', { property: 'og:type', content: 'website' }],
['meta', { property: 'og:locale', content: locale }],
['meta', { property: 'og:site_name', content: 'VitePress' }],
['meta', { property: 'og:image', content: ogImage }],
['meta', { property: 'og:image:secure_url', content: ogImage }],
['meta', { property: 'og:image:type', content: 'image/jpeg' }],
['meta', { property: 'og:image:width', content: '1280' }],
['meta', { property: 'og:image:height', content: '640' }],
['meta', { property: 'og:image:alt', content: 'VitePress' }],
['link', { rel: 'canonical', href: url }]
)
} : undefined
})

@ -1,216 +0,0 @@
import { createRequire } from 'module'
import { defineConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export const es = defineConfig({
lang: 'es-CO',
description: 'Generador de Sitios Estaticos desarrollado con Vite y Vue.',
themeConfig: {
nav: nav(),
sidebar: {
'/es/guide/': { base: '/es/guide/', items: sidebarGuide() },
'/es/reference/': { base: '/es/reference/', items: sidebarReference() }
},
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Editar esta página en GitHub'
},
footer: {
message: 'Liberado bajo la licencia MIT',
copyright: `Derechos reservados © 2019-${new Date().getFullYear()} Evan You`
},
docFooter: {
prev: 'Anterior',
next: 'Siguiente'
},
outline: {
label: 'En esta página'
},
lastUpdated: {
text: 'Actualizado en',
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium'
}
},
langMenuLabel: 'Cambiar Idioma',
returnToTopLabel: 'Volver arriba',
sidebarMenuLabel: 'Menu Lateral',
darkModeSwitchLabel: 'Tema Oscuro',
lightModeSwitchTitle: 'Cambiar a modo claro',
darkModeSwitchTitle: 'Cambiar a modo oscuro',
skipToContentLabel: 'Saltar al contenido'
}
})
function nav(): DefaultTheme.NavItem[] {
return [
{
text: 'Guia',
link: '/es/guide/what-is-vitepress',
activeMatch: '/es/guide/'
},
{
text: 'Referencia',
link: '/es/reference/site-config',
activeMatch: '/es/reference/'
},
{
text: pkg.version,
items: [
{
text: 'Registro de cambios',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: 'Contribuir',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
}
]
}
]
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Introducción',
collapsed: false,
items: [
{ text: 'Qué es VitePress', link: 'what-is-vitepress' },
{ text: 'Iniciando', link: 'getting-started' },
{ text: 'Enrutamiento', link: 'routing' },
{ text: 'Despliegue', link: 'deploy' }
]
},
{
text: 'Escribiendo',
collapsed: false,
items: [
{ text: 'Extensiones Markdown', link: 'markdown' },
{ text: 'Manejo de assets', link: 'asset-handling' },
{ text: 'Frontmatter', link: 'frontmatter' },
{ text: 'Usando Vue en Markdown', link: 'using-vue' },
{ text: 'Internacionalización', link: 'i18n' }
]
},
{
text: 'Pesonalización',
collapsed: false,
items: [
{ text: 'Usando un tema personalizado', link: 'custom-theme' },
{
text: 'Extendiendo el tema por defecto',
link: 'extending-default-theme'
},
{
text: 'Carga de datos en tiempo de compilación',
link: 'data-loading'
},
{ text: 'Compatibilidad SSR', link: 'ssr-compat' },
{ text: 'Conectando a un CMS', link: 'cms' }
]
},
{
text: 'Experimental',
collapsed: false,
items: [
{ text: 'Modo MPA', link: 'mpa-mode' },
{ text: 'Generación de Sitemap', link: 'sitemap-generation' }
]
},
{
text: 'Configuración y Referencia del API',
base: '/es/reference/',
link: 'site-config'
}
]
}
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Referencia',
items: [
{ text: 'Configuración del sitio', link: 'site-config' },
{ text: 'Configuración Frontmatter', link: 'frontmatter-config' },
{ text: 'API de tiempo de ejecución', link: 'runtime-api' },
{ text: 'CLI', link: 'cli' },
{
text: 'Tema por defecto',
base: '/es/reference/default-theme-',
items: [
{ text: 'Visión general', link: 'config' },
{ text: 'Navegación', link: 'nav' },
{ text: 'Barra Lateral', link: 'sidebar' },
{ text: 'Página Inicial', link: 'home-page' },
{ text: 'Pie de página', link: 'footer' },
{ text: 'Layout', link: 'layout' },
{ text: 'Distintivo', link: 'badge' },
{ text: 'Página del equipo', link: 'team-page' },
{ text: 'Links Anterior / Siguiente', link: 'prev-next-links' },
{ text: 'Editar Link', link: 'edit-link' },
{ text: 'Sello temporal de actualización', link: 'last-updated' },
{ text: 'Busqueda', link: 'search' },
{ text: 'Carbon Ads', link: 'carbon-ads' }
]
}
]
}
]
}
export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
es: {
placeholder: 'Buscar documentos',
translations: {
button: {
buttonText: 'Buscar',
buttonAriaLabel: 'Buscar'
},
modal: {
searchBox: {
resetButtonTitle: 'Limpiar búsqueda',
resetButtonAriaLabel: 'Limpiar búsqueda',
cancelButtonText: 'Cancelar',
cancelButtonAriaLabel: 'Cancelar'
},
startScreen: {
recentSearchesTitle: 'Historial de búsqueda',
noRecentSearchesText: 'Ninguna búsqueda reciente',
saveRecentSearchButtonTitle: 'Guardar en el historial de búsqueda',
removeRecentSearchButtonTitle: 'Borrar del historial de búsqueda',
favoriteSearchesTitle: 'Favoritos',
removeFavoriteSearchButtonTitle: 'Borrar de favoritos'
},
errorScreen: {
titleText: 'No fue posible obtener resultados',
helpText: 'Verifique su conexión de red'
},
footer: {
selectText: 'Seleccionar',
navigateText: 'Navegar',
closeText: 'Cerrar',
searchByText: 'Busqueda por'
},
noResultsScreen: {
noResultsText: 'No fue posible encontrar resultados',
suggestedQueryText: 'Puede intentar una nueva búsqueda',
reportMissingResultsText:
'Deberian haber resultados para esa consulta?',
reportMissingResultsLinkText: 'Click para enviar feedback'
}
}
}
}
}

@ -1,223 +0,0 @@
import { createRequire } from 'module'
import { defineConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export const fa = defineConfig({
title: 'ویت‌پرس',
lang: 'fa-IR',
description: 'Vite & Vue powered static site generator.',
dir: 'rtl',
markdown: {
container: {
tipLabel: 'نکته',
warningLabel: 'هشدار',
dangerLabel: 'خطر',
infoLabel: 'اطلاعات',
detailsLabel: 'جزئیات'
}
},
themeConfig: {
nav: nav(),
sidebar: {
'/fa/guide/': { base: '/fa/guide/', items: sidebarGuide() },
'/fa/reference/': { base: '/fa/reference/', items: sidebarReference() }
},
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'ویرایش این صفحه در گیت‌هاب'
},
footer: {
message: 'انتشار یافته تحت لایسنس MIT',
copyright: 'حق نسخه‌برداری © 2019-کنون Evan You'
},
docFooter: {
prev: 'قبلی',
next: 'بعدی'
},
outline: {
label: 'در این صفحه'
},
lastUpdated: {
text: 'آخرین به‌روزرسانی‌',
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium'
}
},
langMenuLabel: 'تغییر زبان',
returnToTopLabel: 'بازگشت به بالا',
sidebarMenuLabel: 'منوی جانبی',
darkModeSwitchLabel: 'تم تاریک',
lightModeSwitchTitle: 'رفتن به حالت روشن',
darkModeSwitchTitle: 'رفتن به حالت تاریک',
notFound: {
linkLabel: 'بازگشت به خانه',
linkText: 'بازگشت به خانه',
title: 'صفحه مورد نظر یافت نشد',
code: '۴۰۴',
quote:
'اما اگر جهت خود را تغییر ندهید و اگر ادامه دهید به دنبال چیزی که دنبال می‌کنید، ممکن است در نهایت به جایی که در حال رفتن به سمتش هستید، برسید.'
},
siteTitle: 'ویت‌پرس'
}
})
function nav(): DefaultTheme.NavItem[] {
return [
{
text: 'راهنما',
link: 'fa/guide/what-is-vitepress',
activeMatch: '/guide/'
},
{
text: 'مرجع',
link: 'fa/reference/site-config',
activeMatch: '/reference/'
},
{
text: pkg.version,
items: [
{
text: 'Changelog',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: 'مشارکت',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
}
]
}
]
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
text: 'معرفی',
collapsed: false,
items: [
{ text: 'ویت‌پرس چیست؟', link: 'what-is-vitepress' },
{ text: 'شروع کار', link: 'getting-started' },
{ text: 'مسیریابی', link: 'routing' },
{ text: 'استقرار', link: 'deploy' }
]
},
{
text: 'نوشتن',
collapsed: false,
items: [
{ text: 'افزونه‌های Markdown', link: 'markdown' },
{ text: 'مدیریت منابع', link: 'asset-handling' },
{ text: 'Frontmatter', link: 'frontmatter' },
{ text: 'استفاده از Vue در Markdown', link: 'using-vue' },
{ text: 'بین‌المللی سازی', link: 'i18n' }
]
},
{
text: 'شخصی‌سازی',
collapsed: false,
items: [
{ text: 'استفاده از تم شخصی', link: 'custom-theme' },
{
text: 'گسترش تم پیش‌فرض',
link: 'extending-default-theme'
},
{ text: 'بارگیری داده در زمان Build', link: 'data-loading' },
{ text: 'سازگاری SSR', link: 'ssr-compat' },
{ text: 'اتصال به CMS', link: 'cms' }
]
},
{
text: 'آزمایشی',
collapsed: false,
items: [
{ text: 'حالت MPA', link: 'mpa-mode' },
{ text: 'جنریت کردن Sitemap', link: 'sitemap-generation' }
]
},
{ text: 'پیکربندی و مرجع API', base: 'fa/reference/', link: 'site-config' }
]
}
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [
{
text: 'مرجع',
base: 'fa/reference/',
items: [
{ text: 'پیکربندی Site', link: 'site-config' },
{ text: 'پیکربندی Frontmatter', link: 'frontmatter-config' },
{ text: 'Runtime API', link: 'runtime-api' },
{ text: 'CLI', link: 'cli' },
{
text: 'تم پیش‌فرض',
base: 'fa/reference/default-theme-',
items: [
{ text: 'بررسی اجمالی', link: 'config' },
{ text: 'ناوبری', link: 'nav' },
{ text: 'نوار کنار صفحه', link: 'sidebar' },
{ text: 'صفحه اصلی', link: 'home-page' },
{ text: 'پاورقی', link: 'footer' },
{ text: 'طرح', link: 'layout' },
{ text: 'نشان', link: 'badge' },
{ text: 'صفحه تیم', link: 'team-page' },
{ text: 'لینک‌های قبلی / بعدی', link: 'prev-next-links' },
{ text: 'ویرایش لینک', link: 'edit-link' },
{ text: 'Timestamp آخرین به‌روزرسانی', link: 'last-updated' },
{ text: 'جستجو', link: 'search' },
{ text: 'تبلیغات Carbon', link: 'carbon-ads' }
]
}
]
}
]
}
export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
fa: {
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: 'نتیجه‌ای یافت نشد برای'
}
}
}
}
}

@ -1,22 +0,0 @@
import { defineConfig } from 'vitepress'
import { shared } from './shared'
import { en } from './en'
import { zh } from './zh'
import { pt } from './pt'
import { ru } from './ru'
import { es } from './es'
import { ko } from './ko'
import { fa } from './fa'
export default defineConfig({
...shared,
locales: {
root: { label: 'English', ...en },
zh: { label: '简体中文', ...zh },
pt: { label: 'Português', ...pt },
ru: { label: 'Русский', ...ru },
es: { label: 'Español', ...es },
ko: { label: '한국어', ...ko },
fa: { label: 'فارسی', ...fa }
}
})

@ -1,253 +0,0 @@
import { createRequire } from 'module'
import { defineConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export const ko = defineConfig({
lang: 'ko-KR',
description: 'Vite 및 Vue 기반 정적 사이트 생성기.',
themeConfig: {
nav: nav(),
sidebar: {
'/ko/guide/': { base: '/ko/guide/', items: sidebarGuide() },
'/ko/reference/': { base: '/ko/reference/', items: sidebarReference() }
},
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: '이 페이지 편집 제안하기'
},
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2019-present Evan You'
},
docFooter: {
prev: '이전',
next: '다음'
},
outline: {
label: '이 페이지 목차'
},
lastUpdated: {
text: '업데이트 날짜'
},
langMenuLabel: '언어 변경',
returnToTopLabel: '맨 위로 돌아가기',
sidebarMenuLabel: '사이드바 메뉴',
darkModeSwitchLabel: '다크 모드',
lightModeSwitchTitle: '라이트 모드로 변경',
darkModeSwitchTitle: '다크 모드로 변경',
skipToContentLabel: '본문으로 건너뛰기'
}
})
function nav(): DefaultTheme.NavItem[] {
return [
{
text: '가이드',
link: '/ko/guide/what-is-vitepress',
activeMatch: '/ko/guide/'
},
{
text: '레퍼런스',
link: '/ko/reference/site-config',
activeMatch: '/ko/reference/'
},
{
text: pkg.version,
items: [
{
text: '변경 로그',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: '기여',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
}
]
}
]
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
text: '소개',
collapsed: false,
items: [
{
text: 'VitePress란 무엇인가?',
link: 'what-is-vitepress'
},
{
text: '시작하기',
link: 'getting-started'
},
{
text: '라우팅',
link: 'routing'
},
{
text: '배포하기',
link: 'deploy'
}
]
},
{
text: '글쓰기',
collapsed: false,
items: [
{
text: '마크다운 확장 기능',
link: 'markdown'
},
{
text: '에셋 핸들링',
link: 'asset-handling'
},
{
text: '전문(Front-matter)',
link: 'frontmatter'
},
{
text: '마크다운에서 Vue 사용하기',
link: 'using-vue'
},
{
text: 'i18n',
link: 'i18n'
}
]
},
{
text: '커스텀',
collapsed: false,
items: [
{
text: '커스텀 테마 사용하기',
link: 'custom-theme'
},
{
text: '기본 테마 확장하기',
link: 'extending-default-theme'
},
{
text: '빌드할 때 데이터 로딩하기',
link: 'data-loading'
},
{
text: 'SSR 호환성',
link: 'ssr-compat'
},
{
text: 'CMS 연결하기',
link: 'cms'
}
]
},
{
text: '실험적인',
collapsed: false,
items: [
{
text: 'MPA 모드',
link: 'mpa-mode'
},
{
text: '사이트맵 생성',
link: 'sitemap-generation'
}
]
},
{
text: '구성 & API 레퍼런스',
base: '/ko/reference/',
link: 'site-config'
}
]
}
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [
{
text: '레퍼런스',
items: [
{ text: '사이트 구성', link: 'site-config' },
{ text: '전문(front-matter) 구성', link: 'frontmatter-config' },
{ text: '런타임 API', link: 'runtime-api' },
{ text: 'CLI', link: 'cli' },
{
text: '기본 테마',
base: '/ko/reference/default-theme-',
items: [
{ text: '개요', link: 'config' },
{ text: '네비게이션 바', link: 'nav' },
{ text: '사이드바', link: 'sidebar' },
{ text: '홈 페이지', link: 'home-page' },
{ text: '푸터', link: 'footer' },
{ text: '레이아웃', link: 'layout' },
{ text: '배지(badge)', link: 'badge' },
{ text: '팀 페이지', link: 'team-page' },
{ text: '이전/다음 링크', link: 'prev-next-links' },
{ text: '편집 링크', link: 'edit-link' },
{ text: '마지막 업데이트 날짜', link: 'last-updated' },
{ text: '검색', link: 'search' },
{ text: '카본 광고', link: 'carbon-ads' }
]
}
]
}
]
}
export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
ko: {
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: '피드백 보내기 클릭'
}
}
}
}
}

@ -1,213 +0,0 @@
import { createRequire } from 'module'
import { defineConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export const pt = defineConfig({
lang: 'pt-BR',
description: 'Gerador de Site Estático desenvolvido com Vite e Vue.',
themeConfig: {
nav: nav(),
sidebar: {
'/pt/guide/': { base: '/pt/guide/', items: sidebarGuide() },
'/pt/reference/': { base: '/pt/reference/', items: sidebarReference() }
},
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Edite esta página no GitHub'
},
footer: {
message: 'Lançado sob licença MIT',
copyright: `Direitos reservados © 2019-${new Date().getFullYear()} Evan You`
},
docFooter: {
prev: 'Anterior',
next: 'Próximo'
},
outline: {
label: 'Nesta página'
},
lastUpdated: {
text: 'Atualizado em',
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium'
}
},
langMenuLabel: 'Alterar Idioma',
returnToTopLabel: 'Voltar ao Topo',
sidebarMenuLabel: 'Menu Lateral',
darkModeSwitchLabel: 'Tema Escuro',
lightModeSwitchTitle: 'Mudar para Modo Claro',
darkModeSwitchTitle: 'Mudar para Modo Escuro',
skipToContentLabel: 'Pular para o Conteúdo'
}
})
function nav(): DefaultTheme.NavItem[] {
return [
{
text: 'Guia',
link: '/pt/guide/what-is-vitepress',
activeMatch: '/pt/guide/'
},
{
text: 'Referência',
link: '/pt/reference/site-config',
activeMatch: '/pt/reference/'
},
{
text: pkg.version,
items: [
{
text: 'Registro de Mudanças',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: 'Contribuindo',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
}
]
}
]
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Introdução',
collapsed: false,
items: [
{ text: 'O que é VitePress', link: 'what-is-vitepress' },
{ text: 'Iniciando', link: 'getting-started' },
{ text: 'Roteamento', link: 'routing' },
{ text: 'Implantação', link: 'deploy' }
]
},
{
text: 'Escrevendo',
collapsed: false,
items: [
{ text: 'Extensões Markdown', link: 'markdown' },
{ text: 'Manipulando Ativos', link: 'asset-handling' },
{ text: 'Frontmatter', link: 'frontmatter' },
{ text: 'Usando Vue em Markdown', link: 'using-vue' },
{ text: 'Internacionalização', link: 'i18n' }
]
},
{
text: 'Personalização',
collapsed: false,
items: [
{ text: 'Usando um tema personalizado', link: 'custom-theme' },
{ text: 'Estendendo o tema padrão', link: 'extending-default-theme' },
{
text: 'Carregamento de dados no momento da compilação',
link: 'data-loading'
},
{ text: 'Compatibilidade SSR', link: 'ssr-compat' },
{ text: 'Conectando a um CMS', link: 'cms' }
]
},
{
text: 'Experimental',
collapsed: false,
items: [
{ text: 'Modo MPA', link: 'mpa-mode' },
{ text: 'Geração de Sitemap', link: 'sitemap-generation' }
]
},
{
text: 'Configuração e Referência da API',
base: '/pt/reference/',
link: 'site-config'
}
]
}
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Referência',
items: [
{ text: 'Configuração do Site', link: 'site-config' },
{ text: 'Configuração Frontmatter', link: 'frontmatter-config' },
{ text: 'API do tempo de execução', link: 'runtime-api' },
{ text: 'CLI', link: 'cli' },
{
text: 'Tema padrão',
base: '/pt/reference/default-theme-',
items: [
{ text: 'Visão Geral', link: 'config' },
{ text: 'Navegação', link: 'nav' },
{ text: 'Barra Lateral', link: 'sidebar' },
{ text: 'Página Inicial', link: 'home-page' },
{ text: 'Rodapé', link: 'footer' },
{ text: 'Layout', link: 'layout' },
{ text: 'Distintivo', link: 'badge' },
{ text: 'Página da Equipe', link: 'team-page' },
{ text: 'Links Anterior / Próximo', link: 'prev-next-links' },
{ text: 'Editar Link', link: 'edit-link' },
{ text: 'Selo Temporal de Atualização', link: 'last-updated' },
{ text: 'Busca', link: 'search' },
{ text: 'Carbon Ads', link: 'carbon-ads' }
]
}
]
}
]
}
export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
pt: {
placeholder: 'Pesquisar documentos',
translations: {
button: {
buttonText: 'Pesquisar',
buttonAriaLabel: 'Pesquisar'
},
modal: {
searchBox: {
resetButtonTitle: 'Limpar pesquisa',
resetButtonAriaLabel: 'Limpar pesquisa',
cancelButtonText: 'Cancelar',
cancelButtonAriaLabel: 'Cancelar'
},
startScreen: {
recentSearchesTitle: 'Histórico de Pesquisa',
noRecentSearchesText: 'Nenhuma pesquisa recente',
saveRecentSearchButtonTitle: 'Salvar no histórico de pesquisas',
removeRecentSearchButtonTitle: 'Remover do histórico de pesquisas',
favoriteSearchesTitle: 'Favoritos',
removeFavoriteSearchButtonTitle: 'Remover dos favoritos'
},
errorScreen: {
titleText: 'Não foi possível obter resultados',
helpText: 'Verifique a sua conexão de rede'
},
footer: {
selectText: 'Selecionar',
navigateText: 'Navegar',
closeText: 'Fechar',
searchByText: 'Pesquisa por'
},
noResultsScreen: {
noResultsText: 'Não foi possível encontrar resultados',
suggestedQueryText: 'Você pode tentar uma nova consulta',
reportMissingResultsText:
'Deveriam haver resultados para essa consulta?',
reportMissingResultsLinkText: 'Clique para enviar feedback'
}
}
}
}
}

@ -1,209 +0,0 @@
import { createRequire } from 'module'
import { defineConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export const ru = defineConfig({
lang: 'ru-RU',
description: 'Генератор статических сайтов на основе Vite и Vue.',
themeConfig: {
nav: nav(),
sidebar: {
'/ru/guide/': { base: '/ru/guide/', items: sidebarGuide() },
'/ru/reference/': { base: '/ru/reference/', items: sidebarReference() }
},
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Редактировать страницу'
},
footer: {
message: 'Опубликовано под лицензией MIT.',
copyright: '© 2019 настоящее время, Эван Ю'
},
outline: { label: 'Содержание страницы' },
docFooter: {
prev: 'Предыдущая страница',
next: 'Следующая страница'
},
lastUpdated: {
text: 'Обновлено'
},
darkModeSwitchLabel: 'Оформление',
lightModeSwitchTitle: 'Переключить на светлую тему',
darkModeSwitchTitle: 'Переключить на тёмную тему',
sidebarMenuLabel: 'Меню',
returnToTopLabel: 'Вернуться к началу',
langMenuLabel: 'Изменить язык',
skipToContentLabel: 'Перейти к содержимому'
}
})
function nav(): DefaultTheme.NavItem[] {
return [
{
text: 'Руководство',
link: '/ru/guide/what-is-vitepress',
activeMatch: '/ru/guide/'
},
{
text: 'Справочник',
link: '/ru/reference/site-config',
activeMatch: '/ru/reference/'
},
{
text: pkg.version,
items: [
{
text: 'Изменения',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: 'Вклад',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
}
]
}
]
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Введение',
collapsed: false,
items: [
{ text: 'Что такое VitePress?', link: 'what-is-vitepress' },
{ text: 'Первые шаги', link: 'getting-started' },
{ text: 'Маршрутизация', link: 'routing' },
{ text: 'Развёртывание', link: 'deploy' }
]
},
{
text: 'Написание',
collapsed: false,
items: [
{ text: 'Расширения Markdown', link: 'markdown' },
{ text: 'Обработка ресурсов', link: 'asset-handling' },
{ text: 'Метаданные', link: 'frontmatter' },
{ text: 'Использование Vue в Markdown', link: 'using-vue' },
{ text: 'Интернационализация', link: 'i18n' }
]
},
{
text: 'Настройка',
collapsed: false,
items: [
{ text: 'Пользовательская тема', link: 'custom-theme' },
{
text: 'Расширение темы по умолчанию',
link: 'extending-default-theme'
},
{
text: 'Загрузка данных в режиме реального времени',
link: 'data-loading'
},
{ text: 'Совместимость с SSR', link: 'ssr-compat' },
{ text: 'Подключение к CMS', link: 'cms' }
]
},
{
text: 'Экспериментально',
collapsed: false,
items: [
{ text: 'Режим MPA', link: 'mpa-mode' },
{ text: 'Генерация карты сайта', link: 'sitemap-generation' }
]
},
{ text: 'Конфигурация и API', base: '/ru/reference/', link: 'site-config' }
]
}
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Справочник',
items: [
{ text: 'Конфигурация сайта', link: 'site-config' },
{ text: 'Конфигурация метаданных', link: 'frontmatter-config' },
{ text: 'Runtime API', link: 'runtime-api' },
{ text: 'Командная строка', link: 'cli' },
{
text: 'Тема по умолчанию',
base: '/ru/reference/default-theme-',
items: [
{ text: 'Обзор', link: 'config' },
{ text: 'Навигация', link: 'nav' },
{ text: 'Сайдбар', link: 'sidebar' },
{ text: 'Главная страница', link: 'home-page' },
{ text: 'Футер', link: 'footer' },
{ text: 'Макет', link: 'layout' },
{ text: 'Значки', link: 'badge' },
{ text: 'Страница команды', link: 'team-page' },
{
text: 'Предыдущая и следующая страницы',
link: 'prev-next-links'
},
{ text: 'Ссылка для редактирования', link: 'edit-link' },
{ text: 'Последнее обновление', link: 'last-updated' },
{ text: 'Поиск', link: 'search' },
{ text: 'Carbon Ads (реклама)', link: 'carbon-ads' }
]
}
]
}
]
}
export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
ru: {
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: 'Нажмите на кнопку «Обратная связь»'
}
}
}
}
}

@ -1,127 +0,0 @@
import { defineConfig } from 'vitepress'
import {
groupIconMdPlugin,
groupIconVitePlugin,
localIconLoader
} from 'vitepress-plugin-group-icons'
import { search as esSearch } from './es'
import { search as faSearch } from './fa'
import { search as koSearch } from './ko'
import { search as ptSearch } from './pt'
import { search as ruSearch } from './ru'
import { search as zhSearch } from './zh'
export const shared = defineConfig({
title: 'VitePress',
rewrites: {
'en/:rest*': ':rest*'
},
lastUpdated: true,
cleanUrls: true,
metaChunk: true,
markdown: {
math: true,
codeTransformers: [
// We use `[!!code` in demo to prevent transformation, here we revert it back.
{
postprocess(code) {
return code.replace(/\[\!\!code/g, '[!code')
}
}
],
config(md) {
// TODO: remove when https://github.com/vuejs/vitepress/issues/4431 is fixed
const fence = md.renderer.rules.fence!
md.renderer.rules.fence = function (tokens, idx, options, env, self) {
const { localeIndex = 'root' } = env
const codeCopyButtonTitle = (() => {
switch (localeIndex) {
case 'es':
return 'Copiar código'
case 'fa':
return 'کپی کد'
case 'ko':
return '코드 복사'
case 'pt':
return 'Copiar código'
case 'ru':
return 'Скопировать код'
case 'zh':
return '复制代码'
default:
return 'Copy code'
}
})()
return fence(tokens, idx, options, env, self).replace(
'<button title="Copy Code" class="copy"></button>',
`<button title="${codeCopyButtonTitle}" class="copy"></button>`
)
}
md.use(groupIconMdPlugin)
}
},
sitemap: {
hostname: 'https://vitepress.dev',
transformItems(items) {
return items.filter((item) => !item.url.includes('migration'))
}
},
/* prettier-ignore */
head: [
['link', { rel: 'icon', type: 'image/svg+xml', href: '/vitepress-logo-mini.svg' }],
['link', { rel: 'icon', type: 'image/png', href: '/vitepress-logo-mini.png' }],
['meta', { name: 'theme-color', content: '#5f67ee' }],
['meta', { property: 'og:type', content: 'website' }],
['meta', { property: 'og:locale', content: 'en' }],
['meta', { property: 'og:title', content: 'VitePress | Vite & Vue Powered Static Site Generator' }],
['meta', { property: 'og:site_name', content: 'VitePress' }],
['meta', { property: 'og:image', content: 'https://vitepress.dev/vitepress-og.jpg' }],
['meta', { property: 'og:url', content: 'https://vitepress.dev/' }],
['script', { src: 'https://cdn.usefathom.com/script.js', 'data-site': 'AZBRSFGG', 'data-spa': 'auto', defer: '' }]
],
themeConfig: {
logo: { src: '/vitepress-logo-mini.svg', width: 24, height: 24 },
socialLinks: [
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }
],
search: {
provider: 'algolia',
options: {
appId: '8J64VVRP8K',
apiKey: '52f578a92b88ad6abde815aae2b0ad7c',
indexName: 'vitepress',
locales: {
...zhSearch,
...ptSearch,
...ruSearch,
...esSearch,
...koSearch,
...faSearch
}
}
},
carbonAds: { code: 'CEBDT27Y', placement: 'vuejsorg' }
},
vite: {
plugins: [
groupIconVitePlugin({
customIcon: {
vitepress: localIconLoader(
import.meta.url,
'../../public/vitepress-logo-mini.svg'
),
firebase: 'logos:firebase'
}
})
]
}
})

@ -1,205 +0,0 @@
import { createRequire } from 'module'
import { defineConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export const zh = defineConfig({
lang: 'zh-Hans',
description: '由 Vite 和 Vue 驱动的静态站点生成器',
themeConfig: {
nav: nav(),
sidebar: {
'/zh/guide/': { base: '/zh/guide/', items: sidebarGuide() },
'/zh/reference/': { base: '/zh/reference/', items: sidebarReference() }
},
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: '在 GitHub 上编辑此页面'
},
footer: {
message: '基于 MIT 许可发布',
copyright: `版权所有 © 2019-${new Date().getFullYear()} 尤雨溪`
},
docFooter: {
prev: '上一页',
next: '下一页'
},
outline: {
label: '页面导航'
},
lastUpdated: {
text: '最后更新于',
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium'
}
},
langMenuLabel: '多语言',
returnToTopLabel: '回到顶部',
sidebarMenuLabel: '菜单',
darkModeSwitchLabel: '主题',
lightModeSwitchTitle: '切换到浅色模式',
darkModeSwitchTitle: '切换到深色模式',
skipToContentLabel: '跳转到内容'
}
})
function nav(): DefaultTheme.NavItem[] {
return [
{
text: '指南',
link: '/zh/guide/what-is-vitepress',
activeMatch: '/zh/guide/'
},
{
text: '参考',
link: '/zh/reference/site-config',
activeMatch: '/zh/reference/'
},
{
text: pkg.version,
items: [
{
text: '更新日志',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: '参与贡献',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
}
]
}
]
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
text: '简介',
collapsed: false,
items: [
{ text: '什么是 VitePress', link: 'what-is-vitepress' },
{ text: '快速开始', link: 'getting-started' },
{ text: '路由', link: 'routing' },
{ text: '部署', link: 'deploy' }
]
},
{
text: '写作',
collapsed: false,
items: [
{ text: 'Markdown 扩展', link: 'markdown' },
{ text: '资源处理', link: 'asset-handling' },
{ text: 'frontmatter', link: 'frontmatter' },
{ text: '在 Markdown 使用 Vue', link: 'using-vue' },
{ text: '国际化', link: 'i18n' }
]
},
{
text: '自定义',
collapsed: false,
items: [
{ text: '自定义主题', link: 'custom-theme' },
{ text: '扩展默认主题', link: 'extending-default-theme' },
{ text: '构建时数据加载', link: 'data-loading' },
{ text: 'SSR 兼容性', link: 'ssr-compat' },
{ text: '连接 CMS', link: 'cms' }
]
},
{
text: '实验性功能',
collapsed: false,
items: [
{ text: 'MPA 模式', link: 'mpa-mode' },
{ text: 'sitemap 生成', link: 'sitemap-generation' }
]
},
{ text: '配置和 API 参考', base: '/zh/reference/', link: 'site-config' }
]
}
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [
{
text: '参考',
items: [
{ text: '站点配置', link: 'site-config' },
{ text: 'frontmatter 配置', link: 'frontmatter-config' },
{ text: '运行时 API', link: 'runtime-api' },
{ text: 'CLI', link: 'cli' },
{
text: '默认主题',
base: '/zh/reference/default-theme-',
items: [
{ text: '概览', link: 'config' },
{ text: '导航栏', link: 'nav' },
{ text: '侧边栏', link: 'sidebar' },
{ text: '主页', link: 'home-page' },
{ text: '页脚', link: 'footer' },
{ text: '布局', link: 'layout' },
{ text: '徽章', link: 'badge' },
{ text: '团队页', link: 'team-page' },
{ text: '上下页链接', link: 'prev-next-links' },
{ text: '编辑链接', link: 'edit-link' },
{ text: '最后更新时间戳', link: 'last-updated' },
{ text: '搜索', link: 'search' },
{ text: 'Carbon Ads', link: 'carbon-ads' }
]
}
]
}
]
}
export const search: DefaultTheme.AlgoliaSearchOptions['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: '点击反馈'
}
}
}
}
}

@ -1,4 +1,5 @@
import Theme from 'vitepress/theme'
import 'virtual:group-icons.css'
import './styles.css'
export default Theme

@ -0,0 +1,43 @@
:root:where(:lang(fa)) {
--vp-font-family-base:
'Vazirmatn', 'Inter', ui-sans-serif, system-ui, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#bd34fe 30%,
#41d1ff
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#bd34fe 50%,
#47caff 50%
);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
.VPHero .VPImage {
filter: drop-shadow(-2px 4px 6px rgba(0, 0, 0, 0.2));
padding: 18px;
}
/* used in reference/default-theme-search */
img[src='/search.png'] {
width: 100%;
aspect-ratio: 1 / 1;
}

@ -1,11 +1,10 @@
import { createRequire } from 'module'
import { defineConfig, type DefaultTheme } from 'vitepress'
import { defineAdditionalConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export const en = defineConfig({
lang: 'en-US',
export default defineAdditionalConfig({
description: 'Vite & Vue powered static site generator.',
themeConfig: {
@ -43,6 +42,10 @@ function nav(): DefaultTheme.NavItem[] {
{
text: pkg.version,
items: [
{
text: '1.6.4',
link: 'https://vuejs.github.io/vitepress/v1/'
},
{
text: 'Changelog',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'

@ -1,3 +1,7 @@
---
description: Learn how to reference and handle static assets such as images, media, and fonts in VitePress.
---
# Asset Handling
## Referencing Static Assets

@ -1,5 +1,6 @@
---
outline: deep
description: Connect VitePress to a headless CMS using dynamic routes and data loaders.
---
# Connecting to a CMS

@ -1,3 +1,7 @@
---
description: Create and use a custom theme in VitePress to fully control the look and feel of your site.
---
# Using a Custom Theme
## Theme Resolving

@ -1,3 +1,7 @@
---
description: Load arbitrary data at build time using VitePress data loaders and import it from pages or components.
---
# Build-Time Data Loading
VitePress provides a feature called **data loaders** that allows you to load arbitrary data and import it from pages or components. The data loading is executed **only at build time**: the resulting data will be serialized as JSON in the final JavaScript bundle.

@ -1,5 +1,6 @@
---
outline: deep
description: Deploy your VitePress site to popular platforms like Netlify, Vercel, GitHub Pages, and more.
---
# Deploy Your VitePress Site
@ -105,13 +106,13 @@ Note: the `vercel.json` file should be placed at the root of your **repository**
## Platform Guides
### Netlify / Vercel / Cloudflare Pages / AWS Amplify / Render
### Netlify / Vercel / Cloudflare Pages / AWS Amplify / Render {#generic}
Set up a new project and change these settings using your dashboard:
- **Build Command:** `npm run docs:build`
- **Output Directory:** `docs/.vitepress/dist`
- **Node Version:** `18` (or above)
- **Node Version:** `20` (or above)
::: warning
Don't enable options like _Auto Minify_ for HTML code. It will remove comments from output which have meaning to Vue. You may see hydration mismatch errors if they get removed.
@ -153,17 +154,17 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
# - uses: pnpm/action-setup@v3 # Uncomment this block if you're using pnpm
# - uses: pnpm/action-setup@v4 # Uncomment this block if you're using pnpm
# with:
# version: 9 # Not needed if you've set "packageManager" in package.json
# - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
cache: npm # or pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
@ -205,7 +206,7 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f
2. Create a file named `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content:
```yaml [.gitlab-ci.yml]
image: node:18
image: node:24
pages:
cache:
paths:
@ -221,7 +222,9 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f
- main
```
### Azure Static Web Apps
<!-- keep headings sorted alphabetically, leave nginx at the end -->
### Azure
1. Follow the [official documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/build-configuration).
@ -231,6 +234,10 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f
- **`output_location`**: `docs/.vitepress/dist`
- **`app_build_command`**: `npm run docs:build`
### CloudRay
You can deploy your VitePress project with [CloudRay](https://cloudray.io/) by following these [instructions](https://cloudray.io/articles/how-to-deploy-vitepress-site).
### Firebase
1. Create `firebase.json` and `.firebaserc` at the root of your project:
@ -262,14 +269,6 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f
firebase deploy
```
### Surge
1. After running `npm run docs:build`, run this command to deploy:
```sh
npx surge docs/.vitepress/dist
```
### Heroku
1. Follow documentation and guide given in [`heroku-buildpack-static`](https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-static).
@ -282,11 +281,11 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f
}
```
### Edgio
### Hostinger
Refer [Creating and Deploying a VitePress App To Edgio](https://docs.edg.io/guides/vitepress).
You can deploy your VitePress project with [Hostinger](https://www.hostinger.com/web-apps-hosting) by following these [instructions](https://www.hostinger.com/support/how-to-deploy-a-nodejs-website-in-hostinger/). While configuring build settings, choose VitePress as the framework and adjust the root directory to `./docs`.
### Kinsta Static Site Hosting
### Kinsta
You can deploy your VitePress website on [Kinsta](https://kinsta.com/static-site-hosting/) by following these [instructions](https://kinsta.com/docs/vitepress-static-site-example/).
@ -294,6 +293,14 @@ You can deploy your VitePress website on [Kinsta](https://kinsta.com/static-site
You can deploy your VitePress project to [Stormkit](https://www.stormkit.io) by following these [instructions](https://stormkit.io/blog/how-to-deploy-vitepress).
### Surge
1. After running `npm run docs:build`, run this command to deploy:
```sh
npx surge docs/.vitepress/dist
```
### Nginx
Here is a example of an Nginx server block configuration. This setup includes gzip compression for common text-based assets, rules for serving your VitePress site's static files with proper caching headers as well as handling `cleanUrls: true`.

@ -1,5 +1,6 @@
---
outline: deep
description: Customize and extend the VitePress default theme with custom CSS, components, layouts, and slots.
---
# Extending the Default Theme
@ -70,7 +71,7 @@ If your font is a local file referenced via `@font-face`, it will be processed a
export default {
transformHead({ assets }) {
// adjust the regex accordingly to match your font
const myFontFile = assets.find(file => /font-name\.\w+\.woff2/)
const myFontFile = assets.find(file => /font-name\.[\w-]+\.woff2/.test(file))
if (myFontFile) {
return [
[
@ -190,6 +191,7 @@ Full list of slots available in the default theme layout:
- `home-hero-info-before`
- `home-hero-info`
- `home-hero-info-after`
- `home-hero-actions-before-actions`
- `home-hero-actions-after`
- `home-hero-image`
- `home-hero-after`
@ -252,6 +254,7 @@ provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
{
duration: 300,
easing: 'ease-in',
fill: 'forwards',
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
}
)
@ -319,7 +322,7 @@ export default defineConfig({
{
find: /^.*\/VPNavBar\.vue$/,
replacement: fileURLToPath(
new URL('./components/CustomNavBar.vue', import.meta.url)
new URL('./theme/components/CustomNavBar.vue', import.meta.url)
)
}
]

@ -1,3 +1,7 @@
---
description: Learn how to use YAML frontmatter in VitePress Markdown files to control page-level metadata and behavior.
---
# Frontmatter
## Usage

@ -1,3 +1,7 @@
---
description: Get up and running with VitePress. Learn how to install, scaffold, and start developing your documentation site.
---
# Getting Started
## Try It Online
@ -8,7 +12,7 @@ You can try VitePress directly in your browser on [StackBlitz](https://vitepress
### Prerequisites
- [Node.js](https://nodejs.org/) version 18 or higher.
- [Node.js](https://nodejs.org/) version 20 or higher.
- Terminal for accessing VitePress via its command line interface (CLI).
- Text Editor with [Markdown](https://en.wikipedia.org/wiki/Markdown) syntax support.
- [VSCode](https://code.visualstudio.com/) is recommended, along with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar).
@ -18,39 +22,19 @@ VitePress can be used on its own, or be installed into an existing project. In b
::: code-group
```sh [npm]
$ npm add -D vitepress
$ npm add -D vitepress@next
```
```sh [pnpm]
$ pnpm add -D vitepress
$ pnpm add -D vitepress@next
```
```sh [yarn]
$ yarn add -D vitepress
```
```sh [yarn (pnp)]
$ yarn add -D vitepress vue
$ yarn add -D vitepress@next vue
```
```sh [bun]
$ bun add -D vitepress
```
:::
::: details Getting missing peer deps warnings?
If using PNPM, you will notice a missing peer warning for `@docsearch/js`. This does not prevent VitePress from working. If you wish to suppress this warning, add the following to your `package.json`:
```json
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@algolia/client-search",
"search-insights"
]
}
}
$ bun add -D vitepress@next
```
:::

@ -1,3 +1,7 @@
---
description: Set up internationalization (i18n) in VitePress to support multiple languages for your site.
---
# Internationalization
To use the built-in i18n features, one needs to create a directory structure as follows:

@ -1,3 +1,7 @@
---
description: VitePress built-in Markdown extensions including custom containers, code blocks with syntax highlighting, line highlighting, code groups, and more.
---
# Markdown Extensions
VitePress comes with built in Markdown Extensions.
@ -255,7 +259,7 @@ This is a special container that can be used to prevent style and router conflic
```md
::: raw
Wraps in a <div class="vp-raw">
Wraps in a `<div class="vp-raw">`
:::
```
@ -277,11 +281,11 @@ Wraps in a <div class="vp-raw">
}
```
It uses [`postcss-prefix-selector`](https://github.com/RadValentin/postcss-prefix-selector) under the hood. You can pass its options like this:
You can pass its options like this:
```js
postcssIsolateStyles({
includeFiles: [/vp-doc\.css/] // defaults to /base\.css/
includeFiles: [/custom\.css/] // defaults to [/vp-doc\.css/, /base\.css/]
})
```
@ -365,7 +369,7 @@ export default {
A [list of valid languages](https://shiki.style/languages) is available on Shiki's repository.
You may also customize syntax highlight theme in app config. Please see [`markdown` options](../reference/site-config#markdown) for more details.
You may also customize syntax highlight theme, configure language aliases, and set custom language labels in app config. Please see [`markdown` options](../reference/site-config#markdown) for more details.
## Line Highlighting in Code Blocks
@ -781,7 +785,7 @@ You can also [import snippets](#import-code-snippets) in code groups:
You can include a markdown file in another markdown file, even nested.
::: tip
You can also prefix the markdown path with `@`, it will act as the source root. By default, it's the VitePress project root, unless `srcDir` is configured.
You can also prefix the markdown path with `@`, and it will act as the source root. By default, the source root is the VitePress project root, unless `srcDir` is configured.
:::
For example, you can include a relative markdown file using this:
@ -793,7 +797,7 @@ For example, you can include a relative markdown file using this:
## Basics
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**Part file** (`parts/basics.md`)
@ -824,17 +828,17 @@ It also supports selecting a line range:
**Input**
```md
```md:line-numbers
# Docs
## Basics
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**Part file** (`parts/basics.md`)
```md
```md:line-numbers
Some getting started stuff.
### Configuration
@ -844,7 +848,7 @@ Can be created using `.foorc.json`.
**Equivalent code**
```md
```md:line-numbers
# Docs
## Basics
@ -860,18 +864,18 @@ You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/co
**Input**
```md
```md:line-numbers
# Docs
## Basics
<!--@include: ./parts/basics.md#basic-usage{,2}-->
<!--@include: ./parts/basics.md#basic-usage{5,}-->
<!--@@include: ./parts/basics.md#basic-usage{,2}-->
<!--@@include: ./parts/basics.md#basic-usage{5,}-->
```
**Part file** (`parts/basics.md`)
```md
```md:line-numbers
<!-- #region basic-usage -->
## Usage Line 1
@ -883,7 +887,7 @@ You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/co
**Equivalent code**
```md
```md:line-numbers
# Docs
## Basics
@ -897,12 +901,59 @@ You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/co
Note that this does not throw errors if your file is not present. Hence, when using this feature make sure that the contents are being rendered as expected.
:::
Instead of VS Code regions, you can also use header anchors to include a specific section of the file. For example, if you have a header in your markdown file like this:
```md
## My Base Section
Some content here.
### My Sub Section
Some more content here.
## Another Section
Content outside `My Base Section`.
```
You can include the `My Base Section` section like this:
```md
## My Extended Section
<!--@@include: ./parts/basics.md#my-base-section-->
```
**Equivalent code**
```md
## My Extended Section
Some content here.
### My Sub Section
Some more content here.
```
Here, `my-base-section` is the generated id of the heading element. In case it's not easily guessable, you can open the part file in your browser and click on the heading anchor (`#` symbol left to the heading when hovered) to see the id in the URL bar. Or use browser dev tools to inspect the element. Alternatively, you can also specify the id to the part file like this:
```md
## My Base Section {#custom-id}
```
and include it like this:
```md
<!--@@include: ./parts/basics.md#custom-id-->
```
## Math Equations
This is currently opt-in. To enable it, you need to install `markdown-it-mathjax3` and set `markdown.math` to `true` in your config file:
```sh
npm add -D markdown-it-mathjax3
npm add -D markdown-it-mathjax3@^4
```
```ts [.vitepress/config.ts]

@ -1,3 +1,7 @@
---
description: Enable MPA (Multi-Page Application) mode in VitePress for zero-JavaScript pages with better initial performance.
---
# MPA Mode <Badge type="warning" text="experimental" />
MPA (Multi-Page Application) mode can be enabled via the command line via `vitepress build --mpa`, or via config through the `mpa: true` option.

@ -1,5 +1,6 @@
---
outline: deep
description: Understand VitePress file-based routing, dynamic routes, clean URLs, and path rewrites.
---
# Routing
@ -121,7 +122,7 @@ Alternatively, you can directly use the anchor tag syntax:
:::
## Generating Clean URL
## Generating Clean URLs
::: warning Server Support Required
To serve clean URLs with VitePress, server-side support is required.
@ -260,6 +261,30 @@ The generated HTML pages will be:
└─ bar.html
```
### Type-safe loader with `defineRoutes`
If you are using TypeScript, you can wrap the loader with `defineRoutes` from `vitepress` to get type hints for route hooks such as `paths`, `watch`, and `transformPageData`:
```ts
// packages/[pkg].paths.ts
import { defineRoutes } from 'vitepress'
export default defineRoutes({
watch: ['../data/**/*.json'],
async paths() {
return [
{ params: { pkg: 'foo' } },
{ params: { pkg: 'bar' } }
]
},
async transformPageData(pageData) {
pageData.title = `${pageData.title} · Packages`
}
})
```
`defineRoutes` is optional, but recommended when authoring `.paths.ts` files.
### Multiple Params
A dynamic route can contain multiple params:
@ -336,6 +361,46 @@ export default {
}
```
### Watching Template and Data Files
When generating page content from templates or external data sources, you can use the watch option to automatically rebuild pages when those files change during development:
```js
// posts/[slug].paths.js
import fs from 'node:fs'
import { renderTemplate } from './templates/renderer.js'
export default {
// Watch for changes to template files and data sources
watch: [
'./templates/**/*.njk', // Template files
'../data/**/*.json' // Data files
],
paths(watchedFiles) {
// watchedFiles will be an array of absolute paths of the matched files
// Read data files to generate routes
const dataFiles = watchedFiles.filter(file => file.endsWith('.json'))
return dataFiles.map(file => {
const data = JSON.parse(fs.readFileSync(file, 'utf-8'))
return {
params: { slug: data.slug },
content: renderTemplate(data) // Use template to generate content
}
})
}
}
```
The `watch` option works the same way as in [data loaders](./data-loading#data-from-local-files):
- Accepts [glob patterns](https://github.com/mrmlnc/fast-glob#pattern-syntax) to match files
- Patterns are relative to the `.paths.js` file itself
- Changes to watched files trigger page regeneration and HMR during development
- In production builds, all pages are generated once regardless of watch configuration
### Accessing Params in Page
You can use the params to pass additional data to each page. The Markdown route file can access the current page params in Vue expressions via the `$params` global property:

@ -1,3 +1,7 @@
---
description: Generate a sitemap.xml file for your VitePress site to improve search engine discoverability.
---
# Sitemap Generation
VitePress comes with out-of-the-box support for generating a `sitemap.xml` file for your site. To enable it, add the following to your `.vitepress/config.js`:

@ -1,5 +1,6 @@
---
outline: deep
description: Ensure your VitePress theme components and custom code are compatible with server-side rendering.
---
# SSR Compatibility

@ -1,3 +1,7 @@
---
description: Use Vue components and dynamic templating features directly inside Markdown files in VitePress.
---
# Using Vue in Markdown
In VitePress, each Markdown file is compiled into HTML and then processed as a [Vue Single-File Component](https://vuejs.org/guide/scaling-up/sfc.html). This means you can use any Vue features inside the Markdown, including dynamic templating, using Vue components, or arbitrary in-page Vue component logic by adding a `<script>` tag.
@ -288,4 +292,4 @@ Vue provides IntelliSense support out of the box via the [Vue - Official VS Code
"vue.server.includeLanguages": ["vue", "markdown"]
}
```
:::
:::

@ -1,3 +1,7 @@
---
description: VitePress is a static site generator designed for building fast, content-centric websites powered by Vite and Vue.
---
# What is VitePress?
VitePress is a [Static Site Generator](https://en.wikipedia.org/wiki/Static_site_generator) (SSG) designed for building fast, content-centric websites. In a nutshell, VitePress takes your source content written in [Markdown](https://en.wikipedia.org/wiki/Markdown), applies a theme to it, and generates static HTML pages that can be easily deployed anywhere.
@ -12,7 +16,7 @@ Just want to try it out? Skip to the [Quickstart](./getting-started).
- **Documentation**
VitePress ships with a default theme designed for technical documentation. It powers this page you are reading right now, along with the documentation for [Vite](https://vitejs.dev/), [Rollup](https://rollupjs.org/), [Pinia](https://pinia.vuejs.org/), [VueUse](https://vueuse.org/), [Vitest](https://vitest.dev/), [D3](https://d3js.org/), [UnoCSS](https://unocss.dev/), [Iconify](https://iconify.design/) and [many more](https://www.vuetelescope.com/explore?framework.slug=vitepress).
VitePress ships with a default theme designed for technical documentation. It powers this page you are reading right now, along with the documentation for [Vite](https://vitejs.dev/), [Rollup](https://rollupjs.org/), [Pinia](https://pinia.vuejs.org/), [VueUse](https://vueuse.org/), [Vitest](https://vitest.dev/), [D3](https://d3js.org/), [UnoCSS](https://unocss.dev/), [Iconify](https://iconify.design/) and [many more](https://github.com/search?q=/%22vitepress%22:+/+path:/(?:package%7Cdeno)%5C.jsonc?$/+NOT+is:fork+NOT+is:archived&type=code).
The [official Vue.js documentation](https://vuejs.org/) is also based on VitePress, but uses a custom theme shared between multiple translations.
@ -50,8 +54,8 @@ Unlike many traditional SSGs where each navigation results in a full page reload
## What About VuePress?
VitePress is the spiritual successor of VuePress. The original VuePress was based on Vue 2 and webpack. With Vue 3 and Vite under the hood, VitePress provides significantly better DX, better production performance, a more polished default theme, and a more flexible customization API.
VitePress is the spiritual successor of VuePress 1. The original VuePress 1 was based on Vue 2 and webpack. With Vue 3 and Vite under the hood, VitePress provides significantly better DX, better production performance, a more polished default theme, and a more flexible customization API.
The API difference between VitePress and VuePress mostly lies in theming and customization. If you are using VuePress 1 with the default theme, it should be relatively straightforward to migrate to VitePress.
The API difference between VitePress and VuePress 1 mostly lies in theming and customization. If you are using VuePress 1 with the default theme, it should be relatively straightforward to migrate to VitePress.
There has also been effort invested into VuePress 2, which also supports Vue 3 and Vite with more compatibility with VuePress 1. However, maintaining two SSGs in parallel isn't sustainable, so the Vue team has decided to focus on VitePress as the main recommended SSG in the long run.
Maintaining two SSGs in parallel isn't sustainable, so the Vue team has decided to focus on VitePress as the main recommended SSG in the long run. Now VuePress 1 has been deprecated, and VuePress 2 has been handed over to the VuePress community team for further development and maintenance.

@ -1,30 +1,28 @@
---
description: VitePress is a Vite & Vue powered static site generator that lets you create beautiful documentation sites from Markdown.
layout: home
title: VitePress
titleTemplate: Vite & Vue Powered Static Site Generator
hero:
name: VitePress
text: Vite & Vue Powered Static Site Generator
tagline: Markdown to Beautiful Docs in Minutes
tagline: Markdown to beautiful docs in minutes
actions:
- theme: brand
text: What is VitePress?
link: /guide/what-is-vitepress
link: ./guide/what-is-vitepress
- theme: alt
text: Quickstart
link: /guide/getting-started
link: ./guide/getting-started
- theme: alt
text: GitHub
link: https://github.com/vuejs/vitepress
image:
src: /vitepress-logo-large.webp
src: /vitepress-logo-large.svg
alt: VitePress
features:
- icon: 📝
title: Focus on Your Content
title: Focus on your content
details: Effortlessly create beautiful documentation sites with just markdown.
- icon: <svg xmlns="http://www.w3.org/2000/svg" width="30" viewBox="0 0 256 256.32"><defs><linearGradient id="a" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"/><stop offset="100%" stop-color="#BD34FE"/></linearGradient><linearGradient id="b" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"/><stop offset="8.333%" stop-color="#FFDD35"/><stop offset="100%" stop-color="#FFA800"/></linearGradient></defs><path fill="url(#a)" d="M255.153 37.938 134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"/><path fill="url(#b)" d="M185.432.063 96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028 72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"/></svg>
title: Enjoy the Vite DX
@ -33,28 +31,6 @@ features:
title: Customize with Vue
details: Use Vue syntax and components directly in markdown, or build custom themes with Vue.
- icon: 🚀
title: Ship Fast Sites
title: Ship fast sites
details: Fast initial load with static HTML, fast post-load navigation with client-side routing.
---
<style>
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #bd34fe 30%, #41d1ff);
--vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe 50%, #47caff 50%);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
</style>

@ -1,3 +1,7 @@
---
description: Reference of VitePress CLI commands including dev, build, preview, and init.
---
# Command Line Interface
## `vitepress dev`
@ -43,7 +47,6 @@ vitepress build [root]
| `--base <path>` | Public base path (default: `/`) (`string`) |
| `--target <target>` | Transpile target (default: `"modules"`) (`string`) |
| `--outDir <dir>` | Output directory relative to **cwd** (default: `<root>/.vitepress/dist`) (`string`) |
| `--minify [minifier]` | Enable/disable minification, or specify minifier to use (default: `"esbuild"`) (`boolean \| "terser" \| "esbuild"`) |
| `--assetsInlineLimit <number>` | Static asset base64 inline threshold in bytes (default: `4096`) (`number`) |
## `vitepress preview`

@ -1,3 +1,7 @@
---
description: Use the Badge component to add status labels to headers in VitePress documentation.
---
# Badge
The badge lets you add status to your headers. For example, it could be useful to specify the section's type, or supported version.

@ -1,3 +1,7 @@
---
description: Integrate Carbon Ads into your VitePress site using the default theme's built-in support.
---
# Carbon Ads
VitePress has built in native support for [Carbon Ads](https://www.carbonads.net/). By defining the Carbon Ads credentials in config, VitePress will display ads on the page.
@ -7,7 +11,8 @@ export default {
themeConfig: {
carbonAds: {
code: 'your-carbon-code',
placement: 'your-carbon-placement'
placement: 'your-carbon-placement',
format: 'classic'
}
}
}
@ -15,8 +20,10 @@ export default {
These values are used to call carbon CDN script as shown below.
The `format` option supports `classic`, `responsive`, and `cover`.
```js
`//cdn.carbonads.com/carbon.js?serve=${code}&placement=${placement}`
`//cdn.carbonads.com/carbon.js?serve=${code}&placement=${placement}&format=${format}`
```
To learn more about Carbon Ads configuration, please visit [Carbon Ads website](https://www.carbonads.net/).

@ -1,3 +1,7 @@
---
description: Reference of all configuration options available for the VitePress default theme.
---
# Default Theme Config
Theme config lets you customize your theme. You can define theme config via the `themeConfig` option in the config file:
@ -89,7 +93,7 @@ type NavItem = NavItemWithLink | NavItemWithChildren
interface NavItemWithLink {
text: string
link: string
link: string | ((payload: PageData) => string)
activeMatch?: string
target?: string
rel?: string
@ -364,6 +368,7 @@ export default {
carbonAds: {
code: 'your-carbon-code',
placement: 'your-carbon-placement'
format: 'classic'
}
}
}
@ -373,6 +378,7 @@ export default {
export interface CarbonAdsOptions {
code: string
placement: string
format?: 'classic' | 'responsive' | 'cover'
}
```
@ -457,3 +463,38 @@ Can be used to customize the label of the skip to content link. This link is sho
- Default: `false`
Whether to show an external link icon next to external links in markdown.
## `useLayout` <Badge type="info" text="composable" />
Returns layout-related data. The returned object has the following type:
```ts
interface {
isHome: ComputedRef<boolean>
sidebar: Readonly<ShallowRef<DefaultTheme.SidebarItem[]>>
sidebarGroups: ComputedRef<DefaultTheme.SidebarItem[]>
hasSidebar: ComputedRef<boolean>
isSidebarEnabled: ComputedRef<boolean>
hasAside: ComputedRef<boolean>
leftAside: ComputedRef<boolean>
headers: Readonly<ShallowRef<DefaultTheme.OutlineItem[]>>
hasLocalNav: ComputedRef<boolean>
}
```
**Example:**
```vue
<script setup>
import { useLayout } from 'vitepress/theme'
const { hasSidebar } = useLayout()
</script>
<template>
<div v-if="hasSidebar">Only show when sidebar exists</div>
</template>
```

@ -1,3 +1,7 @@
---
description: Display an edit link on doc pages to let users suggest changes on GitHub or GitLab.
---
# Edit Link
## Site-Level Config

@ -1,3 +1,7 @@
---
description: Configure the global footer displayed at the bottom of VitePress pages.
---
# Footer
VitePress will display global footer at the bottom of the page when `themeConfig.footer` is present.

@ -1,3 +1,7 @@
---
description: Configure the VitePress default theme home page layout with hero sections, features, and custom content.
---
# Home Page
VitePress default theme provides a homepage layout, which you can also see used on [the homepage of this site](../). You may use it on any of your pages by specifying `layout: home` in the [frontmatter](./frontmatter-config).

@ -1,9 +1,32 @@
---
description: Show the last updated timestamp on VitePress pages based on Git commit history.
---
# Last Updated
The update time of the last content will be displayed in the lower right corner of the page. To enable it, add `lastUpdated` options to your config.
::: tip
You need to commit the markdown file to see the updated time.
::: info
VitePress displays the "last updated" time using the timestamp of the most recent Git commit for each file. To enable this, the Markdown file must be committed to Git.
Internally, VitePress runs `git log -1 --pretty="%ai"` on each file to retrieve its timestamp. If all pages show the same update time, it's likely due to shallow cloning (common in CI environments), which limits Git history.
To fix this in **GitHub Actions**, use the following in your workflow:
```yaml{4}
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
```
Other CI/CD platforms have similar settings.
If such options aren't available, you can prepend the `docs:build` command in your `package.json` with a manual fetch:
```json
"docs:build": "git fetch --unshallow && vitepress build docs"
```
:::
## Site-Level Config

@ -1,3 +1,7 @@
---
description: Choose between doc, page, and home layouts in the VitePress default theme.
---
# Layout
You may choose the page layout by setting `layout` option to the page [frontmatter](./frontmatter-config). There are 3 layout options, `doc`, `page`, and `home`. If nothing is specified, then the page is treated as `doc` page.

@ -1,3 +1,7 @@
---
description: Configure the navigation bar in the VitePress default theme including site title, logo, and menu links.
---
# Nav
The Nav is the navigation bar displayed on top of the page. It contains the site title, global menu links, etc.
@ -55,6 +59,8 @@ export default {
The `text` is the actual text displayed in nav, and the `link` is the link that will be navigated to when the text is clicked. For the link, set path to the actual file without `.md` prefix, and always start with `/`.
The `link` can also be a function that accepts [`PageData`](./runtime-api#usedata) as the argument and returns the path.
Nav links can also be dropdown menus. To do this, set `items` key on link option.
```js

@ -1,3 +1,7 @@
---
description: Customize the previous and next page links displayed at the bottom of doc pages in VitePress.
---
# Prev Next Links
You can customize the text and link for the previous and next pages (shown at doc footer). This is helpful if you want a different text there than what you have on your sidebar. Additionally, you may find it useful to disable the footer or link to a page that is not included in your sidebar.

@ -1,12 +1,13 @@
---
outline: deep
description: Set up local or Algolia-powered search for your VitePress site.
---
# Search
## Local Search
VitePress supports fuzzy full-text search using a in-browser index thanks to [minisearch](https://github.com/lucaong/minisearch/). To enable this feature, simply set the `themeConfig.search.provider` option to `'local'` in your `.vitepress/config.ts` file:
VitePress supports fuzzy full-text search using an in-browser index thanks to [minisearch](https://github.com/lucaong/minisearch/). To enable this feature, simply set the `themeConfig.search.provider` option to `'local'` in your `.vitepress/config.ts` file:
```ts
import { defineConfig } from 'vitepress'
@ -29,6 +30,7 @@ Alternatively, you can use [Algolia DocSearch](#algolia-search) or some communit
- <https://www.npmjs.com/package/vitepress-plugin-search>
- <https://www.npmjs.com/package/vitepress-plugin-pagefind>
- <https://www.npmjs.com/package/@orama/plugin-vitepress>
- <https://www.npmjs.com/package/vitepress-plugin-typesense>
### i18n {#local-search-i18n}
@ -124,9 +126,9 @@ export default defineConfig({
/**
* @param {string} src
* @param {import('vitepress').MarkdownEnv} env
* @param {import('markdown-it')} md
* @param {import('markdown-it-async')} md
*/
_render(src, env, md) {
async _render(src, env, md) {
// return html string
}
}
@ -149,8 +151,8 @@ export default defineConfig({
search: {
provider: 'local',
options: {
_render(src, env, md) {
const html = md.render(src, env)
async _render(src, env, md) {
const html = await md.renderAsync(src, env)
if (env.frontmatter?.search === false) return ''
if (env.relativePath.startsWith('some/path')) return ''
return html
@ -162,7 +164,7 @@ export default defineConfig({
```
::: warning Note
In case a custom `_render` function is provided, you need to handle the `search: false` frontmatter yourself. Also, the `env` object won't be completely populated before `md.render` is called, so any checks on optional `env` properties like `frontmatter` should be done after that.
In case a custom `_render` function is provided, you need to handle the `search: false` frontmatter yourself. Also, the `env` object won't be completely populated before `md.renderAsync` is called, so any checks on optional `env` properties like `frontmatter` should be done after that.
:::
#### Example: Transforming content - adding anchors
@ -175,10 +177,10 @@ export default defineConfig({
search: {
provider: 'local',
options: {
_render(src, env, md) {
const html = md.render(src, env)
async _render(src, env, md) {
const html = await md.renderAsync(src, env)
if (env.frontmatter?.title)
return md.render(`# ${env.frontmatter.title}`) + html
return (await md.renderAsync(`# ${env.frontmatter.title}`)) + html
return html
}
}
@ -189,7 +191,7 @@ export default defineConfig({
## Algolia Search
VitePress supports searching your docs site using [Algolia DocSearch](https://docsearch.algolia.com/docs/what-is-docsearch). Refer their getting started guide. In your `.vitepress/config.ts` you'll need to provide at least the following to make it work:
VitePress supports searching your docs site using [Algolia DocSearch](https://docsearch.algolia.com/docs/what-is-docsearch). Refer to their getting started guide. In your `.vitepress/config.ts` you'll need to provide at least the following to make it work:
```ts
import { defineConfig } from 'vitepress'
@ -212,6 +214,19 @@ export default defineConfig({
You can use a config like this to use multilingual search:
<details>
<summary>View full example</summary>
<<< @/snippets/algolia-i18n.ts
</details>
Refer [official Algolia docs](https://docsearch.algolia.com/docs/api#translations) to learn more about them. To quickly get started, you can also copy the translations used by this site from [our GitHub repo](https://github.com/search?q=repo:vuejs/vitepress+%22function+searchOptions%22&type=code).
### Algolia Ask AI Support {#ask-ai}
If you would like to include **Ask AI**, pass the `askAi` option (or any of the partial fields) inside `options`:
```ts
import { defineConfig } from 'vitepress'
@ -223,46 +238,50 @@ export default defineConfig({
appId: '...',
apiKey: '...',
indexName: '...',
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: '点击反馈'
}
}
// askAi: "YOUR-ASSISTANT-ID"
// OR
askAi: {
// at minimum you must provide the assistantId you received from Algolia
assistantId: 'XXXYYY',
// optional overrides if omitted, the top-level appId/apiKey/indexName values are reused
// apiKey: '...',
// appId: '...',
// indexName: '...'
}
}
}
}
})
```
::: warning Note
If you want to default to keyword search and do not want to use Ask AI, omit the `askAi` property.
:::
### Ask AI Side Panel {#ask-ai-side-panel}
DocSearch v4.5+ supports an optional **Ask AI side panel**. When enabled, it can be opened with **Ctrl/Cmd+I** by default. The [Sidepanel API Reference](https://docsearch.algolia.com/docs/sidepanel/api-reference) contains the full list of options.
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'algolia',
options: {
appId: '...',
apiKey: '...',
indexName: '...',
askAi: {
assistantId: 'XXXYYY',
sidePanel: {
panel: {
variant: 'floating', // or 'inline'
side: 'right',
width: '360px',
expandedWidth: '580px',
suggestedQuestions: true
}
}
}
@ -272,119 +291,70 @@ export default defineConfig({
})
```
[These options](https://github.com/vuejs/vitepress/blob/main/types/docsearch.d.ts) can be overridden. Refer official Algolia docs to learn more about them.
If you need to disable the keyboard shortcut, use the `keyboardShortcuts` option at the sidepanel root level:
### Crawler Config
```ts
import { defineConfig } from 'vitepress'
Here is an example config based on what this site uses:
export default defineConfig({
themeConfig: {
search: {
provider: 'algolia',
options: {
appId: '...',
apiKey: '...',
indexName: '...',
askAi: {
assistantId: 'XXXYYY',
sidePanel: {
keyboardShortcuts: {
'Ctrl/Cmd+I': false
}
}
}
}
}
}
})
```
#### Mode (auto / sidePanel / hybrid / modal) {#ask-ai-mode}
You can optionally control how VitePress integrates keyword search and Ask AI:
- `mode: 'auto'` (default): infer `hybrid` when keyword search is configured, otherwise `sidePanel` when Ask AI side panel is configured.
- `mode: 'sidePanel'`: force side panel only (hides the keyword search button).
- `mode: 'hybrid'`: enable keyword search modal + Ask AI side panel (requires keyword search configuration).
- `mode: 'modal'`: keep Ask AI inside the DocSearch modal (even if you configured the side panel).
#### Ask AI only (no keyword search) {#ask-ai-only}
If you want to use **Ask AI side panel only**, you can omit top-level keyword search config and provide credentials under `askAi`:
```ts
new Crawler({
appId: '...',
apiKey: '...',
rateLimit: 8,
startUrls: ['https://vitepress.dev/'],
renderJavaScript: false,
sitemaps: [],
exclusionPatterns: [],
ignoreCanonicalTo: false,
discoveryPatterns: ['https://vitepress.dev/**'],
schedule: 'at 05:10 on Saturday',
actions: [
{
indexName: 'vitepress',
pathsToMatch: ['https://vitepress.dev/**'],
recordExtractor: ({ $, helpers }) => {
return helpers.docsearch({
recordProps: {
lvl1: '.content h1',
content: '.content p, .content li',
lvl0: {
selectors: 'section.has-active div h2',
defaultValue: 'Documentation'
},
lvl2: '.content h2',
lvl3: '.content h3',
lvl4: '.content h4',
lvl5: '.content h5'
},
indexHeadings: true
})
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'algolia',
options: {
mode: 'sidePanel',
askAi: {
assistantId: 'XXXYYY',
appId: '...',
apiKey: '...',
indexName: '...',
sidePanel: true
}
}
}
],
initialIndexSettings: {
vitepress: {
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: '<span class="algolia-docsearch-suggestion--highlight">',
highlightPostTag: '</span>',
minWordSizefor1Typo: 3,
minWordSizefor2Typos: 7,
allowTyposOnNumericTokens: false,
minProximity: 1,
ignorePlurals: true,
advancedSyntax: true,
attributeCriteriaComputedByMinProximity: true,
removeWordsIfNoResults: 'allOptional'
}
}
})
```
<style>
img[src="/search.png"] {
width: 100%;
aspect-ratio: 1 / 1;
}
</style>
### Crawler Config
Here is an example config based on what this site uses:
<<< @/snippets/algolia-crawler.js

@ -1,3 +1,7 @@
---
description: Configure the sidebar navigation in the VitePress default theme with groups, collapsible sections, and multiple sidebars.
---
# Sidebar
The sidebar is the main navigation block for your documentation. You can configure the sidebar menu in [`themeConfig.sidebar`](./default-theme-config#sidebar).
@ -180,36 +184,3 @@ export default {
}
}
```
## `useSidebar` <Badge type="info" text="composable" />
Returns sidebar-related data. The returned object has the following type:
```ts
export interface DocSidebar {
isOpen: Ref<boolean>
sidebar: ComputedRef<DefaultTheme.SidebarItem[]>
sidebarGroups: ComputedRef<DefaultTheme.SidebarItem[]>
hasSidebar: ComputedRef<boolean>
hasAside: ComputedRef<boolean>
leftAside: ComputedRef<boolean>
isSidebarEnabled: ComputedRef<boolean>
open: () => void
close: () => void
toggle: () => void
}
```
**Example:**
```vue
<script setup>
import { useSidebar } from 'vitepress/theme'
const { hasSidebar } = useSidebar()
</script>
<template>
<div v-if="hasSidebar">Only show when sidebar exists</div>
</template>
```

@ -1,3 +1,7 @@
---
description: Create team pages with member profiles using VitePress built-in team components.
---
<script setup>
import { VPTeamMembers } from 'vitepress/theme'
@ -53,12 +57,12 @@ const members = [
Say hello to our awesome team.
<VPTeamMembers size="small" :members="members" />
<VPTeamMembers size="small" :members />
```
The above will display a team member in card looking element. It should display something similar to below.
<VPTeamMembers size="small" :members="members" />
<VPTeamMembers size="small" :members />
`<VPTeamMembers>` component comes in 2 different sizes, `small` and `medium`. While it boils down to your preference, usually `small` size should fit better when used in doc page. Also, you may add more properties to each member such as adding "description" or "sponsor" button. Learn more about it in [`<VPTeamMembers>`](#vpteammembers).
@ -107,9 +111,7 @@ const members = [
team, some of whom have chosen to be featured below.
</template>
</VPTeamPageTitle>
<VPTeamMembers
:members="members"
/>
<VPTeamMembers :members />
</VPTeamPage>
```

@ -1,5 +1,6 @@
---
outline: deep
description: Reference of all available frontmatter configuration options for VitePress Markdown pages.
---
# Frontmatter Config
@ -225,3 +226,16 @@ Then you can customize styles of this specific page in `.vitepress/theme/custom.
/* page-specific styles */
}
```
### isHome
- Type: `boolean`
The default theme relies on checks like `frontmatter.layout === 'home'` to determine if the current page is the home page.\
This is useful when you want to force show the home page elements in a custom layout.
```yaml
---
isHome: true
---
```

@ -1,3 +1,7 @@
---
description: Reference of VitePress runtime APIs including composables, helper functions, and built-in components.
---
# Runtime API
VitePress offers several built-in APIs to let you access app data. VitePress also comes with a few built-in components that can be used globally.
@ -49,7 +53,7 @@ interface PageData {
titleTemplate?: string | boolean
description: string
relativePath: string
filePath: string,
filePath: string
headers: Header[]
frontmatter: Record<string, any>
params?: Record<string, any>

@ -1,5 +1,6 @@
---
outline: deep
description: Complete reference of VitePress site configuration options including app-level settings, theming, and build options.
---
# Site Config
@ -24,7 +25,7 @@ export default {
}
```
:::details Dynamic (Async) Config
::: details Dynamic (Async) Config
If you need to dynamically generate the config, you can also default export a function. For example:
@ -333,7 +334,7 @@ export default {
- Type: `string`
- Default: `/`
The base URL the site will be deployed at. You will need to set this if you plan to deploy your site under a sub path, for example, GitHub pages. If you plan to deploy your site to `https://foo.github.io/bar/`, then you should set base to `'/bar/'`. It should always start and end with a slash.
The base URL the site will be deployed at. You will need to set this if you plan to deploy your site under a sub path, for example, GitHub pages. If you plan to deploy your site to `https://foo.github.io/bar/`, then you should set base to `'/bar/'`. It should always start and end with a slash. Relative bases are not supported.
The base is automatically prepended to all the URLs that start with / in other options, so you only need to specify it once.
@ -350,7 +351,7 @@ export default {
- Type: `boolean`
- Default: `false`
When set to `true`, VitePress will remove the trailing `.html` from URLs. Also see [Generating Clean URL](../guide/routing#generating-clean-url).
When set to `true`, VitePress will remove the trailing `.html` from URLs. Also see [Generating Clean URLs](../guide/routing#generating-clean-urls).
::: warning Server Support Required
Enabling this may require additional configuration on your hosting platform. For it to work, your server must be able to serve `/foo.html` when visiting `/foo` **without a redirect**.
@ -439,7 +440,7 @@ export default {
### ignoreDeadLinks
- Type: `boolean | 'localhostLinks' | (string | RegExp | ((link: string) => boolean))[]`
- Type: `boolean | 'localhostLinks' | (string | RegExp | ((link: string, source: string) => boolean))[]`
- Default: `false`
When set to `true`, VitePress will not fail builds due to dead links.
@ -606,7 +607,7 @@ interface SSGContext {
- Type: `(context: TransformContext) => Awaitable<HeadConfig[]>`
`transformHead` is a build hook to transform the head before generating each page. It will allow you to add head entries that cannot be statically added to your VitePress config. You only need to return extra entries, they will be merged automatically with the existing ones.
`transformHead` is a build hook to add extra tags to the `<head>` of each page. It allows you to add head entries that cannot be statically added to your VitePress config. You only need to return extra entries, they will be merged automatically with the existing ones.
::: warning
Don't mutate anything inside the `context`.
@ -634,44 +635,38 @@ interface TransformContext {
}
```
Note that this hook is only called when generating the site statically. It is not called during dev. If you need to add dynamic head entries during dev, you can use the [`transformPageData`](#transformpagedata) hook instead:
This hook is only called when performing a build, it is not called during dev.
```ts
export default {
transformPageData(pageData) {
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'meta',
{
name: 'og:title',
content:
pageData.frontmatter.layout === 'home'
? `VitePress`
: `${pageData.title} | VitePress`
}
])
}
}
```
The extra tags will be added to the static HTML files generated by the build. They will not be updated during client-side navigation.
#### Example: Adding a canonical URL `<link>`
In many cases, using the [`transformPageData`](#transformpagedata) hook is a cleaner solution. That hook will also be applied to both client-side navigation and during dev. But if generating the head tags is computationally expensive then `transformHead` will avoid that overhead during dev.
#### Example: Adding `og:image` meta
```ts
export default {
transformPageData(pageData) {
const canonicalUrl = `https://example.com/${pageData.relativePath}`
.replace(/index\.md$/, '')
.replace(/\.md$/, '.html')
async transformHead(context) {
if (context.page === '404.md') {
return
}
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'link',
{ rel: 'canonical', href: canonicalUrl }
])
// The implementation details of `generatePageImage` would depend
// on your requirements. Here we assume it generates a suitable
// image for each page and returns the image URL.
const imageUrl = await generatePageImage(context)
return [[
'meta',
{ name: 'og:image', content: imageUrl }
]]
}
}
```
Here we're assuming that the image URL is dynamic and time-consuming to generate. Using `transformHead` avoids that overhead during development.
For simpler cases, it may be possible to use the [`head`](./frontmatter-config#head) setting in frontmatter, or [`transformPageData`](#transformpagedata).
### transformHtml
- Type: `(code: string, id: string, context: TransformContext) => Awaitable<string | void>`
@ -720,3 +715,39 @@ interface TransformPageContext {
siteConfig: SiteConfig
}
```
#### Example: Adding a `<meta name="og:title">`
```ts
export default {
transformPageData(pageData) {
const title = pageData.frontmatter.layout === 'home'
? 'VitePress'
: `${pageData.title} | VitePress`
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'meta',
{ name: 'og:title', content: title }
])
}
}
```
#### Example: Adding a canonical URL `<link>`
```ts
export default {
transformPageData(pageData) {
const canonicalUrl = `https://example.com/${pageData.relativePath}`
.replace(/index\.md$/, '')
.replace(/\.md$/, '.html')
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'link',
{ rel: 'canonical', href: canonicalUrl }
])
}
}
```

@ -0,0 +1,332 @@
import { createRequire } from 'module'
import { defineAdditionalConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export default defineAdditionalConfig({
description: 'Generador de Sitios Estáticos desarrollado con Vite y Vue.',
themeConfig: {
nav: nav(),
search: { options: searchOptions() },
sidebar: {
'/es/guide/': { base: '/es/guide/', items: sidebarGuide() },
'/es/reference/': { base: '/es/reference/', items: sidebarReference() }
},
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Editar esta página en GitHub'
},
footer: {
message: 'Liberado bajo la licencia MIT',
copyright: 'Todos los derechos reservados © 2019-PRESENTE Evan You'
},
docFooter: {
prev: 'Anterior',
next: 'Siguiente'
},
outline: {
label: 'En esta página'
},
lastUpdated: {
text: 'Actualizado el'
},
notFound: {
title: 'PÁGINA NO ENCONTRADA',
quote:
'Pero si no cambias de dirección y sigues buscando, podrías terminar donde te diriges.',
linkLabel: 'ir a inicio',
linkText: 'Llévame a inicio'
},
langMenuLabel: 'Cambiar Idioma',
returnToTopLabel: 'Volver arriba',
sidebarMenuLabel: 'Menu Lateral',
darkModeSwitchLabel: 'Tema Oscuro',
lightModeSwitchTitle: 'Cambiar a modo claro',
darkModeSwitchTitle: 'Cambiar a modo oscuro',
skipToContentLabel: 'Saltar al contenido'
}
})
function nav(): DefaultTheme.NavItem[] {
return [
{
text: 'Guía',
link: '/es/guide/what-is-vitepress',
activeMatch: '/es/guide/'
},
{
text: 'Referencia',
link: '/es/reference/site-config',
activeMatch: '/es/reference/'
},
{
text: pkg.version,
items: [
{
text: '1.6.4',
link: 'https://vuejs.github.io/vitepress/v1/es/'
},
{
text: 'Registro de cambios',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: 'Contribuir',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
}
]
}
]
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Introducción',
collapsed: false,
items: [
{ text: '¿Qué es VitePress', link: 'what-is-vitepress' },
{ text: 'Iniciando', link: 'getting-started' },
{ text: 'Enrutamiento', link: 'routing' },
{ text: 'Despliegue', link: 'deploy' }
]
},
{
text: 'Escribiendo',
collapsed: false,
items: [
{ text: 'Extensiones Markdown', link: 'markdown' },
{ text: 'Manejo de assets', link: 'asset-handling' },
{ text: 'Frontmatter', link: 'frontmatter' },
{ text: 'Usando Vue en Markdown', link: 'using-vue' },
{ text: 'Internacionalización', link: 'i18n' }
]
},
{
text: 'Pesonalización',
collapsed: false,
items: [
{ text: 'Usando un tema personalizado', link: 'custom-theme' },
{
text: 'Extendiendo el tema por defecto',
link: 'extending-default-theme'
},
{
text: 'Carga de datos en tiempo de compilación',
link: 'data-loading'
},
{ text: 'Compatibilidad SSR', link: 'ssr-compat' },
{ text: 'Conectando a un CMS', link: 'cms' }
]
},
{
text: 'Experimental',
collapsed: false,
items: [
{ text: 'Modo MPA', link: 'mpa-mode' },
{ text: 'Generación de Sitemap', link: 'sitemap-generation' }
]
},
{
text: 'Configuración y Referencia de la API',
base: '/es/reference/',
link: 'site-config'
}
]
}
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Referencia',
items: [
{ text: 'Configuración del sitio', link: 'site-config' },
{ text: 'Configuración Frontmatter', link: 'frontmatter-config' },
{ text: 'API de tiempo de ejecución', link: 'runtime-api' },
{ text: 'CLI', link: 'cli' },
{
text: 'Tema por defecto',
base: '/es/reference/default-theme-',
items: [
{ text: 'Visión general', link: 'config' },
{ text: 'Navegación', link: 'nav' },
{ text: 'Barra Lateral', link: 'sidebar' },
{ text: 'Página Inicial', link: 'home-page' },
{ text: 'Pie de página', link: 'footer' },
{ text: 'Layout', link: 'layout' },
{ text: 'Distintivo', link: 'badge' },
{ text: 'Página del equipo', link: 'team-page' },
{ text: 'Links Anterior / Siguiente', link: 'prev-next-links' },
{ text: 'Editar Link', link: 'edit-link' },
{ text: 'Sello temporal de actualización', link: 'last-updated' },
{ text: 'Búsqueda', link: 'search' },
{ text: 'Carbon Ads', link: 'carbon-ads' }
]
}
]
}
]
}
function searchOptions(): Partial<DefaultTheme.AlgoliaSearchOptions> {
return {
translations: {
button: {
buttonText: 'Buscar',
buttonAriaLabel: 'Buscar'
},
modal: {
searchBox: {
clearButtonTitle: 'Limpiar',
clearButtonAriaLabel: 'Borrar la consulta',
closeButtonText: 'Cerrar',
closeButtonAriaLabel: 'Cerrar',
placeholderText: 'Buscar en la documentación o preguntar a Ask AI',
placeholderTextAskAi: 'Haz otra pregunta...',
placeholderTextAskAiStreaming: 'Respondiendo...',
searchInputLabel: 'Buscar',
backToKeywordSearchButtonText:
'Volver a la búsqueda por palabras clave',
backToKeywordSearchButtonAriaLabel:
'Volver a la búsqueda por palabras clave',
newConversationPlaceholder: 'Haz una pregunta',
conversationHistoryTitle: 'Mi historial de conversaciones',
startNewConversationText: 'Iniciar una nueva conversación',
viewConversationHistoryText: 'Historial de conversaciones',
threadDepthErrorPlaceholder: 'Se alcanzó el límite de conversación'
},
newConversation: {
newConversationTitle: '¿Cómo puedo ayudarte hoy?',
newConversationDescription:
'Busco en tu documentación para ayudarte a encontrar guías de configuración, detalles de funciones y consejos de solución de problemas rápidamente.'
},
footer: {
selectText: 'Seleccionar',
submitQuestionText: 'Enviar pregunta',
selectKeyAriaLabel: 'Tecla Enter',
navigateText: 'Navegar',
navigateUpKeyAriaLabel: 'Flecha arriba',
navigateDownKeyAriaLabel: 'Flecha abajo',
closeText: 'Cerrar',
backToSearchText: 'Volver a la búsqueda',
closeKeyAriaLabel: 'Tecla Escape',
poweredByText: 'Con la tecnología de'
},
errorScreen: {
titleText: 'No se pueden obtener resultados',
helpText: 'Puede que quieras comprobar tu conexión de red.'
},
startScreen: {
recentSearchesTitle: 'Recientes',
noRecentSearchesText: 'No hay búsquedas recientes',
saveRecentSearchButtonTitle: 'Guardar esta búsqueda',
removeRecentSearchButtonTitle: 'Eliminar esta búsqueda del historial',
favoriteSearchesTitle: 'Favoritos',
removeFavoriteSearchButtonTitle:
'Eliminar esta búsqueda de favoritos',
recentConversationsTitle: 'Conversaciones recientes',
removeRecentConversationButtonTitle:
'Eliminar esta conversación del historial'
},
noResultsScreen: {
noResultsText: 'No se encontraron resultados para',
suggestedQueryText: 'Intenta buscar',
reportMissingResultsText:
'¿Crees que esta consulta debería devolver resultados?',
reportMissingResultsLinkText: 'Avísanos.'
},
resultsScreen: {
askAiPlaceholder: 'Preguntar a la IA: ',
noResultsAskAiPlaceholder:
'¿No lo encontraste en la documentación? Pide ayuda a Ask AI: '
},
askAiScreen: {
disclaimerText:
'Las respuestas se generan con IA y pueden contener errores. Verifícalas.',
relatedSourcesText: 'Fuentes relacionadas',
thinkingText: 'Pensando...',
copyButtonText: 'Copiar',
copyButtonCopiedText: '¡Copiado!',
copyButtonTitle: 'Copiar',
likeButtonTitle: 'Me gusta',
dislikeButtonTitle: 'No me gusta',
thanksForFeedbackText: '¡Gracias por tu comentario!',
preToolCallText: 'Buscando...',
duringToolCallText: 'Buscando...',
afterToolCallText: 'Buscado',
stoppedStreamingText: 'Has detenido esta respuesta',
errorTitleText: 'Error de chat',
threadDepthExceededMessage:
'Esta conversación se ha cerrado para mantener respuestas precisas.',
startNewConversationButtonText: 'Iniciar una nueva conversación'
}
}
},
askAi: {
sidePanel: {
button: {
translations: {
buttonText: 'Preguntar a la IA',
buttonAriaLabel: 'Preguntar a la IA'
}
},
panel: {
translations: {
header: {
title: 'Preguntar a la IA',
conversationHistoryTitle: 'Mi historial de conversaciones',
newConversationText: 'Iniciar una nueva conversación',
viewConversationHistoryText: 'Historial de conversaciones'
},
promptForm: {
promptPlaceholderText: 'Haz una pregunta',
promptAnsweringText: 'Respondiendo...',
promptAskAnotherQuestionText: 'Haz otra pregunta',
promptDisclaimerText:
'Las respuestas se generan con IA y pueden contener errores.',
promptLabelText:
'Pulsa Enter para enviar, o Shift+Enter para una nueva línea.',
promptAriaLabelText: 'Entrada de prompt'
},
conversationScreen: {
preToolCallText: 'Buscando...',
searchingText: 'Buscando...',
toolCallResultText: 'Buscado',
conversationDisclaimer:
'Las respuestas se generan con IA y pueden contener errores. Verifícalas.',
reasoningText: 'Razonando...',
thinkingText: 'Pensando...',
relatedSourcesText: 'Fuentes relacionadas',
stoppedStreamingText: 'Has detenido esta respuesta',
copyButtonText: 'Copiar',
copyButtonCopiedText: '¡Copiado!',
likeButtonTitle: 'Me gusta',
dislikeButtonTitle: 'No me gusta',
thanksForFeedbackText: '¡Gracias por tu comentario!',
errorTitleText: 'Error de chat'
},
newConversationScreen: {
titleText: '¿Cómo puedo ayudarte hoy?',
introductionText:
'Busco en tu documentación para ayudarte a encontrar guías de configuración, detalles de funciones y consejos de solución de problemas rápidamente.'
},
logo: {
poweredByText: 'Con la tecnología de'
}
}
}
}
}
}
}

@ -1,3 +1,7 @@
---
description: Aprende cómo referenciar y manejar recursos estáticos como imágenes, medios y fuentes en VitePress.
---
# Manejo de Assets {#asset-handling}
## Referenciando Assets Estáticos {#referencing-static-assets}

@ -1,4 +1,5 @@
---
description: Conecta VitePress a un CMS headless usando rutas dinámicas y cargadores de datos.
outline: deep
---

@ -1,3 +1,7 @@
---
description: Crea y usa un tema personalizado en VitePress para controlar completamente la apariencia y el estilo de tu sitio.
---
# Usando un Tema Personalizado {#using-a-custom-theme}
## Carga de Tema {#theme-resolving}

@ -1,3 +1,7 @@
---
description: Carga datos arbitrarios en tiempo de compilación usando cargadores de datos de VitePress e impórtalos desde páginas o componentes.
---
# Carga de Datos en Tiempo de Compilacion {#build-time-data-loading}
VitePress proporciona un recurso llamado **cargadores de dato** que permite cargar datos arbitrarios e importarlos desde páginas o componentes. La carga de datos es ejecutada **apenas en el tiempo del build** los datos resultantes serán serializados como JSON en el paquete de JavaScript final.

@ -1,4 +1,5 @@
---
description: Despliega tu sitio VitePress en plataformas populares como Netlify, Vercel, GitHub Pages y más.
outline: deep
---
@ -105,13 +106,13 @@ Nota: el archivo `vercel.json` debe ser colocado en la raiz de su **repositório
## Guias de Plataforma {#platform-guides}
### Netlify / Vercel / Cloudflare Pages / AWS Amplify / Render
### Netlify / Vercel / Cloudflare Pages / AWS Amplify / Render {#generic}
Configure un nuevo proyecto y altere estas configuraciones usando su panel:
- **Comando de Compilación:** `npm run docs:build`
- **directorio de Salida:** `docs/.vitepress/dist`
- **Versión de Node:** `18` (o superior)
- **Versión de Node:** `20` (o superior)
::: warning
No active opciones como _Auto Minify_ para código HTML. Eso removera comentarios de salida que tiene significado para Vue. Habrán errores de incompatibilidad de hidratación se fueran removidos.
@ -153,26 +154,24 @@ No active opciones como _Auto Minify_ para código HTML. Eso removera comentario
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # No necesario se lastUpdated no estuviera habilitado
# - uses: pnpm/action-setup@v3 # Desconecte eso si estuviera usando pnpm
# - uses: pnpm/action-setup@v4 # Desconecte eso si estuviera usando pnpm
# with:
# version: 9
# - uses: oven-sh/setup-bun@v1 # Desconecte eso se estuviera usando Bun
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
cache: npm # o pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: npm ci # o pnpm install / yarn install / bun install
- name: Build with VitePress
run: |
npm run docs:build # o pnpm docs:build / yarn docs:build / bun run docs:build
touch docs/.vitepress/dist/.nojekyll
- name: Build with VitePress
run: npm run docs:build # o pnpm docs:build / yarn docs:build / bun run docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
@ -192,6 +191,7 @@ No active opciones como _Auto Minify_ para código HTML. Eso removera comentario
uses: actions/deploy-pages@v4
```
::: warning
Asegurese de que la opción `base` en su VitePress esté configurada correctamentse. Vea [Configuranco un Path base Público](#setting-a-public-base-path) para más detalles.
:::
@ -201,12 +201,12 @@ No active opciones como _Auto Minify_ para código HTML. Eso removera comentario
### GitLab Pages
1. Defina `outDir` en la configuración VitePress como `../public`. Configure la opción `base` para `'/<repository>/'` se desea implantar en `https://<username>.gitlab.io/<repository>/`.
1. Defina `outDir` en la configuración VitePress como `../public`. Configure la opción `base` para `'/<repository>/'` se desea implantar en `https://<username>.gitlab.io/<repository>/`. No necesita `base` si está implementando en un dominio personalizado, páginas de usuario o grupo, o si la configuración "Use unique domain" está habilitada en GitLab.
2. Cree un archivo llamado `.gitlab-ci.yml` en la raiz del proyecto con el contenido abajo. Esto construirá e implantará su sitio siempre que haga alteraciones en el contenido.
```yaml [.gitlab-ci.yml]
image: node:18
image: node:24
pages:
cache:
paths:
@ -222,7 +222,7 @@ No active opciones como _Auto Minify_ para código HTML. Eso removera comentario
- main
```
### Azure Static Web Apps {#azure-static-web-apps}
### Azure
1. Siga la [documentación oficial](https://docs.microsoft.com/en-us/azure/static-web-apps/build-configuration).
@ -232,7 +232,11 @@ No active opciones como _Auto Minify_ para código HTML. Eso removera comentario
- **`output_location`**: `docs/.vitepress/dist`
- **`app_build_command`**: `npm run docs:build`
### Firebase {#firebase}
### CloudRay
Puedes desplegar tu proyecto VitePress con [CloudRay](https://cloudray.io/) siguiendo estas [instrucciones](https://cloudray.io/articles/how-to-deploy-vitepress-site).
### Firebase
1. Cree `firebase.json` y `.firebaserc` en la raiz de su proyecto:
@ -263,14 +267,6 @@ No active opciones como _Auto Minify_ para código HTML. Eso removera comentario
firebase deploy
```
### Surge
1. Después de ejecutar `npm run docs:build`, ejecute este comando para implantar:
```sh
npx surge docs/.vitepress/dist
```
### Heroku
1. Siga la documentación y el guia proporcionados por [`heroku-buildpack-static`](https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-static).
@ -283,10 +279,66 @@ No active opciones como _Auto Minify_ para código HTML. Eso removera comentario
}
```
### Edgio
### Hostinger
Consulte [Crear e Implantar una Aplicación VitePress en Edgio](https://docs.edg.io/guides/vitepress).
Puedes desplegar tu proyecto VitePress con [Hostinger](https://www.hostinger.com/web-apps-hosting) siguiendo estas [instrucciones](https://www.hostinger.com/support/how-to-deploy-a-nodejs-website-in-hostinger/). Al configurar los ajustes de compilación, elige VitePress como framework y ajusta el directorio raíz a `./docs`.
### Kinsta Static Site Hosting {#kinsta-static-site-hosting}
### Kinsta
Puede implantar su sitio VitePress em [Kinsta](https://kinsta.com/static-site-hosting/) siguiendo estas [instrucciones](https://kinsta.com/docs/vitepress-static-site-example/).
### Stormkit
Puedes desplegar tu proyecto VitePress en [Stormkit](https://www.stormkit.io) siguiendo estas [instrucciones](https://stormkit.io/blog/how-to-deploy-vitepress).
### Surge
1. Después de ejecutar `npm run docs:build`, ejecute este comando para implantar:
```sh
npx surge docs/.vitepress/dist
```
### Nginx
Aquí hay un ejemplo de configuración de bloque de servidor Nginx. Esta configuración incluye compresión gzip para recursos comunes basados en texto, reglas para servir los archivos estáticos de su sitio VitePress con encabezados de caché adecuados, así como el manejo de `cleanUrls: true`.
```nginx
server {
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
listen 80;
server_name _;
index index.html;
location / {
# content location
root /app;
# exact matches -> reverse clean urls -> folders -> not found
try_files $uri $uri.html $uri/ =404;
# non existent pages
error_page 404 /404.html;
# a folder without index.html raises 403 in this setup
error_page 403 /404.html;
# adjust caching headers
# files in the assets folder have hashes filenames
location ~* ^/assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}
```
Esta configuración asume que su sitio VitePress compilado está ubicado en el directorio `/app` de su servidor. Ajuste la directiva `root` según corresponda si los archivos de su sitio se encuentran en otro lugar.
::: warning No predeterminar index.html
La resolución de try_files no debe predeterminar index.html como en otras aplicaciones Vue. Esto resultará en un estado de página inválido.
:::
Se puede encontrar más información en la [documentación oficial de nginx](https://nginx.org/en/docs/), en estos issues [#2837](https://github.com/vuejs/vitepress/discussions/2837), [#3235](https://github.com/vuejs/vitepress/issues/3235) así como en este [post del blog](https://blog.mehdi.cc/articles/vitepress-cleanurls-on-nginx-environment#readings) de Mehdi Merah.

@ -1,4 +1,5 @@
---
description: Personaliza y extiende el tema predeterminado de VitePress con CSS personalizado, componentes, layouts y slots.
outline: deep
---
@ -70,7 +71,7 @@ Si su fuente es un archivo local referenciado via `@font-face`, ella será proce
export default {
transformHead({ assets }) {
// ajuste el regex para corresponder a su fuente
const myFontFile = assets.find(file => /font-name\.\w+\.woff2/)
const myFontFile = assets.find(file => /font-name\.[\w-]+\.woff2/.test(file))
if (myFontFile) {
return [
[
@ -189,6 +190,8 @@ Lista completa de _slots_ disponibles en el layout del tema por defecto:
- `home-hero-before`
- `home-hero-info-before`
- `home-hero-info`
- `home-hero-info-after`
- `home-hero-actions-before-actions`
- `home-hero-actions-after`
- `home-hero-image`
- `home-hero-after`
@ -251,6 +254,7 @@ provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
{
duration: 300,
easing: 'ease-in',
fill: 'forwards',
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
}
)

@ -1,3 +1,7 @@
---
description: Aprende cómo usar frontmatter YAML en archivos Markdown de VitePress para controlar los metadatos y el comportamiento a nivel de página.
---
# Frontmatter
## Uso {#usage}

@ -1,3 +1,7 @@
---
description: Comienza a trabajar con VitePress. Aprende cómo instalar, crear la estructura y comenzar a desarrollar tu sitio de documentación.
---
# Iniciando {#getting-started}
## Experimente Online {#try-it-online}
@ -8,7 +12,7 @@ Puede experimentar VitePress directamente en su navegador en [StackBlitz](https:
### Prerrequisitos {#prerequisites}
- [Node.js](https://nodejs.org/) versión 18 o superior.
- [Node.js](https://nodejs.org/) versión 20 o superior.
- Terminal para acessar VitePress a través de su interfaz de linea de comando (CLI).
- Editor de texto con soporte a sintaxis [Markdown](https://en.wikipedia.org/wiki/Markdown).
- [VSCode](https://code.visualstudio.com/) es recomendado, junto con la [extensión oficial Vue](https://marketplace.visualstudio.com/items?itemName=Vue.volar).
@ -18,35 +22,19 @@ VitePress puede ser usado solo, o ser instalado en un proyecto ya existente. En
::: code-group
```sh [npm]
$ npm add -D vitepress
$ npm add -D vitepress@next
```
```sh [pnpm]
$ pnpm add -D vitepress
$ pnpm add -D vitepress@next
```
```sh [yarn]
$ yarn add -D vitepress
$ yarn add -D vitepress@next
```
```sh [bun]
$ bun add -D vitepress
```
:::
::: details Recibiendo avisos sobre dependencias ausentes?
Si usa PNPM, percibirá un aviso de ausencia de `@docsearch/js`. Esto no evita que VitePress funcione. Si desea eliminar este aviso, adicione lo siguiente en su `package.json`:
```json
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@algolia/client-search",
"search-insights"
]
}
}
$ bun add -D vitepress@next
```
:::

@ -1,3 +1,7 @@
---
description: Configura la internacionalización (i18n) en VitePress para soportar múltiples idiomas en tu sitio.
---
# Internacionalización {#internationalization}
Para usar recursos de i18n integrados, es necesario crear una estructura de directorios de la siguiente forma:

@ -1,3 +1,7 @@
---
description: Extensiones Markdown integradas en VitePress, incluyendo contenedores personalizados, bloques de código con resaltado de sintaxis, resaltado de líneas, grupos de código y más.
---
# Extensiones Markdown {#markdown-extensions}
VitePress viene con Extensiones embutidas.
@ -234,7 +238,7 @@ Este es un recipiente especial que puee ser usado para evitar conflictos de esti
```md
::: raw
Envuelve en un <div class="vp-raw">
Envuelve en un `<div class="vp-raw">`
:::
```
@ -256,11 +260,11 @@ La clase `vp-raw` también puede ser usada directamente en elementos. El aislami
}
```
El utiliza [`postcss-prefix-selector`](https://github.com/postcss/postcss-load-config) internamente. Puede pasar opciones así:
Puede pasar opciones así:
```js
postcssIsolateStyles({
includeFiles: [/vp-doc\.css/] // o padrão é /base\.css/
includeFiles: [/custom\.css/] // o padrão é [/vp-doc\.css/, /base\.css/]
})
```
@ -773,7 +777,7 @@ Por ejemplo, puede incluir un archivo markdown relativo usando esto:
## Conceptos Básicos
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**Archivo de Parte** (`parts/basics.md`)
@ -809,7 +813,7 @@ También soporta la selección de un intervalo de lineas:
## Conceptos Básicos
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**Archivo de Parte** (`parts/basics.md`)
@ -844,7 +848,7 @@ Observe que esto no genera errores si el archivo no está presente. Por lo tanto
Esto es actualmente opcional. Para activarlo, necesita instalar `markdown-it-mathjax3` y definir `markdown.math` como `true` en su archivo de configuración:
```sh
npm add -D markdown-it-mathjax3
npm add -D markdown-it-mathjax3@^4
```
```ts [.vitepress/config.ts]

@ -1,3 +1,7 @@
---
description: Habilita el modo MPA (Aplicación de Múltiples Páginas) en VitePress para páginas sin JavaScript con mejor rendimiento inicial.
---
# Modo MPA <Badge type="warning" text="experimental" /> {#mpa-mode}
El modo MPA (Aplicación de multiples páginas) puede ser habilitado por la linea de comandos con `vitepress build --mpa`, o a través de la configuración por la opción `mpa: true`.

@ -1,4 +1,5 @@
---
description: Comprende el enrutamiento basado en archivos de VitePress, rutas dinámicas, URLs limpias y reescritura de rutas.
outline: deep
---
@ -121,7 +122,7 @@ Alternativamente, puede usarse directamente la sintaxis de tag anchor:
:::
## Generación de URL Limpia {#generating-clean-url}
## Generación de URLs Limpias {#generating-clean-urls}
::: warning Soporte del Servidor Necesario
Para servir URLs limpias con VitePress, es necesario soporte en el lado del servidor.

@ -1,3 +1,7 @@
---
description: Genera un archivo sitemap.xml para tu sitio VitePress para mejorar la visibilidad en motores de búsqueda.
---
# Generación de Sitemap {#sitemap-generation}
VitePress viene con soporte embutido para generar un archivo `sitemap.xml` para su sitio. Para habilitar, adicione lo siguiente a su `.vitepress/config.js`:

@ -1,16 +1,17 @@
---
description: Asegura que los componentes del tema y el código personalizado de VitePress sean compatibles con el renderizado del lado del servidor.
outline: deep
---
# Compatibilidad SSR {#ssr-compatibility}
VitePress pre-interpreta la aplicación en Node.js durante la compilación del producción, utilizando las capacidades de Interpretación del lado del servidor (SSR) de Vue. Esto significa que todo el código personalizado en los componentes del tema está sujeto a la compatibilidad SSR.
VitePress pre-renderiza la aplicación en Node.js durante la compilación de producción, utilizando las capacidades de Renderizado del Lado del Servidor (SSR) de Vue. Esto significa que todo el código personalizado en los componentes del tema está sujeto a la Compatibilidad con SSR.
La [sección SSR en la documentación Vue oficial](https://vuejs.org/guide/scaling-up/ssr.html) proporciona más contexto sobre lo que es SSR, la relación entre SSR / SSG y notas comunes sobre escribir código amigable con SSR. La regla general es acceder apenas APIs deln navegador / DOM en los hooks `beforeMount` o `mounted` de los componentes Vue.
La [sección SSR en la documentación Vue oficial](https://vuejs.org/guide/scaling-up/ssr.html) proporciona más contexto sobre lo que es SSR, la relación entre SSR / SSG y notas comunes sobre escribir código amigable para SSR. La regla general es acceder a las APIs del navegador / DOM solo en los hooks `beforeMount` o `mounted` de los componentes de Vue.
## `<ClientOnly>`
Se está usando o demostrando componentes que no son compatibles con SSR (por ejemplo, contienen directivas personalizadas), puede envolverlos en el componente embutido `<ClientOnly>`:
Si está usando o demostrando componentes que no son compatibles con SSR (por ejemplo, contienen directivas personalizadas), puede envolverlos en el componente incorporado `<ClientOnly>`:
```md
<ClientOnly>
@ -20,7 +21,7 @@ Se está usando o demostrando componentes que no son compatibles con SSR (por ej
## Bibliotecas que Acceden el API del Navegador en la Importación {#libraries-that-access-browser-api-on-import}
Algunos componentes o bibliotecas acceden APIs del navegador **en la Importación**. Para usar código que asume un ambiente de navegador en la importación, necesita importarlo dinámicamente.
Algunos componentes o librerías acceden a las APIs del navegador **al momento de ser importados**. Para usar código que asume un entorno de navegador en la importación, necesita importarlos dinámicamente.
### Importando en el Hook `mounted` {#importing-in-mounted-hook}
@ -29,7 +30,7 @@ Algunos componentes o bibliotecas acceden APIs del navegador **en la Importació
import { onMounted } from 'vue'
onMounted(() => {
import('./lib-que-accede-window-en-la-importacion').then((module) => {
import('./lib-que-accede-a-window-en-la-importacion').then((module) => {
// usar código
})
})
@ -38,17 +39,17 @@ onMounted(() => {
### Importación Condicional {#conditional-import}
Puede también importar condicionalmente usando el flag `import.meta.env.SSR` (parte de las [variables de entorno Vite](https://vitejs.dev/guide/env-and-mode.html#env-variables)):
También puede importar una dependencia condicionalmente utilizando la bandera `import.meta.env.SSR` (que forma parte de las [variables de entorno Vite](https://vitejs.dev/guide/env-and-mode.html#env-variables)):
```js
if (!import.meta.env.SSR) {
import('./lib-que-accede-window-en-la-importacion').then((module) => {
import('./lib-que-accede-a-window-en-la-importacion').then((module) => {
// usar código
})
}
```
Como [`Theme.enhanceApp`](./custom-theme#theme-interface) puede ser asíncrono, puede importar condicionalmente y registrar plugins Vue que acceden APIs del navegador en la importación:
Dado que [`Theme.enhanceApp`](./custom-theme#theme-interface) puede ser asíncrono, puede importar y registrar condicionalmente plugins de Vue que accedan a las APIs del navegador al ser importados:
```js [.vitepress/theme/index.js]
/** @type {import('vitepress').Theme} */
@ -56,7 +57,7 @@ export default {
// ...
async enhanceApp({ app }) {
if (!import.meta.env.SSR) {
const plugin = await import('plugin-que-accede-window-en-la-importacion')
const plugin = await import('plugin-que-accede-a-window-en-la-importacion')
app.use(plugin.default)
}
}
@ -71,7 +72,7 @@ export default {
// ...
async enhanceApp({ app }) {
if (!import.meta.env.SSR) {
const plugin = await import('plugin-que-accede-window-en-la-importacion')
const plugin = await import('plugin-que-accede-a-window-en-la-importacion')
app.use(plugin.default)
}
}
@ -80,14 +81,14 @@ export default {
### `defineClientComponent`
VitePress proporciona un auxiliar de conveniencia para importar componentes Vue que acceden APIs del navegador en la importación.
VitePress proporciona un auxiliar de conveniencia (helper) para importar componentes Vue que acceden a las APIs del navegador al ser importados.
```vue
<script setup>
import { defineClientComponent } from 'vitepress'
const ClientComp = defineClientComponent(() => {
return import('componente-que-accede-window-en-la-importacion')
return import('componente-que-accede-a-window-en-la-importacion')
})
</script>
@ -96,7 +97,7 @@ const ClientComp = defineClientComponent(() => {
</template>
```
Puede también pasar propiedades/hijos/_slots_ para el componente objetivo:
Puede pasar propiedades/hijos/_slots_ al componente objetivo:
```vue
<script setup>
@ -105,9 +106,9 @@ import { defineClientComponent } from 'vitepress'
const clientCompRef = ref(null)
const ClientComp = defineClientComponent(
() => import('componente-que-acessa-window-na-importacao'),
() => import('componente-que-accede-a-window-en-la-importacion'),
// los argumentos son pasados para h() - https://vuejs.org/api/render-function.html#h
// los argumentos se pasan a h() - https://vuejs.org/api/render-function.html#h
[
{
ref: clientCompRef

@ -1,3 +1,7 @@
---
description: Usa componentes Vue y funciones de plantillas dinámicas directamente dentro de archivos Markdown en VitePress.
---
# Usando Vue en Markdown {#using-vue-in-markdown}
En VitePress, cada archivo Markdown es compilado para HTML y entonces procesado como un [Componente de Archivo Único de Vue](https://vuejs.org/guide/scaling-up/sfc.html). Esto significa que puede usar cualquier funcionalidad de Vue dentro del Markdown, incluyendo la interpolación dinámica, usar componentes Vue o lógica arbitrária de componentes Vue dentro de la página adicionando una tag `<script>`.
@ -128,7 +132,7 @@ Si un componente fuera usado en la mayoría de las páginas, ellos pueden ser re
Asegurese de que el nombre de un componente personalizado contenga un hífen o esté en PascalCase. Caso contrario, el será tratado como un elemento alineado y envuelto dentro de una tag `<p>`, lo que llevará a una incompatibilidad de hidratación pues `<p>` no permite que elementos de bloque sean colocados dentro de el.
:::
### Usando Componentes En Headers <ComponenteEnHeader /> {#using-components-in-headers}
### Usando Componentes En Headers <ComponentInHeader /> {#using-components-in-headers}
Puede usar componentes Vue en los headers, pero observe la diferencia entre las siguientes sintaxis:
@ -286,4 +290,4 @@ Vue ofrece soporte para IntelliSense de forma predeterminada mediante el [Plugin
"vue.server.includeLanguages": ["vue", "markdown"]
}
```
:::
:::

@ -1,10 +1,14 @@
# Qué es VitePress? {#what-is-vitepress}
---
description: VitePress es un generador de sitios estáticos diseñado para crear sitios web rápidos y centrados en el contenido, impulsado por Vite y Vue.
---
VitePress es un [Generador de Sitios Estáticos](https://en.wikipedia.org/wiki/Static_site_generator) (SSG) proyectado para crear sitios rápidos y centrados en contenido. En suma, VitePress utiliza su contenido fuente escrito en [Markdown](https://en.wikipedia.org/wiki/Markdown), aplica un tema a el y genera páginas HTML estáticas que pueden ser facilmente implantadas en cualquier lugar.
# ¿Qué es VitePress? {#what-is-vitepress}
VitePress es un [Generador de Sitios Estáticos](https://en.wikipedia.org/wiki/Static_site_generator) (SSG) diseñado para construir sitios web rápidos y enfocados en el contenido. En pocas palabras, VitePress toma tu contenido fuente escrito en [Markdown](https://en.wikipedia.org/wiki/Markdown), le aplica un tema y genera páginas HTML estáticas que se pueden desplegar fácilmente en cualquier lugar.
<div class="tip custom-block" style="padding-top: 8px">
Quiere apenas experimentar? Valla al [Início Rápido](./getting-started).
¿Quieres probarlo? Ve directo al [Inicio Rápido](./getting-started).
</div>
@ -12,46 +16,46 @@ Quiere apenas experimentar? Valla al [Início Rápido](./getting-started).
- **Documentación**
VitePress viene con un tema por defecto proyectado para documentación técnica. El alimenta esta página que está leyendo ahora, juntamente con la documentación [Vite](https://vitejs.dev/), [Rollup](https://rollupjs.org/), [Pinia](https://pinia.vuejs.org/), [VueUse](https://vueuse.org/), [Vitest](https://vitest.dev/), [D3](https://d3js.org/), [UnoCSS](https://unocss.dev/), [Iconify](https://iconify.design/) e [muchos otros](https://www.vuetelescope.com/explore?framework.slug=vitepress).
VitePress incluye un tema por defecto diseñado para documentación técnica. Este tema es el que se utiliza en la página que estás leyendo ahora, así como en la documentación de [Vite](https://vitejs.dev/), [Rollup](https://rollupjs.org/), [Pinia](https://pinia.vuejs.org/), [VueUse](https://vueuse.org/), [Vitest](https://vitest.dev/), [D3](https://d3js.org/), [UnoCSS](https://unocss.dev/), [Iconify](https://iconify.design/) y [muchos otros](https://github.com/search?q=/%22vitepress%22:+/+path:/(?:package%7Cdeno)%5C.jsonc?$/+NOT+is:fork+NOT+is:archived&type=code).
La [documentación oficial Vue.js](https://vuejs.org/) también está basada en VitePress, pero usa un tema personalizado compartido entre varias traducciones.
La [documentación oficial Vue.js](https://vuejs.org/) también está basada en VitePress, pero utiliza un tema personalizado compartido entre varias traducciones.
- **Blogs, Portfólios y sitios de Marketing**
- **Blogs, Portfolios y sitios de Marketing**
VitePress soporta [temas totalmente personalizables](./custom-theme), con la experiencia de desarrollador por defecto de una aplicaciónn Vite + Vue. La construcción con Vite significa que puede aprovechar directamente plugins Vite de su rico ecosistema. Adicionalmente, VitePress proporciona APIs flexibles para[cargar datos](./data-loading) (locales o remotos) y [generar rutas dinámicamente](./routing#dynamic-routes). Puede usarlo para construir practicamente cualquier cosa desde que los datos puedan ser determinados en el momento del build.
VitePress soporta [temas completamente personalizables](./custom-theme), con la experiencia de desarrollo de una aplicación estándar de Vite + Vue. Al estar construido sobre Vite, también puedes aprovechar directamente los plugins de su rico ecosistema. Adicionalmente, VitePress proporciona APIs flexibles para [cargar datos](./data-loading) (locales o remotos) y [generar rutas dinámicamente](./routing#dynamic-routes). Puedes usarlo para construir prácticamente cualquier cosa, siempre y cuando los datos puedan ser determinados en el momento de la construcción.
El [blog oficial Vue.js](https://blog.vuejs.org/) es un blog simple que genera su página inicial basada en contenido local.
El [blog oficial Vue.js](https://blog.vuejs.org/) es un blog simple que genera su página de inicio basándose en contenido local.
## Experiencia de Desarrollador {#developer-experience}
VitePress visa proporcionar excelente Experiencia de Desarrollador (DX) al trabajar con contenido en Markdown.
VitePress busca ofrecer una excelente Experiencia de Desarrollador (DX) al trabajar con contenido Markdown.
- **[Alimentado por Vite:](https://vitejs.dev/)** inicialización instantánea del servidor, con ediciones siempre reflejadas instantáneamente (<100ms) sin recarga de página.
- **[Con tecnología Vite:](https://vitejs.dev/)** inicio instantáneo del servidor, con los cambios reflejados al instante (<100ms) sin recargar la página.
- **[Extensiones Markdown Integradas:](./markdown)** Frontmatter, tablas, destaque de sintaxis... usted escoje. Especificamente, VitePress proporciona muchos recursos para trabajar con bloques de código, tornandolo ideal para documentación altamente técnica.
- **[Extensiones Markdown Integradas:](./markdown)** Frontmatter, tablas, destaque de sintaxis... tú decides. Específicamente, VitePress proporciona muchos recursos para trabajar con bloques de código, tornándolo ideal para documentación altamente técnica.
- **[Markdown Mejorado por Vue:](./using-vue)** cada página Markdown es también un [Componente de Archivo único](https://pt.vuejs.org/guide/scaling-up/sfc.html), gracias a la compatibilidad de sintaxis de 100% del template Vue con HTML. Puede también incorporar iteractividad con su contenido estático usando recursos de template Vue o componentes Vue importados.
- **[Markdown Mejorado con Vue:](./using-vue)** cada página Markdown es también un [Componente de Archivo único](https://vuejs.org/guide/scaling-up/sfc.html) de Vue, gracias a la compatibilidad del 100% de la sintaxis de las plantillas de Vue con HTML. Puedes incrustar interactividad en tu contenido estático usando las funciones de plantillas de Vue o componentes de Vue importados.
## Desempeño {#performance}
Al contrario de muchos SSGs tradicionales, un sitio generado por VitePress es la verdad una [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) (SPA).
A diferencia de muchos SSG tradicionales donde cada navegación resulta en una recarga completa de la página, un sitio web generado por VitePress sirve HTML estático en la visita inicial, pero se convierte en una [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) (SPA) para las navegaciones posteriores dentro del sitio. Este modelo, en nuestra opinión, ofrece un equilibrio óptimo para el rendimiento:
- **Carga Inicial Rápida**
La visita inicial a cualquier página será servida con el HTML estático pré-renderizado para velocidad de carga rápida y SEO optimizado. La página entonces carga un paquete JavaScript que transforma la página en una SPA Vue ("hidratación"). El proceso de hidratación es extremadamente rápido: en [PageSpeed Insights](https://pagespeed.web.dev/report?url=https%3A%2F%2Fvitepress.dev%2F), sitios típicos VitePress alcanzan puntuaciones de desempeño casi perfectas, incluso en dispositivos móbiles de bajo desempeño con una red lenta.
La visita inicial a cualquier página será servida con el HTML estático pre-renderizado para una velocidad de carga rápida y SEO óptimo. La página entonces carga un paquete JavaScript que transforma la página en una SPA de Vue (a este proceso se le llama "hidratación"). A diferencia de la creencia popular de que la hidratación de una SPA es lenta, este proceso es de hecho extremadamente rápido gracias al rendimiento nativo y a las optimizaciones del compilador de Vue 3. En [PageSpeed Insights](https://pagespeed.web.dev/report?url=https%3A%2F%2Fvitepress.dev%2F), los sitios típicos VitePress alcanzan puntuaciones de desempeño casi perfectas, incluso en dispositivos móbiles de gama baja con una red lenta.
- **Navegación Rápida pos-carga**
Más importante todavía, el modelo SPA lleva a una mejor experiencia del usuario **después** de la carga inicial. La navegación subsequente dentro del sitio no causará una recarga adicional completa de la página. En vez de eso, el contenido de la página de entrada será buscado y actualizado dinámicamente. VitePress también pre-carga automáticamente pedazos de página para links que están dentro del viewport. En la mayoría de los casos, la navegación pos-carga parecerá instantánea.
Más importante aún, el modelo SPA conduce a una mejor experiencia de usuario **después** de la carga inicial. Las navegaciones posteriores dentro del sitio ya no causarán una recarga completa de la página. En vez de eso, el contenido de la página a la que se accede se buscará y actualizará dinámicamente. VitePress también pre-carga automáticamente fragmentos de las páginas para los enlaces que están dentro del viewport. En la mayoría de los casos, la navegación pos-carga se sentirá instantánea.
- **Interactividad Sin Penalidades**
- **Interactividad Sin Penalización**
Para ser capaz de hidratar las partes dinámicas Vue incorporadas dentro del Markdown estático, cada página Markdown es procesada como un componente Vue y compilada en JavaScript. Esto puede parecer ineficiente, pero el compilador Vue es suficientemente inteligente para separar las partes estáticas y dinámicas, minimizando tanto el costo de hidratación así como el tamaño de carga. Para la carga inicial de la página, las partes estáticas son automáticamente eliminadas de la carga JavaScript y omitidas durante la hidratación.
Para ser capaz de hidratar las partes dinámicas de Vue incrustadas dentro del Markdown estático, cada página Markdown es procesada como un componente Vue y compilada en JavaScript. Esto puede parecer ineficiente, pero el compilador de Vue es lo suficientemente inteligente como para separar las partes estáticas y dinámicas, minimizando tanto el costo de hidratación así como el tamaño de carga útil (payload). Para la carga inicial de la página, las partes estáticas son automáticamente eliminadas del payload de JavaScript y se omiten durante la hidratación.
## Y VuePress? {#what-about-vuepress}
## ¿Y VuePress? {#what-about-vuepress}
VitePress es el sucesor espiritual de VuePress. VuePress era orginalmente basado en Vue 2 y webpack. Con Vue 3 y Vite, VitePress ofrece una experiencia de desarrollador significamente mejor, mejor desempeño en producción, un tema por defecto más pulido y un API de personalización más flexible.
VitePress es el sucesor espiritual de VuePress. El VuePress original se basó en Vue 2 y webpack. Con Vue 3 y Vite como base, VitePress ofrece una Experiencia de Desarrollador (DX) significativamente mejor, un mejor rendimiento en producción, un tema por defecto más pulido y una API de personalización más flexible.
A diferencia del API entre VitePress y VuePress reside principalmente en temas y personalización. Si estuviera usando VuePress 1 con el tema por defecto, la migración para VitePress debe ser relativamente simple.
La diferencia entre la API de VitePress y VuePress radica principalmente en los temas y la personalización. Si estás usando VuePress 1 con el tema por defecto, debería ser relativamente sencillo migrar a VitePress.
También hubo esfuerzo invertido en VuePress 2, que también soporta Vue 3 y Vite con mejor compatibilidad que con VuePress 1. Sin embargo, mantener dos SSGs en paralelo no es sustentable, entonces el equipo Vue decidió enfocarse en VitePress como el principal SSG recomendado a largo plazo.
También se ha invertido esfuerzo en VuePress 2, que también es compatible con Vue 3 y Vite, y tiene mayor compatibilidad con VuePress 1. Sin embargo, mantener dos SSG en paralelo no es sostenible, por lo que el equipo de Vue ha decidido centrarse en VitePress como el principal SSG recomendado a largo plazo.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save