Merge branch 'main' into code-block-title

pull/5007/head
bluwy 7 months ago
commit 62ca48b791

@ -38,11 +38,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- uses: pnpm/action-setup@v3 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: pnpm cache: pnpm
- run: pnpm install - run: pnpm install
- run: pnpm build - run: pnpm build

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

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

@ -1,3 +1,54 @@
## [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) ## [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 ### Bug Fixes

9
client.d.ts vendored

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

@ -153,17 +153,17 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled 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: # with:
# version: 9 # Not needed if you've set "packageManager" in package.json # 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 # - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: npm # or pnpm / yarn cache: npm # or pnpm / yarn
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4

@ -967,7 +967,7 @@ and include it like this:
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: 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 ```sh
npm add -D markdown-it-mathjax3 npm add -D markdown-it-mathjax3@^4
``` ```
```ts [.vitepress/config.ts] ```ts [.vitepress/config.ts]

@ -12,7 +12,7 @@ Just want to try it out? Skip to the [Quickstart](./getting-started).
- **Documentation** - **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://github.com/search?q=/"vitepress":+/+language:json&type=code). 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. The [official Vue.js documentation](https://vuejs.org/) is also based on VitePress, but uses a custom theme shared between multiple translations.

@ -11,7 +11,7 @@ To fix this in **GitHub Actions**, use the following in your workflow:
```yaml{4} ```yaml{4}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
``` ```

@ -29,6 +29,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-search>
- <https://www.npmjs.com/package/vitepress-plugin-pagefind> - <https://www.npmjs.com/package/vitepress-plugin-pagefind>
- <https://www.npmjs.com/package/@orama/plugin-vitepress> - <https://www.npmjs.com/package/@orama/plugin-vitepress>
- <https://www.npmjs.com/package/vitepress-plugin-typesense>
### i18n {#local-search-i18n} ### i18n {#local-search-i18n}

@ -333,7 +333,7 @@ export default {
- Type: `string` - Type: `string`
- Default: `/` - 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. The base is automatically prepended to all the URLs that start with / in other options, so you only need to specify it once.

@ -153,17 +153,17 @@ No active opciones como _Auto Minify_ para código HTML. Eso removera comentario
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # No necesario se lastUpdated no estuviera habilitado 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: # with:
# version: 9 # version: 9
# - uses: oven-sh/setup-bun@v1 # Desconecte eso se estuviera usando Bun # - uses: oven-sh/setup-bun@v1 # Desconecte eso se estuviera usando Bun
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: npm # o pnpm / yarn cache: npm # o pnpm / yarn
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4

@ -844,7 +844,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: Esto es actualmente opcional. Para activarlo, necesita instalar `markdown-it-mathjax3` y definir `markdown.math` como `true` en su archivo de configuración:
```sh ```sh
npm add -D markdown-it-mathjax3 npm add -D markdown-it-mathjax3@^4
``` ```
```ts [.vitepress/config.ts] ```ts [.vitepress/config.ts]

@ -12,7 +12,7 @@ VitePress es un [Generador de Sitios Estáticos](https://en.wikipedia.org/wiki/S
- **Documentación** - **Documentación**
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=/"vitepress":+/+language:json&type=code). 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 utiliza 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.

@ -153,15 +153,15 @@ Cache-Control: max-age=31536000,immutable
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled fetch-depth: 0 # Not needed if lastUpdated is not enabled
# - uses: pnpm/action-setup@v3 # Uncomment this if you're using pnpm # - uses: pnpm/action-setup@v4 # Uncomment this if you're using pnpm
# - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun # - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: npm # or pnpm / yarn cache: npm # or pnpm / yarn
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4

@ -833,7 +833,7 @@ export default config
در حال حاضر این گزینه اختیاری است. برای فعال‌سازی آن، باید `markdown-it-mathjax3` را نصب کرده و `markdown.math` را در فایل پیکربندی خود به `true` تنظیم کنید: در حال حاضر این گزینه اختیاری است. برای فعال‌سازی آن، باید `markdown-it-mathjax3` را نصب کرده و `markdown.math` را در فایل پیکربندی خود به `true` تنظیم کنید:
```sh ```sh
npm add -D markdown-it-mathjax3 npm add -D markdown-it-mathjax3@^4
``` ```
```ts [.vitepress/config.ts] ```ts [.vitepress/config.ts]

@ -12,7 +12,7 @@
- **مستندسازی** - **مستندسازی**
ویت‌پرس با یک تم پیش‌فرض طراحی شده برای مستندات فنی ارائه می‌شود. این صفحه‌ای که اکنون در حال خواندن آن هستید و همچنین مستندات [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/) و [بسیاری دیگر](https://github.com/search?q=/"vitepress":+/+language:json&type=code) با استفاده از ویت‌پرس ساخته شده‌اند. ویت‌پرس با یک تم پیش‌فرض طراحی شده برای مستندات فنی ارائه می‌شود. این صفحه‌ای که اکنون در حال خواندن آن هستید و همچنین مستندات [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/) و [بسیاری دیگر](https://github.com/search?q=/%22vitepress%22:+/+path:/(?:package%7Cdeno)%5C.jsonc?$/+NOT+is:fork+NOT+is:archived&type=code) با استفاده از ویت‌پرس ساخته شده‌اند.
[مستندات رسمی Vue.js](https://vuejs.org/) نیز بر پایه ویت‌پرس ساخته شده است، اما از یک تم سفارشی که بین چندین ترجمه مشترک است استفاده می‌کند. [مستندات رسمی Vue.js](https://vuejs.org/) نیز بر پایه ویت‌پرس ساخته شده است، اما از یک تم سفارشی که بین چندین ترجمه مشترک است استفاده می‌کند.

@ -158,17 +158,17 @@ HTML の _Auto Minify_ のようなオプションを有効にしないでくだ
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled 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: # with:
# version: 9 # Not needed if you've set "packageManager" in package.json # 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 # - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: npm # or pnpm / yarn cache: npm # or pnpm / yarn
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4

@ -954,7 +954,7 @@ VS Code のリージョンの代わりに、ヘッダーアンカーを使って
この機能はオプトインです。利用するには `markdown-it-mathjax3` をインストールし、設定ファイルで `markdown.math``true` に設定します。 この機能はオプトインです。利用するには `markdown-it-mathjax3` をインストールし、設定ファイルで `markdown.math``true` に設定します。
```sh ```sh
npm add -D markdown-it-mathjax3 npm add -D markdown-it-mathjax3@^4
``` ```
```ts [.vitepress/config.ts] ```ts [.vitepress/config.ts]

@ -12,7 +12,7 @@ VitePress は、高速でコンテンツ中心の Web サイトを構築する
- **ドキュメント** - **ドキュメント**
VitePress には技術ドキュメント向けに設計されたデフォルトテーマが同梱されています。今あなたが読んでいるこのページのほか、[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/) など、[まだまだたくさん](https://github.com/search?q=/"vitepress":+/+language:json&type=code)のドキュメントサイトで使われています。 VitePress には技術ドキュメント向けに設計されたデフォルトテーマが同梱されています。今あなたが読んでいるこのページのほか、[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/) など、[まだまだたくさん](https://github.com/search?q=/%22vitepress%22:+/+path:/(?:package%7Cdeno)%5C.jsonc?$/+NOT+is:fork+NOT+is:archived&type=code)のドキュメントサイトで使われています。
[公式の Vue.js ドキュメント](https://vuejs.org/) も VitePress をベースにしています(複数言語で共有されるカスタムテーマを使用)。 [公式の Vue.js ドキュメント](https://vuejs.org/) も VitePress をベースにしています(複数言語で共有されるカスタムテーマを使用)。

@ -11,7 +11,7 @@ VitePress は各ファイルの **直近の Git コミットのタイムスタ
```yaml{4} ```yaml{4}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
``` ```

@ -152,17 +152,17 @@ HTML 코드에 대해 _Auto Minify_ 옵션을 활성화하지 마세요. 이는
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # lastUpdated가 활성화되지 않은 경우 필요하지 않음 fetch-depth: 0 # lastUpdated가 활성화되지 않은 경우 필요하지 않음
# - uses: pnpm/action-setup@v3 # pnpm을 사용하는 경우 주석 해제 # - uses: pnpm/action-setup@v4 # pnpm을 사용하는 경우 주석 해제
# with: # with:
# version: 9 # version: 9
# - uses: oven-sh/setup-bun@v1 # Bun을 사용하는 경우 주석 해제 # - uses: oven-sh/setup-bun@v1 # Bun을 사용하는 경우 주석 해제
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: npm # 또는 pnpm / yarn cache: npm # 또는 pnpm / yarn
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4

@ -880,7 +880,7 @@ Can be created using `.foorc.json`.
선택 사항입니다. 활성화하려면 `markdown-it-mathjax3`를 설치하고 설정 파일에서 `markdown.math``true`로 설정해야 합니다: 선택 사항입니다. 활성화하려면 `markdown-it-mathjax3`를 설치하고 설정 파일에서 `markdown.math``true`로 설정해야 합니다:
```sh ```sh
npm add -D markdown-it-mathjax3 npm add -D markdown-it-mathjax3@^4
``` ```
```ts [.vitepress/config.ts] ```ts [.vitepress/config.ts]

@ -12,7 +12,7 @@ VitePress는 빠르고 컨텐츠 중심의 웹사이트를 구축하기 위해
- **문서화** - **문서화**
VitePress는 기술 문서를 위해 설계된 기본 테마가 함께 제공됩니다. 지금 읽고 있는 이 페이지와 [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/) 및 [다양한 프로젝트](https://github.com/search?q=/"vitepress":+/+language:json&type=code) 문서는 모두 이 테마를 기반으로 합니다. VitePress는 기술 문서를 위해 설계된 기본 테마가 함께 제공됩니다. 지금 읽고 있는 이 페이지와 [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/) 및 [다양한 프로젝트](https://github.com/search?q=/%22vitepress%22:+/+path:/(?:package%7Cdeno)%5C.jsonc?$/+NOT+is:fork+NOT+is:archived&type=code) 문서는 모두 이 테마를 기반으로 합니다.
[Vue.js 공식 문서](https://vuejs.org/)도 VitePress 기반으로 되어 있으며, 여러 번역본에 걸쳐 공유되는 커스텀 테마를 사용합니다. [Vue.js 공식 문서](https://vuejs.org/)도 VitePress 기반으로 되어 있으며, 여러 번역본에 걸쳐 공유되는 커스텀 테마를 사용합니다.

@ -15,7 +15,7 @@
"open-cli": "^8.0.0", "open-cli": "^8.0.0",
"postcss-rtlcss": "^5.7.1", "postcss-rtlcss": "^5.7.1",
"vitepress": "workspace:*", "vitepress": "workspace:*",
"vitepress-plugin-group-icons": "^1.6.3", "vitepress-plugin-group-icons": "^1.6.5",
"vitepress-plugin-llms": "^1.7.3" "vitepress-plugin-llms": "^1.9.3"
} }
} }

@ -153,17 +153,17 @@ Não ative opções como _Auto Minify_ para código HTML. Isso removerá coment
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # Não necessário se lastUpdated não estiver habilitado fetch-depth: 0 # Não necessário se lastUpdated não estiver habilitado
# - uses: pnpm/action-setup@v3 # Descomente isso se estiver usando pnpm # - uses: pnpm/action-setup@v4 # Descomente isso se estiver usando pnpm
# with: # with:
# version: 9 # version: 9
# - uses: oven-sh/setup-bun@v1 # Descomente isso se estiver usando Bun # - uses: oven-sh/setup-bun@v1 # Descomente isso se estiver usando Bun
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: npm # ou pnpm / yarn cache: npm # ou pnpm / yarn
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4

@ -843,7 +843,7 @@ Observe que isso não gera erros se o arquivo não estiver presente. Portanto, a
Isso é atualmente opcional. Para ativá-lo, você precisa instalar `markdown-it-mathjax3` e definir `markdown.math` como `true` no seu arquivo de configuração: Isso é atualmente opcional. Para ativá-lo, você precisa instalar `markdown-it-mathjax3` e definir `markdown.math` como `true` no seu arquivo de configuração:
```sh ```sh
npm add -D markdown-it-mathjax3 npm add -D markdown-it-mathjax3@^4
``` ```
```ts [.vitepress/config.ts] ```ts [.vitepress/config.ts]

@ -12,7 +12,7 @@ Quer apenas experimentar? Pule para o [Início Rápido](./getting-started).
- **Documentação** - **Documentação**
VitePress vem com um tema padrão projetado para documentação técnica. Ele alimenta esta página que você está lendo agora, juntamente com a documentação [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 [muitos outros](https://github.com/search?q=/"vitepress":+/+language:json&type=code). VitePress vem com um tema padrão projetado para documentação técnica. Ele alimenta esta página que você está lendo agora, juntamente com a documentação [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 [muitos outros](https://github.com/search?q=/%22vitepress%22:+/+path:/(?:package%7Cdeno)%5C.jsonc?$/+NOT+is:fork+NOT+is:archived&type=code).
A [documentação oficial Vue.js](https://vuejs.org/) também é baseada em VitePress, mas usa um tema personalizado compartilhado entre várias traduções. A [documentação oficial Vue.js](https://vuejs.org/) também é baseada em VitePress, mas usa um tema personalizado compartilhado entre várias traduções.

@ -153,17 +153,17 @@ Cache-Control: max-age=31536000,immutable
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # Не требуется, если функция lastUpdated не включена fetch-depth: 0 # Не требуется, если функция lastUpdated не включена
# - uses: pnpm/action-setup@v3 # Раскомментируйте, если вы используете pnpm # - uses: pnpm/action-setup@v4 # Раскомментируйте, если вы используете pnpm
# with: # with:
# version: 9 # version: 9
# - uses: oven-sh/setup-bun@v1 # Раскомментируйте, если вы используете Bun # - uses: oven-sh/setup-bun@v1 # Раскомментируйте, если вы используете Bun
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: npm # или pnpm / yarn cache: npm # или pnpm / yarn
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4

@ -951,7 +951,7 @@ export default config
В настоящее время эта фича предоставляется по желанию. Чтобы включить её, вам нужно установить `markdown-it-mathjax3` и установить значение `true` для опции `markdown.math` в вашем файле конфигурации: В настоящее время эта фича предоставляется по желанию. Чтобы включить её, вам нужно установить `markdown-it-mathjax3` и установить значение `true` для опции `markdown.math` в вашем файле конфигурации:
```sh ```sh
npm add -D markdown-it-mathjax3 npm add -D markdown-it-mathjax3@^4
``` ```
```ts [.vitepress/config.ts] ```ts [.vitepress/config.ts]

@ -1,6 +1,6 @@
# Что такое VitePress? {#what-is-vitepress} # Что такое VitePress? {#what-is-vitepress}
VitePress — это [Генератор статических сайтов](https://en.wikipedia.org/wiki/Static_site_generator) (ГСС), предназначенный для быстрого создания сайтов, ориентированных на контент. В двух словах, VitePress берёт ваш исходный контент, написанный на [Markdown](https://ru.wikipedia.org/wiki/Markdown), применяет к нему тему и генерирует статические HTML-страницы, которые можно легко развернуть в любом месте. VitePress — это [Генератор статических сайтов](https://ru.wikipedia.org/wiki/%D0%93%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D1%8B_%D1%81%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2) (ГСС), предназначенный для быстрого создания сайтов, ориентированных на контент. В двух словах, VitePress берёт ваш исходный контент, написанный на [Markdown](https://ru.wikipedia.org/wiki/Markdown), применяет к нему тему и генерирует статические HTML-страницы, которые можно легко развернуть в любом месте.
<div class="tip custom-block" style="padding-top: 8px"> <div class="tip custom-block" style="padding-top: 8px">
@ -12,13 +12,13 @@ VitePress — это [Генератор статических сайтов](ht
- **Документация** - **Документация**
VitePress поставляется с темой по умолчанию, предназначенной для технической документации. Она содержит эту страницу, которую вы сейчас читаете, а также документацию по [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/) и [многое другое](https://github.com/search?q=/"vitepress":+/+language:json&type=code). VitePress поставляется с темой по умолчанию, предназначенной для технической документации. Именно она обеспечивает работу этой страницы, которую вы сейчас читаете, а также документации для [Vite](https://vite-docs.ru/), [Rollup](https://rollupjs.org/), [Pinia](https://pinia-ru.netlify.app), [VueUse](https://vueuse.org/), [Vitest](https://vitest.dev/), [D3](https://d3js.org/), [UnoCSS](https://unocss.dev/), [Iconify](https://iconify.design/) и [многих других](https://github.com/search?q=/%22vitepress%22:+/+path:/(?:package%7Cdeno)%5C.jsonc?$/+NOT+is:fork+NOT+is:archived&type=code).
[Официальная документация Vue.js](https://vuejs.org/) также основана на VitePress, но использует пользовательскую тему, разделяемую между несколькими переводами. [Официальная документация Vue.js](https://vuejs.org/) также основана на VitePress, но использует кастомную тему, общую для нескольких переводов.
- **Блоги, портфолио и маркетинговые сайты** - **Блоги, портфолио и маркетинговые сайты**
VitePress поддерживает [полностью кастомизированные темы](./custom-theme), при этом разработчики могут использовать стандартное приложение Vite + Vue. То, что он построен на базе Vite, также означает, что вы можете напрямую использовать плагины Vite из его богатой экосистемы. Кроме того, VitePress предоставляет гибкие API для [загрузки данных](./data-loading) (локальной или удаленной) и [динамической генерации маршрутов](./routing#dynamic-routes). С его помощью можно построить практически всё, что угодно, если данные могут быть определены во время сборки. VitePress поддерживает [полностью кастомизированные темы](./custom-theme), при этом разработчики могут использовать стандартное приложение Vite + Vue. То, что он построен на базе Vite, также означает, что вы можете напрямую использовать плагины Vite из его богатой экосистемы. Кроме того, VitePress предоставляет гибкие API для [загрузки данных](./data-loading) (локальной или удалённой) и [динамической генерации маршрутов](./routing#dynamic-routes). С его помощью можно построить практически всё, что угодно, если данные могут быть определены во время сборки.
Официальный [блог Vue.js](https://blog.vuejs.org/) — это простой блог, который генерирует свою индексную страницу на основе локального контента. Официальный [блог Vue.js](https://blog.vuejs.org/) — это простой блог, который генерирует свою индексную страницу на основе локального контента.
@ -50,8 +50,8 @@ VitePress стремится обеспечить отличные возмож
## Что насчёт VuePress? {#what-about-vuepress} ## Что насчёт VuePress? {#what-about-vuepress}
VitePress — это духовный преемник VuePress 1. Оригинальный VuePress 1 был основан на Vue 2 и webpack. С Vue 3 и Vite под капотом VitePress обеспечивает значительно лучший опыт разработки (DX), лучшую производительность в продакшене, более отточенную стандартную тему и более гибкий API для кастомизации. VitePress — это духовный наследник VuePress 1. Оригинальный VuePress 1 основывался на Vue 2 и webpack. Благодаря Vue 3 и Vite под капотом, VitePress обеспечивает значительно лучший опыт разработки (DX), более высокую производительность в продакшене, более отполированную тему по умолчанию и более гибкий API для кастомизации.
Различия в API между VitePress и VuePress 1 в основном касаются тем и кастомизации. Если вы используете VuePress 1 со стандартной темой, миграция на VitePress должна быть относительно простой. Различия в API между VitePress и VuePress 1 в основном касаются тем и настройки. Если вы используете VuePress 1 с темой по умолчанию, миграция на VitePress должна пройти относительно просто.
Поддержка двух генераторов статических сайтов (SSG) параллельно не является устойчивой, поэтому команда Vue решила сосредоточиться на VitePress как на основном рекомендуемом SSG в долгосрочной перспективе. Теперь VuePress 1 признан устаревшим, а VuePress 2 передан команде сообщества VuePress для дальнейшей разработки и поддержки. Поддерживать два SSG параллельно нецелесообразно, поэтому команда Vue решила сосредоточиться на VitePress как на основном рекомендуемом SSG в долгосрочной перспективе. Теперь VuePress 1 объявлен устаревшим, а VuePress 2 передан команде сообщества VuePress для дальнейшей разработки и поддержки.

@ -11,7 +11,7 @@ VitePress отображает время «последнего обновле
```yaml{4} ```yaml{4}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
``` ```

@ -153,17 +153,17 @@ Cache-Control: max-age=31536000,immutable
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # 如果未启用 lastUpdated则不需要 fetch-depth: 0 # 如果未启用 lastUpdated则不需要
# - uses: pnpm/action-setup@v3 # 如果使用 pnpm请取消此区域注释 # - uses: pnpm/action-setup@v4 # 如果使用 pnpm请取消此区域注释
# with: # with:
# version: 9 # version: 9
# - uses: oven-sh/setup-bun@v1 # 如果使用 Bun请取消注释 # - uses: oven-sh/setup-bun@v1 # 如果使用 Bun请取消注释
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 24
cache: npm # 或 pnpm / yarn cache: npm # 或 pnpm / yarn
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v4
@ -308,20 +308,20 @@ server {
index index.html; index index.html;
location / { location / {
# content location # 内容位置
root /app; root /app;
# exact matches -> reverse clean urls -> folders -> not found # 完全匹配 -> 反向清理 url -> 文件夹 -> 没有发现
try_files $uri $uri.html $uri/ =404; try_files $uri $uri.html $uri/ =404;
# non existent pages # 不存在的页面
error_page 404 /404.html; error_page 404 /404.html;
# a folder without index.html raises 403 in this setup # 在此设置中,如果文件夹没有 index.html就会引发 403 错误
error_page 403 /404.html; error_page 403 /404.html;
# adjust caching headers # 调整缓存标头
# files in the assets folder have hashes filenames # assets 文件夹中的文件都有哈希文件名
location ~* ^/assets/ { location ~* ^/assets/ {
expires 1y; expires 1y;
add_header Cache-Control "public, immutable"; add_header Cache-Control "public, immutable";

@ -55,8 +55,8 @@ export default DefaultTheme
```css ```css
/* .vitepress/theme/my-fonts.css */ /* .vitepress/theme/my-fonts.css */
:root { :root {
--vp-font-family-base: /* normal text font */ --vp-font-family-base: /* 普通文本字体 */
--vp-font-family-mono: /* code font */ --vp-font-family-mono: /* 代码字体 */
} }
``` ```

@ -843,7 +843,7 @@ Can be created using `.foorc.json`.
现在这是可选的。要启用它,需要安装 `markdown-it-mathjax3`,在配置文件中设置`markdown.math` 为 `true` 现在这是可选的。要启用它,需要安装 `markdown-it-mathjax3`,在配置文件中设置`markdown.math` 为 `true`
```sh ```sh
npm add -D markdown-it-mathjax3 npm add -D markdown-it-mathjax3@^4
``` ```
```ts [.vitepress/config.ts] ```ts [.vitepress/config.ts]

@ -12,7 +12,7 @@ VitePress 是一个[静态站点生成器](https://en.wikipedia.org/wiki/Static_
- **文档** - **文档**
VitePress 附带一个专为技术文档设计的默认主题。你现在正在阅读的这个页面以及 [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/) [](https://github.com/search?q=/"vitepress":+/+language:json&type=code)文档都是基于这个主题的。 VitePress 附带一个专为技术文档设计的默认主题。你现在正在阅读的这个页面以及 [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/) [](https://github.com/search?q=/%22vitepress%22:+/+path:/(?:package%7Cdeno)%5C.jsonc?$/+NOT+is:fork+NOT+is:archived&type=code)文档都是基于这个主题的。
[Vue.js 官方文档](https://cn.vuejs.org/)也是基于 VitePress 的。但是为了可以在不同的翻译文档之间切换,它自定义了自己的主题。 [Vue.js 官方文档](https://cn.vuejs.org/)也是基于 VitePress 的。但是为了可以在不同的翻译文档之间切换,它自定义了自己的主题。

@ -11,7 +11,7 @@ VitePress 通过每个文件最近一次 Git 提交的时间戳显示"最后更
```yaml{4} ```yaml{4}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
``` ```

@ -1,5 +1,5 @@
[build.environment] [build.environment]
NODE_VERSION = "22" NODE_VERSION = "24"
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1" PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1"
[build] [build]

@ -1,6 +1,6 @@
{ {
"name": "vitepress", "name": "vitepress",
"version": "2.0.0-alpha.12", "version": "2.0.0-alpha.15",
"description": "Vite & Vue powered static site generator", "description": "Vite & Vue powered static site generator",
"keywords": [ "keywords": [
"vite", "vite",
@ -95,28 +95,28 @@
"*": "prettier --experimental-cli --ignore-unknown --write" "*": "prettier --experimental-cli --ignore-unknown --write"
}, },
"dependencies": { "dependencies": {
"@docsearch/css": "^4.0.0-beta.8", "@docsearch/css": "^4.3.2",
"@docsearch/js": "^4.0.0-beta.8", "@docsearch/js": "^4.3.2",
"@iconify-json/simple-icons": "^1.2.49", "@iconify-json/simple-icons": "^1.2.59",
"@shikijs/core": "^3.12.0", "@shikijs/core": "^3.15.0",
"@shikijs/transformers": "^3.12.0", "@shikijs/transformers": "^3.15.0",
"@shikijs/types": "^3.12.0", "@shikijs/types": "^3.15.0",
"@types/markdown-it": "^14.1.2", "@types/markdown-it": "^14.1.2",
"@vitejs/plugin-vue": "^6.0.1", "@vitejs/plugin-vue": "^6.0.1",
"@vue/devtools-api": "^8.0.1", "@vue/devtools-api": "^8.0.5",
"@vue/shared": "^3.5.20", "@vue/shared": "^3.5.24",
"@vueuse/core": "^13.8.0", "@vueuse/core": "^14.0.0",
"@vueuse/integrations": "^13.8.0", "@vueuse/integrations": "^14.0.0",
"focus-trap": "^7.6.5", "focus-trap": "^7.6.6",
"mark.js": "8.11.1", "mark.js": "8.11.1",
"minisearch": "^7.1.2", "minisearch": "^7.2.0",
"shiki": "^3.12.0", "shiki": "^3.15.0",
"vite": "^7.1.3", "vite": "^7.2.2",
"vue": "^3.5.20" "vue": "^3.5.24"
}, },
"devDependencies": { "devDependencies": {
"@clack/prompts": "^1.0.0-alpha.4", "@clack/prompts": "^1.0.0-alpha.6",
"@iconify/utils": "^3.0.1", "@iconify/utils": "^3.0.2",
"@mdit-vue/plugin-component": "^3.0.2", "@mdit-vue/plugin-component": "^3.0.2",
"@mdit-vue/plugin-frontmatter": "^3.0.2", "@mdit-vue/plugin-frontmatter": "^3.0.2",
"@mdit-vue/plugin-headers": "^3.0.2", "@mdit-vue/plugin-headers": "^3.0.2",
@ -125,13 +125,12 @@
"@mdit-vue/plugin-toc": "^3.0.2", "@mdit-vue/plugin-toc": "^3.0.2",
"@mdit-vue/shared": "^3.0.2", "@mdit-vue/shared": "^3.0.2",
"@polka/compression": "^1.0.0-next.28", "@polka/compression": "^1.0.0-next.28",
"@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-alias": "^6.0.0",
"@rollup/plugin-commonjs": "^28.0.6", "@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-json": "^6.1.0", "@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-replace": "^6.0.2", "@rollup/plugin-replace": "^6.0.3",
"@types/cross-spawn": "^6.0.6", "@types/cross-spawn": "^6.0.6",
"@types/debug": "^4.1.12",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/lodash.template": "^4.5.3", "@types/lodash.template": "^4.5.3",
"@types/mark.js": "^8.11.12", "@types/mark.js": "^8.11.12",
@ -139,62 +138,63 @@
"@types/markdown-it-container": "^2.0.10", "@types/markdown-it-container": "^2.0.10",
"@types/markdown-it-emoji": "^3.0.1", "@types/markdown-it-emoji": "^3.0.1",
"@types/minimist": "^1.2.5", "@types/minimist": "^1.2.5",
"@types/node": "^24.3.0", "@types/node": "^24.10.1",
"@types/picomatch": "^4.0.2", "@types/picomatch": "^4.0.2",
"@types/prompts": "^2.4.9", "@types/prompts": "^2.4.9",
"chokidar": "^4.0.3", "chokidar": "^4.0.3",
"conventional-changelog-cli": "^5.0.0", "conventional-changelog": "^7.1.1",
"conventional-changelog-angular": "^8.1.0",
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.6",
"debug": "^4.4.1", "esbuild": "^0.25.12",
"esbuild": "^0.25.9",
"execa": "^9.6.0", "execa": "^9.6.0",
"fs-extra": "^11.3.1", "fs-extra": "^11.3.2",
"get-port": "^7.1.0", "get-port": "^7.1.0",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"lint-staged": "^16.1.5", "lint-staged": "^16.2.6",
"lodash.template": "^4.5.0", "lodash.template": "^4.5.0",
"lru-cache": "^11.1.0", "lru-cache": "^11.2.2",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"markdown-it-anchor": "^9.2.0", "markdown-it-anchor": "^9.2.0",
"markdown-it-async": "^2.2.0", "markdown-it-async": "^2.2.0",
"markdown-it-attrs": "^4.3.1", "markdown-it-attrs": "^4.3.1",
"markdown-it-cjk-friendly": "^1.2.0", "markdown-it-cjk-friendly": "^1.3.2",
"markdown-it-container": "^4.0.0", "markdown-it-container": "^4.0.0",
"markdown-it-emoji": "^3.0.0", "markdown-it-emoji": "^3.0.0",
"markdown-it-mathjax3": "^4.3.2", "markdown-it-mathjax3": "^4.3.2",
"minimist": "^1.2.8", "minimist": "^1.2.8",
"nanoid": "^5.1.5", "nanoid": "^5.1.6",
"ora": "^8.2.0", "obug": "^2.0.0",
"oxc-minify": "^0.82.3", "ora": "^9.0.0",
"p-map": "^7.0.3", "oxc-minify": "^0.98.0",
"p-map": "^7.0.4",
"package-directory": "^8.1.0", "package-directory": "^8.1.0",
"path-to-regexp": "^6.3.0", "path-to-regexp": "^6.3.0",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"picomatch": "^4.0.3", "picomatch": "^4.0.3",
"playwright-chromium": "^1.55.0", "playwright-chromium": "^1.56.1",
"polka": "^1.0.0-next.28", "polka": "^1.0.0-next.28",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"postcss-selector-parser": "^7.1.0", "postcss-selector-parser": "^7.1.0",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prompts": "^2.4.2", "prompts": "^2.4.2",
"punycode": "^2.3.1", "punycode": "^2.3.1",
"rimraf": "^6.0.1", "rimraf": "^6.1.0",
"rollup": "^4.49.0", "rollup": "^4.53.2",
"rollup-plugin-dts": "6.1.1", "rollup-plugin-dts": "6.1.1",
"rollup-plugin-esbuild": "^6.2.1", "rollup-plugin-esbuild": "^6.2.1",
"semver": "^7.7.2", "semver": "^7.7.3",
"simple-git-hooks": "^2.13.1", "simple-git-hooks": "^2.13.1",
"sirv": "^3.0.1", "sirv": "^3.0.2",
"sitemap": "^8.0.0", "sitemap": "^9.0.0",
"tinyglobby": "^0.2.14", "tinyglobby": "^0.2.15",
"typescript": "^5.9.2", "typescript": "^5.9.3",
"vitest": "4.0.0-beta.4", "vitest": "4.0.0-beta.4",
"vue-tsc": "^3.0.6", "vue-tsc": "^3.1.4",
"wait-on": "^8.0.4" "wait-on": "^9.0.3"
}, },
"peerDependencies": { "peerDependencies": {
"markdown-it-mathjax3": "^4", "markdown-it-mathjax3": "^4",
"oxc-minify": "^0.82.3", "oxc-minify": "*",
"postcss": "^8" "postcss": "^8"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
@ -208,5 +208,5 @@
"optional": true "optional": true
} }
}, },
"packageManager": "pnpm@10.14.0" "packageManager": "pnpm@10.22.0"
} }

@ -0,0 +1,23 @@
diff --git a/index.d.ts b/index.d.ts
index 41d4a858c6ece5a61a2088733cf8b333b45603d8..2cb19bcd8fc76ae82ebe87af742916e17f49334b 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -1,3 +1,15 @@
-import MarkdownIt = require("markdown-it");
-declare function attrs(md: MarkdownIt): void;
-export = attrs;
+import type MarkdownIt from 'markdown-it'
+
+export interface MarkdownItAttrsOptions {
+ /** left delimiter, default is `{`(left curly bracket) */
+ leftDelimiter?: string
+ /** right delimiter, default is `}`(right curly bracket) */
+ rightDelimiter?: string
+ /** rule of allowed attribute, empty means no limit */
+ allowedAttributes?: (string | RegExp)[]
+}
+
+export default function attrsPlugin(
+ md: MarkdownIt,
+ options?: MarkdownItAttrsOptions
+): void

@ -0,0 +1,14 @@
diff --git a/patterns.js b/patterns.js
index e56a3b785df29e726194f2b30e86bb19a78ef902..e1f6211b92cbb2bbbe2a3c317dd9e5f1f41ab8d1 100644
--- a/patterns.js
+++ b/patterns.js
@@ -28,7 +28,8 @@ module.exports = options => {
{
shift: 0,
block: true,
- info: utils.hasDelimiters('end', options)
+ info: utils.hasDelimiters('end', options),
+ markup: (str) => !/^[`~]{3,}$/.test(str)
}
],
transform: (tokens, i) => {

File diff suppressed because it is too large Load Diff

@ -2,6 +2,8 @@ packages:
- docs - docs
- __tests__/* - __tests__/*
autoInstallPeers: false
onlyBuiltDependencies: onlyBuiltDependencies:
- esbuild - esbuild
- playwright-chromium - playwright-chromium
@ -12,9 +14,10 @@ overrides:
vite: npm:rolldown-vite@latest vite: npm:rolldown-vite@latest
patchedDependencies: patchedDependencies:
'@types/markdown-it-attrs': patches/@types__markdown-it-attrs.patch
'@types/mdurl@2.0.0': patches/@types__mdurl@2.0.0.patch '@types/mdurl@2.0.0': patches/@types__mdurl@2.0.0.patch
markdown-it-anchor@9.2.0: patches/markdown-it-anchor@9.2.0.patch markdown-it-anchor@9.2.0: patches/markdown-it-anchor@9.2.0.patch
markdown-it-attrs@4.3.1: patches/markdown-it-attrs@4.3.1.patch
vitepress-plugin-group-icons: patches/vitepress-plugin-group-icons.patch vitepress-plugin-group-icons: patches/vitepress-plugin-group-icons.patch
autoInstallPeers: false
shellEmulator: true shellEmulator: true

@ -7,7 +7,7 @@ export function useCodeGroups() {
Array.from(el.children).forEach((child) => { Array.from(el.children).forEach((child) => {
child.classList.remove('active') child.classList.remove('active')
}) })
el.children[0].classList.add('active') activate(el.children[0])
}) })
}) })
} }
@ -36,7 +36,7 @@ export function useCodeGroups() {
if (!next || current === next) return if (!next || current === next) return
current.classList.remove('active') current.classList.remove('active')
next.classList.add('active') activate(next)
const label = group?.querySelector(`label[for="${el.id}"]`) const label = group?.querySelector(`label[for="${el.id}"]`)
label?.scrollIntoView({ block: 'nearest' }) label?.scrollIntoView({ block: 'nearest' })
@ -44,3 +44,10 @@ export function useCodeGroups() {
}) })
} }
} }
function activate(el: Element): void {
el.classList.add('active')
window.dispatchEvent(
new CustomEvent('vitepress:codeGroupTabActivate', { detail: el })
)
}

@ -1,6 +1,6 @@
import { inBrowser } from 'vitepress' import { inBrowser } from 'vitepress'
import { isShell } from '../../shared'
const shellRE = /language-(shellscript|shell|bash|sh|zsh)/
const ignoredNodes = ['.vp-copy-ignore', '.diff.remove'].join(', ') const ignoredNodes = ['.vp-copy-ignore', '.diff.remove'].join(', ')
export function useCopyCode() { export function useCopyCode() {
@ -15,8 +15,6 @@ export function useCopyCode() {
return return
} }
const isShell = shellRE.test(parent.className)
// Clone the node and remove the ignored nodes // Clone the node and remove the ignored nodes
const clone = sibling.cloneNode(true) as HTMLElement const clone = sibling.cloneNode(true) as HTMLElement
clone.querySelectorAll(ignoredNodes).forEach((node) => node.remove()) clone.querySelectorAll(ignoredNodes).forEach((node) => node.remove())
@ -26,7 +24,10 @@ export function useCopyCode() {
let text = clone.textContent || '' let text = clone.textContent || ''
if (isShell) { // NOTE: Any changes to this the code here may also need to update
// `transformerDisableShellSymbolSelect` in `src/node/markdown/plugins/highlight.ts`
const lang = /language-(\w+)/.exec(parent.className)?.[1] || ''
if (isShell(lang)) {
text = text.replace(/^ *(\$|>) /gm, '').trim() text = text.replace(/^ *(\$|>) /gm, '').trim()
} }

@ -131,7 +131,9 @@ function newRouter(): Router {
} }
if (import.meta.env.DEV) { if (import.meta.env.DEV) {
pageModule = import(/*@vite-ignore*/ pageFilePath).catch(() => { pageModule = import(/*@vite-ignore*/ pageFilePath).catch((e) => {
// page load could fail for other reasons, don't swallow
console.error(e)
// try with/without trailing slash // try with/without trailing slash
// in prod this is handled in src/client/app/utils.ts#pathToFile // in prod this is handled in src/client/app/utils.ts#pathToFile
const url = new URL(pageFilePath!, 'http://a.com') const url = new URL(pageFilePath!, 'http://a.com')

@ -39,15 +39,14 @@ withDefaults(defineProps<Props>(), {
display: none; display: none;
} }
.vp-doc h1 > .VPBadge { .vp-doc h1 > .VPBadge,
margin-top: 4px; .vp-doc h2 > .VPBadge {
vertical-align: top; margin: 0 0 0 2px;
vertical-align: middle;
} }
.vp-doc h2 > .VPBadge { .vp-doc h2 > .VPBadge {
margin-top: 3px;
padding: 0 8px; padding: 0 8px;
vertical-align: top;
} }
.vp-doc h3 > .VPBadge { .vp-doc h3 > .VPBadge {

@ -88,8 +88,8 @@ const { isHome, hasSidebar } = useLayout()
@media (min-width: 1440px) { @media (min-width: 1440px) {
.VPContent.has-sidebar { .VPContent.has-sidebar {
padding-right: calc((100vw - var(--vp-layout-max-width)) / 2); padding-right: calc((100% - var(--vp-layout-max-width)) / 2);
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)); padding-left: calc((100% - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
} }
} }
</style> </style>

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme' import type { DefaultTheme } from 'vitepress/theme'
import { inject } from 'vue' import { computed, inject } from 'vue'
import { layoutInfoInjectionKey } from '../composables/layout' import { layoutInfoInjectionKey } from '../composables/layout'
import VPButton from './VPButton.vue' import VPButton from './VPButton.vue'
import VPImage from './VPImage.vue' import VPImage from './VPImage.vue'
@ -21,7 +21,10 @@ defineProps<{
actions?: HeroAction[] actions?: HeroAction[]
}>() }>()
const { heroImageSlotExists } = inject(layoutInfoInjectionKey)! const { heroImageSlotExists } = inject(
layoutInfoInjectionKey,
{ heroImageSlotExists: computed(() => false) }
)
</script> </script>
<template> <template>

@ -98,12 +98,6 @@ const classes = computed(() => {
} }
} }
@media (min-width: 1440px) {
.VPLocalNav.has-sidebar {
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
}
}
.container { .container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

@ -171,20 +171,14 @@ watchPostEffect(() => {
position: relative; position: relative;
z-index: 1; z-index: 1;
padding-left: var(--vp-sidebar-width); padding-left: var(--vp-sidebar-width);
}
.VPNavBar.has-sidebar .content-body {
padding-right: 32px; padding-right: 32px;
} }
} }
@media (min-width: 1440px) { @media (min-width: 1440px) {
.VPNavBar.has-sidebar .content { .VPNavBar.has-sidebar .content {
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)); padding-left: calc((100% - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
} padding-right: calc((100% - var(--vp-layout-max-width)) / 2 + 32px);
.VPNavBar.has-sidebar .content-body {
padding-right: calc((100vw - var(--vp-layout-max-width)) / 2 + 32px);
} }
} }
@ -205,6 +199,11 @@ watchPostEffect(() => {
.VPNavBar:not(.has-sidebar):not(.home.top) .content-body { .VPNavBar:not(.has-sidebar):not(.home.top) .content-body {
background-color: transparent; background-color: transparent;
} }
.content-body {
margin-right: -100vw;
padding-right: 100vw;
}
} }
@media (max-width: 767px) { @media (max-width: 767px) {
@ -252,7 +251,7 @@ watchPostEffect(() => {
@media (min-width: 1440px) { @media (min-width: 1440px) {
.VPNavBar.has-sidebar .divider { .VPNavBar.has-sidebar .divider {
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)); padding-left: calc((100% - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
} }
} }

@ -2,6 +2,7 @@
import type { DefaultTheme } from 'vitepress/theme' import type { DefaultTheme } from 'vitepress/theme'
import { computed, inject } from 'vue' import { computed, inject } from 'vue'
import { useData } from '../composables/data' import { useData } from '../composables/data'
import { isActive } from '../../shared'
import { navInjectionKey } from '../composables/nav' import { navInjectionKey } from '../composables/nav'
import VPLink from './VPLink.vue' import VPLink from './VPLink.vue'
@ -17,12 +18,20 @@ const href = computed(() =>
: props.item.link : props.item.link
) )
const isActiveLink = computed(() =>
isActive(
page.value.relativePath,
props.item.activeMatch || href.value,
!!props.item.activeMatch
)
)
const { closeScreen } = inject(navInjectionKey)! const { closeScreen } = inject(navInjectionKey)!
</script> </script>
<template> <template>
<VPLink <VPLink
class="VPNavScreenMenuGroupLink" :class="{ VPNavScreenMenuGroupLink: true, active: isActiveLink }"
:href :href
:target="item.target" :target="item.target"
:rel="item.rel" :rel="item.rel"
@ -47,4 +56,8 @@ const { closeScreen } = inject(navInjectionKey)!
.VPNavScreenMenuGroupLink:hover { .VPNavScreenMenuGroupLink:hover {
color: var(--vp-c-brand-1); color: var(--vp-c-brand-1);
} }
.VPNavScreenMenuGroupLink.active {
color: var(--vp-c-brand-1);
}
</style> </style>

@ -2,6 +2,7 @@
import type { DefaultTheme } from 'vitepress/theme' import type { DefaultTheme } from 'vitepress/theme'
import { computed, inject } from 'vue' import { computed, inject } from 'vue'
import { useData } from '../composables/data' import { useData } from '../composables/data'
import { isActive } from '../../shared'
import { navInjectionKey } from '../composables/nav' import { navInjectionKey } from '../composables/nav'
import VPLink from './VPLink.vue' import VPLink from './VPLink.vue'
@ -17,12 +18,20 @@ const href = computed(() =>
: props.item.link : props.item.link
) )
const isActiveLink = computed(() =>
isActive(
page.value.relativePath,
props.item.activeMatch || href.value,
!!props.item.activeMatch
)
)
const { closeScreen } = inject(navInjectionKey)! const { closeScreen } = inject(navInjectionKey)!
</script> </script>
<template> <template>
<VPLink <VPLink
class="VPNavScreenMenuLink" :class="{ VPNavScreenMenuLink: true, active: isActiveLink }"
:href :href
:target="item.target" :target="item.target"
:rel="item.rel" :rel="item.rel"
@ -50,4 +59,8 @@ const { closeScreen } = inject(navInjectionKey)!
.VPNavScreenMenuLink:hover { .VPNavScreenMenuLink:hover {
color: var(--vp-c-brand-1); color: var(--vp-c-brand-1);
} }
.VPNavScreenMenuLink.active {
color: var(--vp-c-brand-1);
}
</style> </style>

@ -111,8 +111,8 @@ watch(
@media (min-width: 1440px) { @media (min-width: 1440px) {
.VPSidebar { .VPSidebar {
padding-left: max(32px, calc((100vw - (var(--vp-layout-max-width) - 64px)) / 2)); padding-left: max(32px, calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));
width: calc((100vw - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px); width: calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px);
} }
} }

@ -1,3 +1,4 @@
@layer __vitepress_base {
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
*, *,
::before, ::before,
@ -249,3 +250,4 @@ mjx-container > svg {
display: inline-block; display: inline-block;
margin: auto; margin: auto;
} }
}

@ -235,11 +235,21 @@
background-color: transparent; background-color: transparent;
} }
.vp-doc .custom-block .vp-code-group,
.vp-doc .custom-block [class*='vp-code-block'] {
margin-top: 8px;
}
.vp-doc .custom-block .vp-code-group .tabs { .vp-doc .custom-block .vp-code-group .tabs {
margin: 0; margin: 0;
border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0;
} }
.vp-doc .custom-block .vp-code-group div[class*='language-'],
.vp-doc .custom-block [class*='vp-code-block'] div[class*='language-'] {
margin-top: 0 !important;
}
/** /**
* Code * Code
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */

@ -5,7 +5,7 @@
"outDir": "../../dist/client", "outDir": "../../dist/client",
"declaration": true, "declaration": true,
"declarationDir": "../../dist/client-types", "declarationDir": "../../dist/client-types",
"types": ["vite/client", "@types/node"], "types": ["../../client.d.ts", "@types/node"],
"paths": { "paths": {
"vitepress": ["index.ts"], "vitepress": ["index.ts"],
"vitepress/theme": ["../../theme.d.ts"] "vitepress/theme": ["../../theme.d.ts"]

@ -268,7 +268,7 @@ async function minifyScript(code: string, filename: string): Promise<string> {
// @ts-ignore use oxc-minify when rolldown-vite is used // @ts-ignore use oxc-minify when rolldown-vite is used
if (vite.rolldownVersion) { if (vite.rolldownVersion) {
const oxcMinify = await import('oxc-minify') const oxcMinify = await import('oxc-minify')
return oxcMinify.minify(filename, code).code.trim() return (await oxcMinify.minify(filename, code)).code.trim()
} }
return ( return (
await transformWithEsbuild(code, filename, { minify: true }) await transformWithEsbuild(code, filename, { minify: true })

@ -1,4 +1,4 @@
import _debug from 'debug' import { createDebug } from 'obug'
import fs from 'fs-extra' import fs from 'fs-extra'
import path from 'node:path' import path from 'node:path'
import c from 'picocolors' import c from 'picocolors'
@ -29,7 +29,7 @@ export { resolvePages } from './plugins/dynamicRoutesPlugin'
export { resolveSiteDataByRoute } from './shared' export { resolveSiteDataByRoute } from './shared'
export * from './siteConfig' export * from './siteConfig'
const debug = _debug('vitepress:config') const debug = createDebug('vitepress:config')
const resolve = (root: string, file: string) => const resolve = (root: string, file: string) =>
normalizePath(path.resolve(root, `.vitepress`, file)) normalizePath(path.resolve(root, `.vitepress`, file))

@ -20,8 +20,8 @@ import type {
ThemeRegistrationAny ThemeRegistrationAny
} from '@shikijs/types' } from '@shikijs/types'
import anchorPlugin from 'markdown-it-anchor' import anchorPlugin from 'markdown-it-anchor'
import { MarkdownItAsync, type Options } from 'markdown-it-async' import { MarkdownItAsync, type MarkdownItAsyncOptions } from 'markdown-it-async'
import attrsPlugin from 'markdown-it-attrs' import attrsPlugin, { type MarkdownItAttrsOptions } from 'markdown-it-attrs'
import mditCjkFriendly from 'markdown-it-cjk-friendly' import mditCjkFriendly from 'markdown-it-cjk-friendly'
import { full as emojiPlugin } from 'markdown-it-emoji' import { full as emojiPlugin } from 'markdown-it-emoji'
import type { BuiltinLanguage, BuiltinTheme, Highlighter } from 'shiki' import type { BuiltinLanguage, BuiltinTheme, Highlighter } from 'shiki'
@ -30,7 +30,6 @@ import type { Awaitable } from '../shared'
import { containerPlugin, type ContainerOptions } from './plugins/containers' import { containerPlugin, type ContainerOptions } from './plugins/containers'
import { gitHubAlertsPlugin } from './plugins/githubAlerts' import { gitHubAlertsPlugin } from './plugins/githubAlerts'
import { highlight as createHighlighter } from './plugins/highlight' import { highlight as createHighlighter } from './plugins/highlight'
import { highlightLinePlugin } from './plugins/highlightLines'
import { imagePlugin, type Options as ImageOptions } from './plugins/image' import { imagePlugin, type Options as ImageOptions } from './plugins/image'
import { lineNumberPlugin } from './plugins/lineNumbers' import { lineNumberPlugin } from './plugins/lineNumbers'
import { linkPlugin } from './plugins/link' import { linkPlugin } from './plugins/link'
@ -48,7 +47,7 @@ export type ThemeOptions =
dark: ThemeRegistrationAny | BuiltinTheme dark: ThemeRegistrationAny | BuiltinTheme
} }
export interface MarkdownOptions extends Options { export interface MarkdownOptions extends MarkdownItAsyncOptions {
/* ==================== General Options ==================== */ /* ==================== General Options ==================== */
/** /**
@ -153,12 +152,7 @@ export interface MarkdownOptions extends Options {
* Options for `markdown-it-attrs` * Options for `markdown-it-attrs`
* @see https://github.com/arve0/markdown-it-attrs * @see https://github.com/arve0/markdown-it-attrs
*/ */
attrs?: { attrs?: MarkdownItAttrsOptions & { disable?: boolean }
leftDelimiter?: string
rightDelimiter?: string
allowedAttributes?: Array<string | RegExp>
disable?: boolean
}
/** /**
* Options for `markdown-it-emoji` * Options for `markdown-it-emoji`
* @see https://github.com/markdown-it/markdown-it-emoji * @see https://github.com/markdown-it/markdown-it-emoji
@ -230,7 +224,10 @@ export interface MarkdownOptions extends Options {
export type MarkdownRenderer = MarkdownItAsync export type MarkdownRenderer = MarkdownItAsync
let md: MarkdownRenderer | undefined // highlight is marked as any to avoid type conflicts with plugins expecting
// regular markdown-it which has sync highlight function. Such plugins will fail
// if they access highlight directly but currently none of the ones we use do that.
let md: (MarkdownRenderer & { options: { highlight?: any } }) | undefined
let _disposeHighlighter: (() => void) | undefined let _disposeHighlighter: (() => void) | undefined
export function disposeMdItInstance() { export function disposeMdItInstance() {
@ -263,7 +260,7 @@ export async function createMarkdownRenderer(
md = new MarkdownItAsync({ html: true, linkify: true, highlight, ...options }) md = new MarkdownItAsync({ html: true, linkify: true, highlight, ...options })
md.linkify.set({ fuzzyLink: false }) md.linkify.set({ fuzzyLink: false })
md.use(restoreEntities) restoreEntities(md)
if (options.preConfig) { if (options.preConfig) {
await options.preConfig(md) await options.preConfig(md)
@ -272,22 +269,21 @@ export async function createMarkdownRenderer(
const slugify = options.anchor?.slugify ?? defaultSlugify const slugify = options.anchor?.slugify ?? defaultSlugify
// custom plugins // custom plugins
md.use(componentPlugin, { ...options.component }) componentPlugin(md, options.component)
.use(highlightLinePlugin) preWrapperPlugin(md, {
.use(preWrapperPlugin, {
codeCopyButtonTitle, codeCopyButtonTitle,
languageLabel: options.languageLabel languageLabel: options.languageLabel
}) })
.use(snippetPlugin, srcDir) snippetPlugin(md, srcDir)
.use(containerPlugin, options.container) containerPlugin(md, options.container)
.use(imagePlugin, options.image) imagePlugin(md, options.image)
.use( linkPlugin(
linkPlugin, md,
{ target: '_blank', rel: 'noreferrer', ...options.externalLinks }, { target: '_blank', rel: 'noreferrer', ...options.externalLinks },
base, base,
slugify slugify
) )
.use(lineNumberPlugin, options.lineNumbers) lineNumberPlugin(md, options.lineNumbers)
const tableOpen = md.renderer.rules.table_open const tableOpen = md.renderer.rules.table_open
md.renderer.rules.table_open = function (tokens, idx, options, env, self) { md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
@ -299,17 +295,17 @@ export async function createMarkdownRenderer(
} }
if (options.gfmAlerts !== false) { if (options.gfmAlerts !== false) {
md.use(gitHubAlertsPlugin, options.container) gitHubAlertsPlugin(md, options.container)
} }
// third party plugins // third party plugins
if (!options.attrs?.disable) { if (!options.attrs?.disable) {
md.use(attrsPlugin, options.attrs) attrsPlugin(md, options.attrs)
} }
md.use(emojiPlugin, { ...options.emoji }) emojiPlugin(md, options.emoji)
// mdit-vue plugins // mdit-vue plugins
md.use(anchorPlugin, { anchorPlugin(md, {
slugify, slugify,
getTokensText: (tokens) => { getTokensText: (tokens) => {
return tokens return tokens
@ -343,35 +339,33 @@ export async function createMarkdownRenderer(
state.tokens[idx + 1].children?.push(...linkTokens) state.tokens[idx + 1].children?.push(...linkTokens)
}, },
...options.anchor ...options.anchor
} as anchorPlugin.AnchorOptions).use(frontmatterPlugin, { })
...options.frontmatter
} as FrontmatterPluginOptions) frontmatterPlugin(md, options.frontmatter)
if (options.headers) { if (options.headers) {
md.use(headersPlugin, { headersPlugin(md, {
level: [2, 3, 4, 5, 6], level: [2, 3, 4, 5, 6],
slugify, slugify,
...(typeof options.headers === 'boolean' ? undefined : options.headers) ...(typeof options.headers === 'boolean' ? undefined : options.headers)
} as HeadersPluginOptions) })
} }
md.use(sfcPlugin, { sfcPlugin(md, options.sfc)
...options.sfc titlePlugin(md)
} as SfcPluginOptions) tocPlugin(md, {
.use(titlePlugin)
.use(tocPlugin, {
slugify, slugify,
...options.toc, ...options.toc,
format: (s) => { format: (s) => {
const title = s.replaceAll('&amp;', '&') // encoded twice because of restoreEntities const title = s.replaceAll('&amp;', '&') // encoded twice because of restoreEntities
return options.toc?.format?.(title) ?? title return options.toc?.format?.(title) ?? title
} }
} as TocPluginOptions) })
if (options.math) { if (options.math) {
try { try {
const mathPlugin = await import('markdown-it-mathjax3') const mathPlugin = await import('markdown-it-mathjax3')
md.use(mathPlugin.default ?? mathPlugin, { ;(mathPlugin.default ?? mathPlugin)(md, {
...(typeof options.math === 'boolean' ? {} : options.math) ...(typeof options.math === 'boolean' ? {} : options.math)
}) })
const origMathInline = md.renderer.rules.math_inline! const origMathInline = md.renderer.rules.math_inline!
@ -388,13 +382,13 @@ export async function createMarkdownRenderer(
} }
} catch (error) { } catch (error) {
throw new Error( throw new Error(
'You need to install `markdown-it-mathjax3` to use math support.' 'You need to install `markdown-it-mathjax3@^4` to use math support.'
) )
} }
} }
if (options.cjkFriendlyEmphasis !== false && options.cjkFriendly !== false) { if (options.cjkFriendlyEmphasis !== false && options.cjkFriendly !== false) {
md.use(mditCjkFriendly) mditCjkFriendly(md)
} }
// apply user config // apply user config

@ -1,50 +1,52 @@
import { import {
transformerCompactLineOptions, transformerMetaHighlight,
transformerNotationDiff, transformerNotationDiff,
transformerNotationErrorLevel, transformerNotationErrorLevel,
transformerNotationFocus, transformerNotationFocus,
transformerNotationHighlight, transformerNotationHighlight
type TransformerCompactLineOption
} from '@shikijs/transformers' } from '@shikijs/transformers'
import { customAlphabet } from 'nanoid' import { customAlphabet } from 'nanoid'
import c from 'picocolors' import c from 'picocolors'
import type { BundledLanguage, ShikiTransformer } from 'shiki' import type { BundledLanguage, ShikiTransformer } from 'shiki'
import { createHighlighter, guessEmbeddedLanguages, isSpecialLang } from 'shiki' import { createHighlighter, guessEmbeddedLanguages, isSpecialLang } from 'shiki'
import type { Logger } from 'vite' import type { Logger } from 'vite'
import { isShell } from '../../shared'
import type { MarkdownOptions, ThemeOptions } from '../markdown' import type { MarkdownOptions, ThemeOptions } from '../markdown'
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
/** /**
* 2 steps: * Prevents the leading '$' symbol etc from being selectable/copyable. Also
* normalizes its syntax so there's no leading spaces, and only a single
* trailing space.
* *
* 1. convert attrs into line numbers: * NOTE: Any changes to this function may also need to update
* {4,7-13,16,23-27,40} -> [4,7,8,9,10,11,12,13,16,23,24,25,26,27,40] * `src/client/app/composables/copyCode.ts`
* 2. convert line numbers into line options:
* [{ line: number, classes: string[] }]
*/ */
function attrsToLines(attrs: string): TransformerCompactLineOption[] { function transformerDisableShellSymbolSelect(): ShikiTransformer {
attrs = attrs.replace(/^(?:\[.*?\])?.*?([\d,-]+).*/, '$1').trim() return {
const result: number[] = [] name: 'vitepress:disable-shell-symbol-select',
if (!attrs) { tokens(tokensByLine) {
return [] if (!isShell(this.options.lang)) return
}
attrs for (const tokens of tokensByLine) {
.split(',') if (tokens.length < 2) continue
.map((v) => v.split('-').map((v) => parseInt(v, 10)))
.forEach(([start, end]) => { // The first token should only be a symbol token
if (start && end) { const firstTokenText = tokens[0].content.trim()
result.push( if (firstTokenText !== '$' && firstTokenText !== '>') continue
...Array.from({ length: end - start + 1 }, (_, i) => start + i)
) // The second token must have a leading space (separates the symbol)
} else { if (tokens[1].content[0] !== ' ') continue
result.push(start)
tokens[0].content = firstTokenText + ' '
tokens[0].htmlStyle ??= {}
tokens[0].htmlStyle['user-select'] = 'none'
tokens[0].htmlStyle['-webkit-user-select'] = 'none'
tokens[1].content = tokens[1].content.slice(1)
}
}
} }
})
return result.map((v) => ({
line: v,
classes: ['highlighted']
}))
} }
export async function highlight( export async function highlight(
@ -76,6 +78,7 @@ export async function highlight(
await options?.shikiSetup?.(highlighter) await options?.shikiSetup?.(highlighter)
const transformers: ShikiTransformer[] = [ const transformers: ShikiTransformer[] = [
transformerMetaHighlight(),
transformerNotationDiff(), transformerNotationDiff(),
transformerNotationFocus({ transformerNotationFocus({
classActiveLine: 'has-focus', classActiveLine: 'has-focus',
@ -83,6 +86,7 @@ export async function highlight(
}), }),
transformerNotationHighlight(), transformerNotationHighlight(),
transformerNotationErrorLevel(), transformerNotationErrorLevel(),
transformerDisableShellSymbolSelect(),
{ {
name: 'vitepress:add-dir', name: 'vitepress:add-dir',
pre(node) { pre(node) {
@ -91,20 +95,24 @@ export async function highlight(
} }
] ]
const vueRE = /-vue(?=:|$)/ // keep in sync with ./preWrapper.ts#extractLang
const lineNoStartRE = /=(\d*)/ const langRE = /^[a-zA-Z0-9-_]+/
const lineNoRE = /:(no-)?line-numbers(=\d*)?$/ const vueRE = /-vue$/
const mustacheRE = /\{\{.*?\}\}/g
return [ return [
async (str: string, lang: string, attrs: string) => { async (str, lang, attrs) => {
const vPre = vueRE.test(lang) ? '' : 'v-pre' const match = langRE.exec(lang)
lang = if (match) {
lang const orig = lang
.replace(lineNoStartRE, '') lang = match[0].toLowerCase()
.replace(lineNoRE, '') attrs = orig.slice(lang.length).replace(/(?<!=)\{/g, ' {') + ' ' + attrs
.replace(vueRE, '') attrs = attrs.trim().replace(/\s+/g, ' ')
.toLowerCase() || defaultLang }
lang ||= defaultLang
const vPre = !vueRE.test(lang)
if (!vPre) lang = lang.slice(0, -4)
try { try {
// https://github.com/shikijs/shiki/issues/952 // https://github.com/shikijs/shiki/issues/952
@ -123,12 +131,11 @@ export async function highlight(
lang = defaultLang lang = defaultLang
} }
const lineOptions = attrsToLines(attrs)
const mustaches = new Map<string, string>() const mustaches = new Map<string, string>()
const removeMustache = (s: string) => { const removeMustache = (s: string) => {
if (vPre) return s if (vPre) return s
return s.replace(mustacheRE, (match) => { return s.replace(/\{\{.*?\}\}/g, (match) => {
let marker = mustaches.get(match) let marker = mustaches.get(match)
if (!marker) { if (!marker) {
marker = nanoid() marker = nanoid()
@ -154,7 +161,6 @@ export async function highlight(
lang, lang,
transformers: [ transformers: [
...transformers, ...transformers,
transformerCompactLineOptions(lineOptions),
{ {
name: 'vitepress:v-pre', name: 'vitepress:v-pre',
pre(node) { pre(node) {

@ -1,48 +0,0 @@
// Modified from https://github.com/egoist/markdown-it-highlight-lines
// Now this plugin is only used to normalize line attrs.
// The else part of line highlights logic is in './highlight.ts'.
import type { MarkdownItAsync } from 'markdown-it-async'
const RE = /{([\d,-]+)}/
export const highlightLinePlugin = (md: MarkdownItAsync) => {
const fence = md.renderer.rules.fence!
md.renderer.rules.fence = (...args) => {
const [tokens, idx] = args
const token = tokens[idx]
// due to use of markdown-it-attrs, the {0} syntax would have been
// converted to attrs on the token
const attr = token.attrs && token.attrs[0]
let lines = null
if (!attr) {
// markdown-it-attrs maybe disabled
const rawInfo = token.info
if (!rawInfo || !RE.test(rawInfo)) {
return fence(...args)
}
const langName = rawInfo.replace(RE, '').trim()
// ensure the next plugin get the correct lang
token.info = langName
lines = RE.exec(rawInfo)![1]
}
if (!lines) {
lines = attr![0]
if (!lines || !/[\d,-]+/.test(lines)) {
return fence(...args)
}
}
token.info += ' ' + lines
return fence(...args)
}
}

@ -12,14 +12,14 @@ export const lineNumberPlugin = (md: MarkdownItAsync, enable = false) => {
const info = tokens[idx].info const info = tokens[idx].info
if ( if (
(!enable && !/:line-numbers($| |=)/.test(info)) || (!enable && !/:line-numbers\b/.test(info)) ||
(enable && /:no-line-numbers($| )/.test(info)) (enable && /:no-line-numbers\b/.test(info))
) { ) {
return rawCode return rawCode
} }
let startLineNumber = 1 let startLineNumber = 1
const matchStartLineNumber = info.match(/=(\d*)/) const matchStartLineNumber = info.match(/=(\d+)/)
if (matchStartLineNumber && matchStartLineNumber[1]) { if (matchStartLineNumber && matchStartLineNumber[1]) {
startLineNumber = parseInt(matchStartLineNumber[1]) startLineNumber = parseInt(matchStartLineNumber[1])
} }

@ -63,14 +63,14 @@ export function extractTitle(info: string, options?: ExtractTitleOptions) {
) )
} }
function extractLang(info: string) { function extractLang(info: string): string {
return info return (
.trim() /^[a-zA-Z0-9-_]+/
.replace(/=(\d*)/, '') .exec(info)?.[0]
.replace(/:(no-)?line-numbers({| |$|=\d*).*/, '') .replace(/-vue$/, '') // remove -vue suffix
.replace(/(-vue|{| ).*$/, '')
.replace(/^vue-html$/, 'template') .replace(/^vue-html$/, 'template')
.replace(/^ansi$/, '') .replace(/^ansi$/, '') || ''
)
} }
/** /**

@ -1,5 +1,5 @@
import { resolveTitleFromToken } from '@mdit-vue/shared' import { resolveTitleFromToken } from '@mdit-vue/shared'
import _debug from 'debug' import { createDebug } from 'obug'
import fs from 'fs-extra' import fs from 'fs-extra'
import { LRUCache } from 'lru-cache' import { LRUCache } from 'lru-cache'
import path from 'node:path' import path from 'node:path'
@ -22,7 +22,7 @@ import {
import { getGitTimestamp } from './utils/getGitTimestamp' import { getGitTimestamp } from './utils/getGitTimestamp'
import { processIncludes } from './utils/processIncludes' import { processIncludes } from './utils/processIncludes'
const debug = _debug('vitepress:md') const debug = createDebug('vitepress:md')
const cache = new LRUCache<string, MarkdownCompileResult>({ max: 1024 }) const cache = new LRUCache<string, MarkdownCompileResult>({ max: 1024 })
export interface MarkdownCompileResult { export interface MarkdownCompileResult {

@ -221,6 +221,7 @@ export async function createVitePressPlugin(
this.environment.mode === 'dev' && this.environment.mode === 'dev' &&
this.environment.name === 'client' this.environment.name === 'client'
) { ) {
logDeadLinks(deadLinks, siteConfig.logger, true)
const payload: PageDataPayload = { const payload: PageDataPayload = {
path: `/${siteConfig.rewrites.map[relativePath] || relativePath}`, path: `/${siteConfig.rewrites.map[relativePath] || relativePath}`,
pageData pageData
@ -238,15 +239,7 @@ export async function createVitePressPlugin(
renderStart() { renderStart() {
if (allDeadLinks.length > 0) { if (allDeadLinks.length > 0) {
allDeadLinks.forEach(({ url, file }, i) => { logDeadLinks(allDeadLinks, siteConfig.logger)
siteConfig.logger.warn(
c.yellow(
`${i === 0 ? '\n\n' : ''}(!) Found dead link ${c.cyan(
url
)} in file ${c.white(c.dim(file))}`
)
)
})
siteConfig.logger.info( siteConfig.logger.info(
c.cyan( c.cyan(
'\nIf this is expected, you can disable this check via config. Refer: https://vitepress.dev/reference/site-config#ignoredeadlinks\n' '\nIf this is expected, you can disable this check via config. Refer: https://vitepress.dev/reference/site-config#ignoredeadlinks\n'
@ -423,3 +416,22 @@ export async function createVitePressPlugin(
await dynamicRoutesPlugin(siteConfig) await dynamicRoutesPlugin(siteConfig)
] ]
} }
function logDeadLinks(
deadLinks: MarkdownCompileResult['deadLinks'],
logger: SiteConfig['logger'],
devMode = false
) {
const logged = new Set<string>()
deadLinks.forEach(({ url, file }, i) => {
const key = `${file}:::${url}`
if (logged.has(key)) return
logged.add(key)
const prefix = '\n'.repeat(i === 0 ? (devMode ? 1 : 2) : 0)
logger.warn(
c.yellow(
`${prefix}(!) Found dead link ${c.cyan(url)} in file ${c.white(c.dim(file))}`
)
)
})
}

@ -1,4 +1,4 @@
import _debug from 'debug' import { createDebug } from 'obug'
import fs from 'fs-extra' import fs from 'fs-extra'
import MiniSearch from 'minisearch' import MiniSearch from 'minisearch'
import path from 'node:path' import path from 'node:path'
@ -10,7 +10,7 @@ import { createMarkdownRenderer } from '../markdown/markdown'
import { getLocaleForPath, slash, type MarkdownEnv } from '../shared' import { getLocaleForPath, slash, type MarkdownEnv } from '../shared'
import { processIncludes } from '../utils/processIncludes' import { processIncludes } from '../utils/processIncludes'
const debug = _debug('vitepress:local-search') const debug = createDebug('vitepress:local-search')
const LOCAL_SEARCH_INDEX_ID = '@localSearchIndex' const LOCAL_SEARCH_INDEX_ID = '@localSearchIndex'
const LOCAL_SEARCH_INDEX_REQUEST_PATH = '/' + LOCAL_SEARCH_INDEX_ID const LOCAL_SEARCH_INDEX_REQUEST_PATH = '/' + LOCAL_SEARCH_INDEX_ID

@ -1,11 +1,11 @@
import { spawn, sync } from 'cross-spawn' import { spawn, sync } from 'cross-spawn'
import _debug from 'debug' import { createDebug } from 'obug'
import fs from 'node:fs' import fs from 'node:fs'
import path from 'node:path' import path from 'node:path'
import { Transform, type TransformCallback } from 'node:stream' import { Transform, type TransformCallback } from 'node:stream'
import { slash } from '../shared' import { slash } from '../shared'
const debug = _debug('vitepress:git') const debug = createDebug('vitepress:git')
const cache = new Map<string, number>() const cache = new Map<string, number>()
const RS = 0x1e const RS = 0x1e

@ -353,3 +353,8 @@ type ObjectType = Record<PropertyKey, any>
export function isObject(value: unknown): value is ObjectType { export function isObject(value: unknown): value is ObjectType {
return Object.prototype.toString.call(value) === '[object Object]' return Object.prototype.toString.call(value) === '[object Object]'
} }
const shellLangs = ['shellscript', 'shell', 'bash', 'sh', 'zsh']
export function isShell(lang: string): boolean {
return shellLangs.includes(lang)
}

Loading…
Cancel
Save